Skip to content

Commit ccbab0a

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix-java-control-response-subtype-v2
Resolve SessionTest.java: main rewrote the file (static assertion imports, INIT_RESPONSE, @tempdir, TestTransport). Rebuild it from main and re-apply this branch's control-response subtype tests, log-appender helpers and FakeTransport on top. mvn -Dtest=SessionTest: 13 passed.
2 parents cd80147 + 413b6d1 commit ccbab0a

3,393 files changed

Lines changed: 325871 additions & 475529 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/.github/workflows/security-checks.yml @pomelo-nwu @wenshao
1414

1515
# --- Core package ---
16-
/packages/core/ @wenshao @tanzhenxin @yiliang114 @LaZzyMan @doudouOUC
16+
/packages/core/ @wenshao @tanzhenxin @yiliang114 @LaZzyMan @doudouOUC @qqqys
1717

1818
# --- CUA Driver & Mobile MCP ---
1919
/packages/cua-driver/ @LaZzyMan

.github/issue-owners.json

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$comment": "Label-driven issue assignment map; areas are keyed on the existing issue label taxonomy. Assignment is a pure function of an issue's labels — no model output is involved. Owners need push access but do NOT need a CODEOWNERS entry; every candidate is re-checked against the collaborator API at write time, so adding a login here cannot grant access to someone who lacks it. Areas match in file order, first match wins. See docs/design/2026-08-07-issue-auto-assignment.md.",
2+
"$comment": "Label-driven issue assignment map; areas are keyed on the existing issue label taxonomy. Assignment is a pure function of an issue's labels — no model output is involved. Owners need push access but do NOT need a CODEOWNERS entry; every candidate is re-checked against the collaborator API at write time, so adding a login here cannot grant access to someone who lacks it. Areas match in file order, first match wins. An area's optional paths list routes PR assignment (assign-pr-owner.mjs) by changed-file prefix, longest prefix wins; the core area's packages/core/ entry is the fallback for paths no module claims. Issue assignment ignores paths; module areas sit after core so label-based issue matching still resolves core first. See docs/design/2026-08-07-issue-auto-assignment.md.",
33
"requireLabels": ["need-discussion"],
44
"skipLabels": [
55
"welcome-pr",
@@ -13,6 +13,7 @@
1313
{
1414
"name": "core",
1515
"labels": ["category/core", "scope/core"],
16+
"paths": ["packages/core/"],
1617
"owners": [
1718
"wenshao",
1819
"yiliang114",
@@ -30,6 +31,59 @@
3031
"zjunothing",
3132
"ZijianZhang989"
3233
]
34+
},
35+
{
36+
"name": "core-skills",
37+
"labels": ["scope/core"],
38+
"paths": ["packages/core/src/skills/"],
39+
"owners": ["wenshao"]
40+
},
41+
{
42+
"name": "core-memory",
43+
"labels": ["scope/core"],
44+
"paths": ["packages/core/src/memory/"],
45+
"owners": ["wenshao"]
46+
},
47+
{
48+
"name": "core-goals",
49+
"labels": ["scope/core"],
50+
"paths": ["packages/core/src/goals/"],
51+
"owners": ["qqqys"]
52+
},
53+
{
54+
"name": "core-telemetry",
55+
"labels": ["scope/core"],
56+
"paths": ["packages/core/src/telemetry/"],
57+
"owners": ["zjunothing"]
58+
},
59+
{
60+
"name": "core-extension",
61+
"labels": ["scope/core"],
62+
"paths": ["packages/core/src/extension/"],
63+
"owners": ["callmeYe"]
64+
},
65+
{
66+
"name": "core-agents",
67+
"labels": ["scope/core"],
68+
"paths": ["packages/core/src/agents/"],
69+
"owners": ["qqqys"]
70+
},
71+
{
72+
"name": "core-config",
73+
"labels": ["scope/core"],
74+
"paths": ["packages/core/src/config/"],
75+
"owners": ["qqqys"]
76+
},
77+
{
78+
"name": "core-runtime",
79+
"labels": ["scope/core"],
80+
"paths": [
81+
"packages/core/src/core/",
82+
"packages/core/src/services/",
83+
"packages/core/src/tools/",
84+
"packages/core/src/utils/"
85+
],
86+
"owners": ["yiliang114"]
3387
}
3488
]
3589
}

.github/scripts/assign-issue-owner.mjs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ function isStringArray(value) {
2020
return Array.isArray(value) && value.every((v) => typeof v === 'string');
2121
}
2222

23+
// A changed-file prefix for assign-pr-owner.mjs: relative, no `//`, no
24+
// backslash, no `.`/`..` segments, and ending in `/` so startsWith cannot
25+
// leak into a sibling directory (packages/core matching packages/coredump/).
26+
function isPathPrefix(prefix) {
27+
if (typeof prefix !== 'string' || prefix.length === 0) return false;
28+
if (prefix.startsWith('/') || prefix.startsWith('./')) return false;
29+
if (!prefix.endsWith('/')) return false;
30+
if (prefix.includes('\\') || prefix.includes('//')) return false;
31+
return !prefix
32+
.split('/')
33+
.some((segment) => segment === '.' || segment === '..');
34+
}
35+
2336
export function loadPolicy(raw) {
2437
const policy = JSON.parse(raw);
2538
if (!policy || typeof policy !== 'object' || Array.isArray(policy)) {
@@ -74,6 +87,28 @@ export function loadPolicy(raw) {
7487
}
7588
seen.add(normalizedOwner);
7689
}
90+
// A never-matching paths entry silently unroutes the area from PR
91+
// assignment, so reject it here like other malformed config.
92+
if (area.paths !== undefined && !Array.isArray(area.paths)) {
93+
throw new Error(
94+
`${OWNERS_FILE}: area ${area.name} paths must be an array`,
95+
);
96+
}
97+
// An explicitly empty list can never route the area either, yet the
98+
// entry loop below cannot catch it — reject it like the sibling
99+
// labels/owners checks do.
100+
if (Array.isArray(area.paths) && area.paths.length === 0) {
101+
throw new Error(
102+
`${OWNERS_FILE}: area ${area.name} paths must not be empty; omit paths for a label-only area`,
103+
);
104+
}
105+
for (const prefix of area.paths ?? []) {
106+
if (!isPathPrefix(prefix)) {
107+
throw new Error(
108+
`${OWNERS_FILE}: invalid paths entry in ${area.name}: ${JSON.stringify(prefix)}`,
109+
);
110+
}
111+
}
77112
}
78113
return policy;
79114
}
@@ -151,7 +186,7 @@ function canWrite(repository, login) {
151186
}
152187
}
153188

154-
function openIssueCount(repository, login) {
189+
export function openIssueCount(repository, login) {
155190
return Number(
156191
gh([
157192
'issue',

.github/scripts/assign-issue-owner.test.mjs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,43 @@ describe('assign-issue-owner: owner map', () => {
149149
broken.areas[0].owners = [42];
150150
assert.throws(() => loadPolicy(JSON.stringify(broken)), /invalid login/);
151151
});
152+
153+
it('rejects paths entries that could never route the area', () => {
154+
// startsWith matching can never honour these spellings; accepting them
155+
// would silently unroute the area from PR assignment forever.
156+
for (const paths of [
157+
['./packages/core/'],
158+
['packages//core/'],
159+
['.github/../packages/core/'],
160+
['/packages/core/'],
161+
['packages/core'],
162+
['packages\\core/'],
163+
[''],
164+
[42],
165+
]) {
166+
const broken = JSON.parse(ownersRaw);
167+
broken.areas[0].paths = paths;
168+
assert.throws(
169+
() => loadPolicy(JSON.stringify(broken)),
170+
/invalid paths entry/,
171+
);
172+
}
173+
174+
const notArray = JSON.parse(ownersRaw);
175+
notArray.areas[0].paths = 'packages/core/';
176+
assert.throws(
177+
() => loadPolicy(JSON.stringify(notArray)),
178+
/paths must be an array/,
179+
);
180+
181+
// An explicitly empty list can never route the area either.
182+
const emptyPaths = JSON.parse(ownersRaw);
183+
emptyPaths.areas[0].paths = [];
184+
assert.throws(
185+
() => loadPolicy(JSON.stringify(emptyPaths)),
186+
/paths must not be empty/,
187+
);
188+
});
152189
});
153190

154191
describe('assign-issue-owner: skip policy', () => {

0 commit comments

Comments
 (0)