@staix/agent-hub 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +2 -2
- package/docs/agent-notes/README.md +5 -0
- package/docs/agent-notes/adapters.md +2 -0
- package/docs/agent-notes/benchmarks.md +1 -0
- package/docs/agent-notes/daemon.md +1 -0
- package/docs/agent-notes/local-worker.md +1 -0
- package/docs/agent-notes/tasks.md +7 -1
- package/docs/agent-notes/tests.md +1 -0
- package/docs/dogfood.md +15 -1
- package/docs/memory.md +1 -1
- package/docs/operations.md +105 -16
- package/docs/profiles.md +36 -0
- package/docs/quickstart.md +9 -3
- package/docs/sdlc.md +29 -3
- package/docs/security.md +26 -4
- package/docs/specs/2026-09-19-agent-hub-design.md +16 -6
- package/docs/verification/2026-10-25-0.18.0.md +76 -0
- package/docs/verification/2026-10-25-codex-review-live-verdict.md +35 -0
- package/docs/verification/2026-10-25-remote-docker-transport.md +46 -0
- package/docs/verification/profiles/YYYY-MM-DD-general.md +46 -0
- package/docs/verification/profiles/YYYY-MM-DD-research.md +61 -0
- package/docs/verification/profiles/YYYY-MM-DD-writing.md +46 -0
- package/docs/verified.json +24 -18
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +94 -33
- package/src/adapters/definitions/builtin-metadata.ts +1 -1
- package/src/adapters/local-worker.ts +5 -3
- package/src/cli/console-state.ts +9 -2
- package/src/cli/help.ts +29 -28
- package/src/cli/identity.ts +87 -6
- package/src/cli/main.ts +190 -69
- package/src/cli/output.ts +7 -2
- package/src/cli/sdlc-output.ts +242 -0
- package/src/cli/task-output.ts +212 -0
- package/src/cli/upgrade-output.ts +57 -0
- package/src/hub/board.ts +17 -0
- package/src/hub/check-runner.ts +252 -22
- package/src/hub/conductor.ts +5 -0
- package/src/hub/config-apply.ts +16 -7
- package/src/hub/config-edit-host.ts +1 -1
- package/src/hub/config-tier.ts +1 -1
- package/src/hub/daemon.ts +353 -72
- package/src/hub/dogfood-profile.ts +248 -0
- package/src/hub/dogfood-read.ts +77 -14
- package/src/hub/dogfood.ts +5 -1
- package/src/hub/events.ts +1 -1
- package/src/hub/host-executable.ts +2 -1
- package/src/hub/hub-tools.ts +2 -2
- package/src/hub/operational-authority.ts +60 -0
- package/src/hub/operational-view.ts +141 -0
- package/src/hub/orphan-process.ts +69 -0
- package/src/hub/permission-mode.ts +1 -1
- package/src/hub/read-only-shell-proof.ts +80 -0
- package/src/hub/read-only-shell.ts +162 -0
- package/src/hub/reviewer-pool-config.ts +3 -2
- package/src/hub/reviewer-pool.ts +3 -0
- package/src/hub/routing.ts +23 -6
- package/src/hub/sdlc-merge.ts +299 -0
- package/src/hub/sdlc-mirror.ts +129 -0
- package/src/hub/sdlc.ts +58 -12
- package/src/hub/settings.ts +11 -5
- package/src/hub/snapshots.ts +60 -0
- package/src/hub/task-repository.ts +31 -5
- package/src/hub/tasks.ts +241 -16
- package/src/local/tools.ts +17 -4
- package/src/ui/index.html +2 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Research profile weekly verification receipt
|
|
2
|
+
|
|
3
|
+
Template only. No pilot result or verdict has been recorded.
|
|
4
|
+
|
|
5
|
+
- Week ending: YYYY-MM-DD
|
|
6
|
+
- Profile id: research
|
|
7
|
+
- Window bounds: unknown
|
|
8
|
+
|
|
9
|
+
## Recorded profile JSON
|
|
10
|
+
|
|
11
|
+
Run ahub report --dogfood --since 7d --json once at the review boundary. Replace the null below with only the emitted profile subsection. Preserve numbers and null values. Include no project/person name, task title, path, raw log or model text; use anonymous identifiers where necessary.
|
|
12
|
+
|
|
13
|
+
null
|
|
14
|
+
|
|
15
|
+
## Research measurements
|
|
16
|
+
|
|
17
|
+
Copy only emitted counts and anonymous task/claim slots. These placeholders remain unknown until recorded evidence supports a value.
|
|
18
|
+
|
|
19
|
+
- Current-plan declared sources and claims per task: unknown
|
|
20
|
+
- Claims reaching a person verdict: unknown
|
|
21
|
+
- Claim slots with model label, person verdict and agreement: unknown
|
|
22
|
+
- Agreement counts by supported/unsupported/unclear label: unknown
|
|
23
|
+
- Recorded task-check outcomes by class alias: unknown
|
|
24
|
+
- Proven checks.fact_check selection and pass/fail by alias: unknown
|
|
25
|
+
- Missing check keys per run: unknown
|
|
26
|
+
- Section-overlap notices and confirmed section edits: unknown
|
|
27
|
+
|
|
28
|
+
Task-check history alone does not prove checks.fact_check ran. A model label or task approval alone does not prove a person verdict or agreement. Preserve every emitted null.
|
|
29
|
+
|
|
30
|
+
## Verification verdicts
|
|
31
|
+
|
|
32
|
+
Choose exactly one listed verdict for each claim after inspecting recorded evidence. Unknown data alone cannot establish support. These placeholders are not verdicts.
|
|
33
|
+
|
|
34
|
+
- writing-live-example
|
|
35
|
+
- Verdict: [supported | partly supported | not supported]
|
|
36
|
+
- Evidence task/event/receipt ids: []
|
|
37
|
+
- Remaining unknowns: unknown
|
|
38
|
+
- general-live-example
|
|
39
|
+
- Verdict: [supported | partly supported | not supported]
|
|
40
|
+
- Evidence task/event/receipt ids: []
|
|
41
|
+
- Remaining unknowns: unknown
|
|
42
|
+
- research-live-example
|
|
43
|
+
- Verdict: [supported | partly supported | not supported]
|
|
44
|
+
- Evidence task/event/receipt ids: []
|
|
45
|
+
- Remaining unknowns: unknown
|
|
46
|
+
- native-model-ingestion
|
|
47
|
+
- Verdict: [supported | partly supported | not supported]
|
|
48
|
+
- Evidence task/event/receipt ids: []
|
|
49
|
+
- Remaining unknowns: unknown
|
|
50
|
+
- context-retention
|
|
51
|
+
- Verdict: [supported | partly supported | not supported]
|
|
52
|
+
- Evidence task/event/receipt ids: []
|
|
53
|
+
- Remaining unknowns: unknown
|
|
54
|
+
- native-account-research
|
|
55
|
+
- Verdict: [supported | partly supported | not supported]
|
|
56
|
+
- Evidence task/event/receipt ids: []
|
|
57
|
+
- Remaining unknowns: unknown
|
|
58
|
+
- research-measurement
|
|
59
|
+
- Verdict: [supported | partly supported | not supported]
|
|
60
|
+
- Evidence task/event/receipt ids: []
|
|
61
|
+
- Remaining unknowns: unknown
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Writing profile weekly verification receipt
|
|
2
|
+
|
|
3
|
+
Template only. No pilot result or verdict has been recorded.
|
|
4
|
+
|
|
5
|
+
- Week ending: YYYY-MM-DD
|
|
6
|
+
- Profile id: writing
|
|
7
|
+
- Window bounds: unknown
|
|
8
|
+
|
|
9
|
+
## Recorded profile JSON
|
|
10
|
+
|
|
11
|
+
Run ahub report --dogfood --since 7d --json once at the review boundary. Replace the null below with only the emitted profile subsection. Preserve numbers and null values. Include no project/person name, task title, path, raw log or model text; use anonymous identifiers where necessary.
|
|
12
|
+
|
|
13
|
+
null
|
|
14
|
+
|
|
15
|
+
## Verification verdicts
|
|
16
|
+
|
|
17
|
+
Choose exactly one listed verdict for each claim after inspecting recorded evidence. Unknown data alone cannot establish support. These placeholders are not verdicts.
|
|
18
|
+
|
|
19
|
+
- writing-live-example
|
|
20
|
+
- Verdict: [supported | partly supported | not supported]
|
|
21
|
+
- Evidence task/event/receipt ids: []
|
|
22
|
+
- Remaining unknowns: unknown
|
|
23
|
+
- general-live-example
|
|
24
|
+
- Verdict: [supported | partly supported | not supported]
|
|
25
|
+
- Evidence task/event/receipt ids: []
|
|
26
|
+
- Remaining unknowns: unknown
|
|
27
|
+
- research-live-example
|
|
28
|
+
- Verdict: [supported | partly supported | not supported]
|
|
29
|
+
- Evidence task/event/receipt ids: []
|
|
30
|
+
- Remaining unknowns: unknown
|
|
31
|
+
- native-model-ingestion
|
|
32
|
+
- Verdict: [supported | partly supported | not supported]
|
|
33
|
+
- Evidence task/event/receipt ids: []
|
|
34
|
+
- Remaining unknowns: unknown
|
|
35
|
+
- context-retention
|
|
36
|
+
- Verdict: [supported | partly supported | not supported]
|
|
37
|
+
- Evidence task/event/receipt ids: []
|
|
38
|
+
- Remaining unknowns: unknown
|
|
39
|
+
- native-account-research
|
|
40
|
+
- Verdict: [supported | partly supported | not supported]
|
|
41
|
+
- Evidence task/event/receipt ids: []
|
|
42
|
+
- Remaining unknowns: unknown
|
|
43
|
+
- research-measurement
|
|
44
|
+
- Verdict: [supported | partly supported | not supported]
|
|
45
|
+
- Evidence task/event/receipt ids: []
|
|
46
|
+
- Remaining unknowns: unknown
|
package/docs/verified.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"documents": {
|
|
4
4
|
"README.md": {
|
|
5
|
-
"verifiedAgainst": "
|
|
5
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
6
6
|
"paths": [
|
|
7
7
|
"package.json",
|
|
8
8
|
"src/",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"docs/security.md": {
|
|
14
|
-
"verifiedAgainst": "
|
|
14
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
15
15
|
"paths": [
|
|
16
16
|
"src/",
|
|
17
17
|
"templates/"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"docs/operations.md": {
|
|
21
|
-
"verifiedAgainst": "
|
|
21
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
22
22
|
"paths": [
|
|
23
23
|
"package.json",
|
|
24
24
|
"src/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"docs/quickstart.md": {
|
|
38
|
-
"verifiedAgainst": "
|
|
38
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
39
39
|
"paths": [
|
|
40
40
|
"package.json",
|
|
41
41
|
"src/",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"docs/agent-notes/adapters.md": {
|
|
46
|
-
"verifiedAgainst": "
|
|
46
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
47
47
|
"paths": [
|
|
48
48
|
"src/adapters/",
|
|
49
49
|
"src/pi/",
|
|
@@ -71,11 +71,14 @@
|
|
|
71
71
|
"src/hub/roster.ts",
|
|
72
72
|
"src/hub/instances.ts",
|
|
73
73
|
"src/hub/action-enforce.ts",
|
|
74
|
-
"src/hub/permission-payload.ts"
|
|
74
|
+
"src/hub/permission-payload.ts",
|
|
75
|
+
"src/hub/read-only-shell.ts",
|
|
76
|
+
"src/hub/read-only-shell-proof.ts",
|
|
77
|
+
"src/hub/operational-authority.ts"
|
|
75
78
|
]
|
|
76
79
|
},
|
|
77
80
|
"docs/agent-notes/benchmarks.md": {
|
|
78
|
-
"verifiedAgainst": "
|
|
81
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
79
82
|
"paths": [
|
|
80
83
|
"scripts/benchmarks/",
|
|
81
84
|
"test/benchmarks/",
|
|
@@ -87,7 +90,7 @@
|
|
|
87
90
|
]
|
|
88
91
|
},
|
|
89
92
|
"docs/agent-notes/budget.md": {
|
|
90
|
-
"verifiedAgainst": "
|
|
93
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
91
94
|
"paths": [
|
|
92
95
|
"src/hub/budget.ts",
|
|
93
96
|
"src/cli/statusline-tee.ts",
|
|
@@ -111,7 +114,7 @@
|
|
|
111
114
|
]
|
|
112
115
|
},
|
|
113
116
|
"docs/agent-notes/bus.md": {
|
|
114
|
-
"verifiedAgainst": "
|
|
117
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
115
118
|
"paths": [
|
|
116
119
|
"src/hub/bus.ts",
|
|
117
120
|
"src/hub/envelope.ts",
|
|
@@ -126,7 +129,7 @@
|
|
|
126
129
|
]
|
|
127
130
|
},
|
|
128
131
|
"docs/agent-notes/daemon.md": {
|
|
129
|
-
"verifiedAgainst": "
|
|
132
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
130
133
|
"paths": [
|
|
131
134
|
"src/hub/daemon.ts",
|
|
132
135
|
"src/hub/control-client.ts",
|
|
@@ -199,7 +202,7 @@
|
|
|
199
202
|
]
|
|
200
203
|
},
|
|
201
204
|
"docs/agent-notes/local-worker.md": {
|
|
202
|
-
"verifiedAgainst": "
|
|
205
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
203
206
|
"paths": [
|
|
204
207
|
"src/adapters/local-worker.ts",
|
|
205
208
|
"src/local/",
|
|
@@ -211,7 +214,7 @@
|
|
|
211
214
|
]
|
|
212
215
|
},
|
|
213
216
|
"docs/agent-notes/models.md": {
|
|
214
|
-
"verifiedAgainst": "
|
|
217
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
215
218
|
"paths": [
|
|
216
219
|
"src/models/",
|
|
217
220
|
"src/hub/inference.ts",
|
|
@@ -219,7 +222,7 @@
|
|
|
219
222
|
]
|
|
220
223
|
},
|
|
221
224
|
"docs/agent-notes/tasks.md": {
|
|
222
|
-
"verifiedAgainst": "
|
|
225
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
223
226
|
"paths": [
|
|
224
227
|
"src/hub/tasks.ts",
|
|
225
228
|
"src/hub/board.ts",
|
|
@@ -251,11 +254,13 @@
|
|
|
251
254
|
"src/hub/spec-read.ts",
|
|
252
255
|
"src/hub/workflow.ts",
|
|
253
256
|
"src/hub/deliverables.ts",
|
|
254
|
-
"src/hub/deliverable-files.ts"
|
|
257
|
+
"src/hub/deliverable-files.ts",
|
|
258
|
+
"src/hub/sdlc-merge.ts",
|
|
259
|
+
"src/hub/sdlc-mirror.ts"
|
|
255
260
|
]
|
|
256
261
|
},
|
|
257
262
|
"docs/agent-notes/tests.md": {
|
|
258
|
-
"verifiedAgainst": "
|
|
263
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
259
264
|
"paths": [
|
|
260
265
|
"test/",
|
|
261
266
|
"scripts/check.sh",
|
|
@@ -274,11 +279,12 @@
|
|
|
274
279
|
"scripts/changed-tests.mjs",
|
|
275
280
|
"scripts/check-closing-refs.mjs",
|
|
276
281
|
".github/workflows/closing-refs.yml",
|
|
277
|
-
"scripts/lib/ts-ast.ts"
|
|
282
|
+
"scripts/lib/ts-ast.ts",
|
|
283
|
+
"test/cli-json-guard.test.ts"
|
|
278
284
|
]
|
|
279
285
|
},
|
|
280
286
|
"docs/agent-notes/README.md": {
|
|
281
|
-
"verifiedAgainst": "
|
|
287
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
282
288
|
"paths": [
|
|
283
289
|
"src/cli/console-state.ts",
|
|
284
290
|
"src/cli/attention.ts",
|
|
@@ -287,7 +293,7 @@
|
|
|
287
293
|
]
|
|
288
294
|
},
|
|
289
295
|
"src/cli/help.ts": {
|
|
290
|
-
"verifiedAgainst": "
|
|
296
|
+
"verifiedAgainst": "af22ee0dd9c6a1f4a5c5fc3f10fbfe4875350c1e",
|
|
291
297
|
"paths": [
|
|
292
298
|
"src/cli/main.ts",
|
|
293
299
|
"src/cli/launch.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staix/agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"bun": ">=1.4.2"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@staix/agent-mem": "0.
|
|
20
|
+
"@staix/agent-mem": "0.8.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Channel between Claude Code and the agent-hub daemon: peer messages from Codex, Kimi and the local worker arrive as channel events; hub_send replies.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Young Joon Lee",
|
|
@@ -82,7 +82,7 @@ function memoryGitEnv(env = process.env, options = {}) {
|
|
|
82
82
|
}
|
|
83
83
|
function memoryGitSync(args, options) {
|
|
84
84
|
const argv = memoryGitArgv(args);
|
|
85
|
-
return childProcess().spawnSync(argv[0], argv.slice(1), { ...options, env: memoryGitEnv(options.env ?? process.env) });
|
|
85
|
+
return childProcess().spawnSync(argv[0], argv.slice(1), { ...ambientGitTimeout !== undefined ? { timeout: ambientGitTimeout, killSignal: "SIGKILL" } : {}, ...options, env: memoryGitEnv(options.env ?? process.env) });
|
|
86
86
|
}
|
|
87
87
|
function processSignature(pid) {
|
|
88
88
|
try {
|
|
@@ -118,7 +118,7 @@ var require2, processes, childProcess = () => processes ??= require2("node:child
|
|
|
118
118
|
const parent = dirname(absolute);
|
|
119
119
|
return parent === absolute ? absolute : join(canonical(parent), basename(absolute));
|
|
120
120
|
}
|
|
121
|
-
}, DENY_SEGMENTS, DENY_NAMES, hasSegment = (rel, seg) => `/${rel}/`.includes(`/${seg}/`), SAFE_CONFIG, CREDENTIAL_KEYS, memoryGitArgv = (args, opts = {}) => ["git", "--no-pager", ...SAFE_CONFIG.filter((c) => !opts.credentials || !CREDENTIAL_KEYS.has(c)).flatMap((c) => ["-c", c]), ...args];
|
|
121
|
+
}, DENY_SEGMENTS, DENY_NAMES, hasSegment = (rel, seg) => `/${rel}/`.includes(`/${seg}/`), SAFE_CONFIG, CREDENTIAL_KEYS, memoryGitArgv = (args, opts = {}) => ["git", "--no-pager", ...SAFE_CONFIG.filter((c) => !opts.credentials || !CREDENTIAL_KEYS.has(c)).flatMap((c) => ["-c", c]), ...args], ambientGitTimeout;
|
|
122
122
|
var init_platform = __esm(() => {
|
|
123
123
|
require2 = createRequire(import.meta.url);
|
|
124
124
|
DENY_SEGMENTS = [".maru/secrets", ".agenthub/state", ".agenthub/archive"];
|
|
@@ -6154,9 +6154,26 @@ ${preview(content)}`, "write", ctx.signal, file, undefined, String(a.path), a),
|
|
|
6154
6154
|
return "error: empty command";
|
|
6155
6155
|
if (command.length > 4000)
|
|
6156
6156
|
return "error: command longer than 4000 characters; put it in a script file with write, then run that";
|
|
6157
|
-
const
|
|
6158
|
-
|
|
6159
|
-
|
|
6157
|
+
const input = Object.freeze({ ...a, command });
|
|
6158
|
+
for (let attempt = 0;; attempt++) {
|
|
6159
|
+
const refused = approvalError(await ctx.permit(`bash: ${command}`, "bash", ctx.signal, undefined, undefined, undefined, input, attempt ? { forceAsk: true } : undefined), "error: the user did not approve this command");
|
|
6160
|
+
if (refused)
|
|
6161
|
+
return refused;
|
|
6162
|
+
if (ctx.signal?.aborted)
|
|
6163
|
+
return "error: turn cancelled before shell execution";
|
|
6164
|
+
let check = "valid";
|
|
6165
|
+
try {
|
|
6166
|
+
check = ctx.shellExecutionCheck?.(input) ?? "valid";
|
|
6167
|
+
} catch {
|
|
6168
|
+
check = "ask";
|
|
6169
|
+
}
|
|
6170
|
+
if (check === "aborted" || ctx.signal?.aborted)
|
|
6171
|
+
return "error: turn cancelled before shell execution";
|
|
6172
|
+
if (check === "valid")
|
|
6173
|
+
break;
|
|
6174
|
+
if (attempt)
|
|
6175
|
+
return "error: read-only shell proof remained unstable; no command was executed";
|
|
6176
|
+
}
|
|
6160
6177
|
const res = await sandboxedExec(["/bin/bash", "-c", command], { cwd: ctx.cwd, profile: ctx.sandboxProfile, readOnlyData: !!ctx.readOnly?.length, timeoutMs: (Number(a.timeout_s) || 120) * 1000, ...ctx.sandboxEnv ? { env: ctx.sandboxEnv } : {}, ...ctx.signal ? { signal: ctx.signal } : {} });
|
|
6161
6178
|
try {
|
|
6162
6179
|
ctx.onProcessExit?.(res.code);
|
|
@@ -6220,7 +6237,7 @@ var init_tools = __esm(() => {
|
|
|
6220
6237
|
fn("read", "Read a text file inside the project. Returns numbered lines.", { path: str, offset: { type: "integer", minimum: 1 }, limit: { type: "integer", minimum: 1 } }, ["path"]),
|
|
6221
6238
|
fn("write", "Create or overwrite a file inside the project. Needs the user's approval.", { path: str, content: str }, ["path", "content"]),
|
|
6222
6239
|
fn("edit", "Replace one exact, unique occurrence of `old` with `new` in a file. Needs the user's approval.", { path: str, old: str, new: str }, ["path", "old", "new"]),
|
|
6223
|
-
fn("bash", "Run a shell command in the project directory, sandboxed: no writes outside the project, no credential reads, no network unless the hub enables it.
|
|
6240
|
+
fn("bash", "Run a shell command in the project directory, sandboxed: no writes outside the project, no credential reads, no network unless the hub enables it. Follows hub permission policy: proven bounded read-only commands may run automatically in ask-when-needed; other commands ask.", { command: str, timeout_s: { type: "integer", minimum: 1, maximum: 600 } }, ["command"]),
|
|
6224
6241
|
fn("git", "Run git in the project. status, diff, log, show, ls-files, blame, rev-parse run freely; add, commit, restore, checkout, switch, stash, mv, rm, branch, merge, rebase, cherry-pick, revert, tag need approval; everything else (push, fetch, config, remote ...) is refused.", { args: { type: "array", items: str, minItems: 1 } }, ["args"]),
|
|
6225
6242
|
fn("hub_send", "Send a message to the other agents now, before your turn ends. Conclusions only. Your final answer is shared anyway; use this for something that cannot wait.", { text: str, to: { type: "array", items: str } }, ["text"])
|
|
6226
6243
|
];
|
|
@@ -10017,7 +10034,7 @@ var init_hub_tools = __esm(() => {
|
|
|
10017
10034
|
tool("hub_release_list", "Read public release scope and planned/actual dates for phased planning. Configured planners only.", {}),
|
|
10018
10035
|
tool("hub_task_propose", "Put a piece of work on the shared task board. The hub assigns an owner by class (routing.toml) unless you name one. Classes: plan, implement, bulk_edit, test, review, summarize, triage. Name the class when you know it; without one the hub tries to pick it. Name yourself as owner to claim work nobody assigned you: it starts in progress. Put the paths the work touches in refs, set refs.repo for work in another repository, and when you claim work, a plan; the hub says when they overlap another open task.", { title: str2, class: { type: "string", enum: ["plan", "implement", "bulk_edit", "test", "review", "summarize", "triage"] }, detail: str2, role: { type: "string", description: "catalog role id for this task class; guidance only, no routing or authority change" }, refs, plan, owner: { type: "string", description: "peer id; yours to claim the work, omit to let the hub route it. With after, the owner is reserved: offered the task first once it is ready" }, after: { type: "array", items: { type: "integer" }, description: "ids of tasks that must be approved first; until then this one waits, offered to nobody, and cannot be claimed" }, backlog: { type: "boolean", description: "stage unoffered work; owner is only a reservation, even when naming yourself" }, urgent: { type: "boolean", description: "hand it over even when its owner is paused for quota only briefly" }, goal: { type: "string", description: "id of this project's long-term task (ahub goal list) the work is part of" } }, ["title"]),
|
|
10019
10036
|
tool("hub_task_accept", "Take a task that was assigned to you, with your plan for it. Set refs.repo for work in another repository; acceptance may correct only that repository metadata.", { id, plan: acceptPlan, refs: { ...refs, properties: { repo: refs.properties.repo } } }, ["id"]),
|
|
10020
|
-
tool("hub_task_cancel", "Cancel
|
|
10037
|
+
tool("hub_task_cancel", "Cancel proposed/backlog work you proposed as its configured planner, including work an owner accepted and then declined while it has no execution, branch, commit or PR evidence; work with any such evidence is person-only. Keeps history and does not stop a native turn or delete files.", { id, reason: str2, superseded_by: str2, revision: { type: "integer", minimum: 0 } }, ["id", "reason"]),
|
|
10021
10038
|
tool("hub_task_decline", "Pass on a task assigned to you; the hub offers it to the next peer. A plain decline is a lasting refusal: you are never offered this task again. Pass capacity: true when you only have no room right now: you are skipped for this move only, and a later reroute or planner pull may offer it to you again.", { id, reason: str2, capacity: { type: "boolean" } }, ["id"]),
|
|
10022
10039
|
tool("hub_task_done", "Mark your task finished. It goes to its reviewer with your summary and refs; no duplicate hub_send chat is needed. When the project configures a check for its class, the hub runs it first and the result comes as a task message: a failed check keeps the task with you.", { id, summary: { type: "string", description: "what changed, why, and the check you ran with its result" }, refs, deliverables: deliverablesSchema }, ["id", "summary"]),
|
|
10023
10040
|
tool("hub_sdlc_record", "Record artifact ids or claimed local checks on your task. This grants no authority or external-check evidence. Only its owner/reviewer or the person may record; only its reviewer/person may name a reviewed head or phase checklist. Phase evidence refs are content hashes, not external verification.", { id, receipt: { type: "object", properties: { deliverables: deliverablesSchema, stage: { type: "string", enum: ["spec", "plan", "implement", "review", "verify", "merge", "release"] }, repo: str2, issue: id, pr: id, localHead: str2, localResult: { type: "string", enum: ["passed", "failed", "unknown"] }, reviewedHead: str2, phase: { type: "object", properties: { id: str2, head: str2, tasks: { type: "array", maxItems: 64, items: { type: "object", properties: { id, after: { type: "array", maxItems: 16, items: id } }, required: ["id", "after"], additionalProperties: false } }, criteria: { type: "array", maxItems: 32, items: { type: "object", properties: { id: str2, required: { type: "boolean" }, result: { type: "string", enum: ["verified", "failed", "unverified"] }, refs: { type: "array", maxItems: 8, items: str2 } }, required: ["id", "required", "result", "refs"], additionalProperties: false } } }, required: ["id", "tasks", "criteria"], additionalProperties: false } }, additionalProperties: false } }, ["id", "receipt"]),
|
|
@@ -10041,7 +10058,7 @@ var init_hub_tools = __esm(() => {
|
|
|
10041
10058
|
TASK_TOOL_NAMES = new Set(TASK_TOOLS.map((t) => t.name));
|
|
10042
10059
|
CONDUCTOR_TOOLS = [
|
|
10043
10060
|
tool("hub_status", "Inspect public team state, holds, quota windows, task counts and pending approval peer/tool/age. Only the configured conductor may use this tool.", {}),
|
|
10044
|
-
tool("hub_task_show", "Read a task's public view and history, its done summary and check result included. PII tasks remain stubs. Only the conductor and the task's current owner and reviewer may use this tool.", { id }, ["id"]),
|
|
10061
|
+
tool("hub_task_show", "Read a task's public view and history, its done summary and check result included. PII tasks remain stubs. Only the conductor, the configured planner and the task's current owner and reviewer may use this tool.", { id }, ["id"]),
|
|
10045
10062
|
tool("hub_task_assign", "Name an owner (default role) or reviewer. Only the conductor may name a reviewer through this tool. A proposer may redirect an unaccepted proposed task's owner; a task that still waits reserves that owner. Pull-only peers receive work when named while attached or when they claim it; a peer that cannot take the task is refused with the reason, and the task is unchanged. With an explicit capabilities list, the conductor needs assign capability for either role, the proposer only to give ownership to another peer.", { id, peer: str2, role: { type: "string", enum: ["owner", "reviewer"] } }, ["id", "peer"]),
|
|
10046
10063
|
tool("hub_task_escalate", "Escalate a task through the normal task flow, as the conductor. Requires assign capability when explicitly listed.", { id }, ["id"]),
|
|
10047
10064
|
tool("hub_peer_start", "Start local, kimi, qwen, jcode, pi, claude or codex in the start mode a person set for it; you cannot choose the mode. A headless peer starts at once. A TUI peer (claude, and pi and codex by default) opens in a terminal with a fixed command; when the hub cannot open a terminal the call fails with the command for the person to run.", { peer: { type: "string", enum: CONDUCTOR_START_PEERS } }, ["peer"]),
|
|
@@ -10367,11 +10384,12 @@ var init_local_worker = __esm(() => {
|
|
|
10367
10384
|
return `(stopped after ${maxSteps} steps) ${progress.last}`.trim();
|
|
10368
10385
|
}
|
|
10369
10386
|
toolContext(envs, turnSignal, pii, reply) {
|
|
10387
|
+
const executionGeneration = this.turn;
|
|
10370
10388
|
return {
|
|
10371
10389
|
cwd: this.opts.cwd,
|
|
10372
10390
|
deny: this.opts.tools.deny,
|
|
10373
10391
|
readOnly: this.opts.tools.readOnly,
|
|
10374
|
-
permit: async (title, tool, _signal, canonicalTarget, _observe, originalPath, input) => {
|
|
10392
|
+
permit: async (title, tool, _signal, canonicalTarget, _observe, originalPath, input, policy) => {
|
|
10375
10393
|
if (turnSignal.aborted)
|
|
10376
10394
|
return "aborted";
|
|
10377
10395
|
const generation = this.turn;
|
|
@@ -10400,7 +10418,7 @@ var init_local_worker = __esm(() => {
|
|
|
10400
10418
|
};
|
|
10401
10419
|
const onAbort = () => finish("aborted");
|
|
10402
10420
|
turnSignal.addEventListener("abort", onAbort, { once: true });
|
|
10403
|
-
this.opts.tools.permit(title, tool, turnSignal, canonicalTarget, observe, originalPath, input).then(finish, (error) => {
|
|
10421
|
+
this.opts.tools.permit(title, tool, turnSignal, canonicalTarget, observe, originalPath, input, policy).then(finish, (error) => {
|
|
10404
10422
|
turnSignal.removeEventListener("abort", onAbort);
|
|
10405
10423
|
reject(error);
|
|
10406
10424
|
});
|
|
@@ -10413,6 +10431,7 @@ var init_local_worker = __esm(() => {
|
|
|
10413
10431
|
observe({ source: "expired", answerEpoch: this.approvalAnswerEpoch ?? 0, eligibleExpiry: true });
|
|
10414
10432
|
return picked;
|
|
10415
10433
|
},
|
|
10434
|
+
shellExecutionCheck: (input) => turnSignal.aborted || executionGeneration !== this.turn ? "aborted" : this.opts.tools.shellExecutionCheck?.(input) ?? "valid",
|
|
10416
10435
|
sandboxProfile: this.sandboxProfile,
|
|
10417
10436
|
sandboxEnv: { ...proxyEnv(this.opts.tools.bashNetwork ?? false), AGENTHUB_PEER_ID: this.id },
|
|
10418
10437
|
signal: turnSignal,
|
|
@@ -12704,7 +12723,7 @@ var init_builtin_metadata = __esm(() => {
|
|
|
12704
12723
|
headlessForms = Object.freeze(["headless"]);
|
|
12705
12724
|
hubGrants = {
|
|
12706
12725
|
ask: "every write, edit and shell command asks",
|
|
12707
|
-
"ask-when-needed": "write and
|
|
12726
|
+
"ask-when-needed": "write, edit and proven bounded read-only shell commands inside the project run without asking; other shell commands and git writes still ask",
|
|
12708
12727
|
"never-ask": "every tool runs without asking, inside the hub sandbox and path guard"
|
|
12709
12728
|
};
|
|
12710
12729
|
CLAUDE_REVIEWER_VARIANT = {
|
|
@@ -14087,6 +14106,14 @@ var init_work_lists = __esm(() => {
|
|
|
14087
14106
|
forbidden = new Set(["__proto__", "constructor", "prototype"]);
|
|
14088
14107
|
});
|
|
14089
14108
|
|
|
14109
|
+
// node_modules/@staix/agent-mem/src/memory/candidate-pending.ts
|
|
14110
|
+
var CANDIDATE_AGE_MS, CANDIDATE_SHOWN_GRACE_MS, CANDIDATE_DUE_MS;
|
|
14111
|
+
var init_candidate_pending = __esm(() => {
|
|
14112
|
+
CANDIDATE_AGE_MS = 30 * 86400000;
|
|
14113
|
+
CANDIDATE_SHOWN_GRACE_MS = 7 * 86400000;
|
|
14114
|
+
CANDIDATE_DUE_MS = 7 * 86400000;
|
|
14115
|
+
});
|
|
14116
|
+
|
|
14090
14117
|
// node_modules/@staix/agent-mem/src/memory/generation-sync.ts
|
|
14091
14118
|
var zero;
|
|
14092
14119
|
var init_generation_sync = __esm(() => {
|
|
@@ -14118,14 +14145,6 @@ var init_sync = __esm(() => {
|
|
|
14118
14145
|
liveGroups = new Set;
|
|
14119
14146
|
});
|
|
14120
14147
|
|
|
14121
|
-
// node_modules/@staix/agent-mem/src/memory/candidate-pending.ts
|
|
14122
|
-
var CANDIDATE_AGE_MS, CANDIDATE_SHOWN_GRACE_MS, CANDIDATE_DUE_MS;
|
|
14123
|
-
var init_candidate_pending = __esm(() => {
|
|
14124
|
-
CANDIDATE_AGE_MS = 30 * 86400000;
|
|
14125
|
-
CANDIDATE_SHOWN_GRACE_MS = 7 * 86400000;
|
|
14126
|
-
CANDIDATE_DUE_MS = 7 * 86400000;
|
|
14127
|
-
});
|
|
14128
|
-
|
|
14129
14148
|
// node_modules/@staix/agent-mem/src/memory/path-glob.ts
|
|
14130
14149
|
var init_path_glob = __esm(() => {
|
|
14131
14150
|
init_project2();
|
|
@@ -21423,6 +21442,37 @@ init_policy();
|
|
|
21423
21442
|
init_screen();
|
|
21424
21443
|
init_shard();
|
|
21425
21444
|
init_work_lists();
|
|
21445
|
+
// node_modules/@staix/agent-mem/src/memory/project-import.ts
|
|
21446
|
+
init_bridge();
|
|
21447
|
+
init_private_spans();
|
|
21448
|
+
init_shard();
|
|
21449
|
+
init_store();
|
|
21450
|
+
var PROJECT_IMPORT_MAX_BYTES = 64 * 1024 * 1024;
|
|
21451
|
+
var PROJECT_IMPORT_MAX_LINE_BYTES = 64 * 1024;
|
|
21452
|
+
var PROJECT_IMPORT_FUTURE_SKEW_MS = 5 * 60 * 1000;
|
|
21453
|
+
var NOTE_KEYS = new Set(["schema", "id", "at", "machine", "op", "project", "scope", "tier", "kind", "peer", "title", "text", "task", "about", "supersedes", "promotedFrom", "source", "expires"]);
|
|
21454
|
+
// node_modules/@staix/agent-mem/src/memory/external-import.ts
|
|
21455
|
+
init_aliases();
|
|
21456
|
+
|
|
21457
|
+
// node_modules/@staix/agent-mem/src/memory/candidates.ts
|
|
21458
|
+
init_candidate_pending();
|
|
21459
|
+
|
|
21460
|
+
// node_modules/@staix/agent-mem/src/memory/note-add.ts
|
|
21461
|
+
init_store();
|
|
21462
|
+
|
|
21463
|
+
// node_modules/@staix/agent-mem/src/memory/candidates.ts
|
|
21464
|
+
init_home();
|
|
21465
|
+
init_platform();
|
|
21466
|
+
init_private_spans();
|
|
21467
|
+
var CANDIDATE_RETENTION = { count: 500, ageMs: 30 * 86400000 };
|
|
21468
|
+
var COUNTER_RETENTION = { count: 1e4, ageMs: 90 * 86400000 };
|
|
21469
|
+
|
|
21470
|
+
// node_modules/@staix/agent-mem/src/memory/external-import.ts
|
|
21471
|
+
init_candidate_pending();
|
|
21472
|
+
init_platform();
|
|
21473
|
+
init_private_spans();
|
|
21474
|
+
init_screen();
|
|
21475
|
+
var EXTERNAL_IMPORT_LIMITS = { maxFiles: 256, maxDirectories: 512, maxEntriesPerDirectory: 512, maxDepth: 16, maxFileBytes: 1024 * 1024, maxCandidatesPerRun: 100, candidateTextChars: 3000, titleChars: 200 };
|
|
21426
21476
|
// node_modules/@staix/agent-mem/src/memory/cross-project.ts
|
|
21427
21477
|
init_search_fts();
|
|
21428
21478
|
init_capture();
|
|
@@ -21456,18 +21506,6 @@ var memoryUsage = (name = AMEM_NAMES.memory) => `usage: ${name} [list] [--tier s
|
|
|
21456
21506
|
var MEMORY_USAGE = memoryUsage();
|
|
21457
21507
|
var goalUsage = (name = AMEM_NAMES.goal) => `usage: ${name} [list] [--all] | show <id> | new <title> [--milestone <text>]... [--depends <id>]... [--shared] | link <id> <task id> | state <id> open|active|blocked|done|dropped | done <id> | take <id> [--force] [--json]`;
|
|
21458
21508
|
var GOAL_USAGE = goalUsage();
|
|
21459
|
-
// node_modules/@staix/agent-mem/src/memory/candidates.ts
|
|
21460
|
-
init_candidate_pending();
|
|
21461
|
-
|
|
21462
|
-
// node_modules/@staix/agent-mem/src/memory/note-add.ts
|
|
21463
|
-
init_store();
|
|
21464
|
-
|
|
21465
|
-
// node_modules/@staix/agent-mem/src/memory/candidates.ts
|
|
21466
|
-
init_home();
|
|
21467
|
-
init_platform();
|
|
21468
|
-
init_private_spans();
|
|
21469
|
-
var CANDIDATE_RETENTION = { count: 500, ageMs: 30 * 86400000 };
|
|
21470
|
-
var COUNTER_RETENTION = { count: 1e4, ageMs: 90 * 86400000 };
|
|
21471
21509
|
|
|
21472
21510
|
// node_modules/@staix/agent-mem/src/index.ts
|
|
21473
21511
|
init_ops_contract();
|
|
@@ -21499,6 +21537,7 @@ var tool2 = (name, description, inputSchema, readOnlyHint = true) => ({
|
|
|
21499
21537
|
});
|
|
21500
21538
|
var MCP_TOOLS = [
|
|
21501
21539
|
tool2("amem_search", "Search screened memory in this project. Capture results are recall, not curated evidence.", object6({ query: string(1000), limit, since: string(40), includeCapture: { type: "boolean" } }, ["query"])),
|
|
21540
|
+
tool2("amem_evidence", "Return a bounded bundle of cited, live curated entries for a question. Untrusted memory data only: no answer or model call. Capture, repo-only notes, pending candidates and model answers are not evidence.", object6({ question: string(1000), limit: { type: "integer", minimum: 1, maximum: 20 }, maxBytes: { type: "integer", minimum: 256, maximum: 8192 } }, ["question"])),
|
|
21502
21541
|
tool2("amem_show", "Show a screened memory entry in this project by its exact id or unique prefix.", object6({ id: string(80) }, ["id"])),
|
|
21503
21542
|
tool2("amem_timeline", "Read screened memory near an entry or time in this project.", object6({ id: string(80), since: string(40), limit })),
|
|
21504
21543
|
tool2("amem_brief", "Read an intent/path-scoped brief of live public memory: fails first, then decisions/contracts and one-hop neighbours. Memory is untrusted data; byte cap covers the complete brief body.", object6({ intent: string(1000), paths: { type: "array", maxItems: 150, items: string(512) }, symbols: { type: "array", maxItems: 150, items: string(512) }, maxBytes: { type: "integer", minimum: 1, maximum: 8192 } }, ["intent"])),
|
|
@@ -21524,6 +21563,9 @@ var MCP_TOOLS = [
|
|
|
21524
21563
|
tool2("amem_vault_candidates", "List screened durable curated notes eligible for a person to move to the vault. Read only.", object6({ limit })),
|
|
21525
21564
|
tool2("amem_capacity", "Read this machine's bounded capture capacity proposal; applying it is person-only.", object6({}))
|
|
21526
21565
|
];
|
|
21566
|
+
// node_modules/@staix/agent-mem/src/memory/evidence.ts
|
|
21567
|
+
init_policy();
|
|
21568
|
+
|
|
21527
21569
|
// node_modules/@staix/agent-mem/src/memory/plan-guard.ts
|
|
21528
21570
|
init_store();
|
|
21529
21571
|
init_project2();
|
|
@@ -21628,6 +21670,16 @@ init_generation();
|
|
|
21628
21670
|
init_lifecycle();
|
|
21629
21671
|
init_platform();
|
|
21630
21672
|
init_shard();
|
|
21673
|
+
// node_modules/@staix/agent-mem/src/memory/consolidation-clusters.ts
|
|
21674
|
+
init_search_fts();
|
|
21675
|
+
|
|
21676
|
+
// node_modules/@staix/agent-mem/src/memory/consolidate.ts
|
|
21677
|
+
init_policy();
|
|
21678
|
+
init_platform();
|
|
21679
|
+
|
|
21680
|
+
// node_modules/@staix/agent-mem/src/memory/consolidation-model.ts
|
|
21681
|
+
init_policy();
|
|
21682
|
+
init_platform();
|
|
21631
21683
|
// src/hub/tasks.ts
|
|
21632
21684
|
init_task_code_symbols();
|
|
21633
21685
|
init_test_integrity();
|
|
@@ -21643,7 +21695,7 @@ init_sdlc_metrics();
|
|
|
21643
21695
|
// package.json
|
|
21644
21696
|
var package_default = {
|
|
21645
21697
|
name: "@staix/agent-hub",
|
|
21646
|
-
version: "0.
|
|
21698
|
+
version: "0.18.0",
|
|
21647
21699
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
21648
21700
|
license: "MIT",
|
|
21649
21701
|
type: "module",
|
|
@@ -21660,7 +21712,7 @@ var package_default = {
|
|
|
21660
21712
|
bun: ">=1.4.2"
|
|
21661
21713
|
},
|
|
21662
21714
|
dependencies: {
|
|
21663
|
-
"@staix/agent-mem": "0.
|
|
21715
|
+
"@staix/agent-mem": "0.8.0"
|
|
21664
21716
|
},
|
|
21665
21717
|
devDependencies: {
|
|
21666
21718
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -21714,6 +21766,14 @@ init_plan_conformance();
|
|
|
21714
21766
|
init_review_findings();
|
|
21715
21767
|
init_sdlc();
|
|
21716
21768
|
|
|
21769
|
+
// src/hub/sdlc-merge.ts
|
|
21770
|
+
init_review_findings();
|
|
21771
|
+
init_sdlc();
|
|
21772
|
+
|
|
21773
|
+
// src/hub/sdlc-mirror.ts
|
|
21774
|
+
init_envelope();
|
|
21775
|
+
var inFlight = new Set;
|
|
21776
|
+
|
|
21717
21777
|
// src/hub/tasks.ts
|
|
21718
21778
|
init_review_findings();
|
|
21719
21779
|
init_roles();
|
|
@@ -21748,6 +21808,7 @@ init_project();
|
|
|
21748
21808
|
|
|
21749
21809
|
// src/hub/task-repository.ts
|
|
21750
21810
|
init_git();
|
|
21811
|
+
init_host_executable();
|
|
21751
21812
|
init_project();
|
|
21752
21813
|
init_spec_read();
|
|
21753
21814
|
var GIT_MAX_BYTES = 16 * 1024;
|
|
@@ -61,7 +61,7 @@ const dualForms = Object.freeze(["tui", "headless"] as const);
|
|
|
61
61
|
const headlessForms = Object.freeze(["headless"] as const);
|
|
62
62
|
const hubGrants = {
|
|
63
63
|
ask: "every write, edit and shell command asks",
|
|
64
|
-
"ask-when-needed": "write and
|
|
64
|
+
"ask-when-needed": "write, edit and proven bounded read-only shell commands inside the project run without asking; other shell commands and git writes still ask",
|
|
65
65
|
"never-ask": "every tool runs without asking, inside the hub sandbox and path guard",
|
|
66
66
|
};
|
|
67
67
|
|
|
@@ -36,7 +36,7 @@ export interface LocalOptions {
|
|
|
36
36
|
turnId?: () => string | undefined;
|
|
37
37
|
onApprovalStop?: (reason: string) => void;
|
|
38
38
|
onTool?: (observation: ToolObservation, task?: string) => void;
|
|
39
|
-
tools: { deny: string[]; permit: ToolContext["permit"]; bashNetwork?: SandboxNetwork; readAllow?: string[]; readOnly?: string[] };
|
|
39
|
+
tools: { deny: string[]; permit: ToolContext["permit"]; shellExecutionCheck?: ToolContext["shellExecutionCheck"]; bashNetwork?: SandboxNetwork; readAllow?: string[]; readOnly?: string[] };
|
|
40
40
|
capture?: Capture;
|
|
41
41
|
/** Runs a hub task tool (hub_task_*, hub_review, hub_remember) as this peer. Absent = the tools are not offered. */
|
|
42
42
|
taskTool?: (name: string, args: Record<string, unknown>, turn: { pii: boolean }) => Promise<string>;
|
|
@@ -290,11 +290,12 @@ export class LocalPeer extends BasePeer {
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
private toolContext(envs: Envelope[], turnSignal: AbortSignal, pii: boolean, reply: EnvelopeOpts): ToolContext {
|
|
293
|
+
const executionGeneration=this.turn;
|
|
293
294
|
return {
|
|
294
295
|
cwd: this.opts.cwd,
|
|
295
296
|
deny: this.opts.tools.deny,
|
|
296
297
|
readOnly: this.opts.tools.readOnly,
|
|
297
|
-
permit: async (title, tool, _signal, canonicalTarget, _observe, originalPath, input) => {
|
|
298
|
+
permit: async (title, tool, _signal, canonicalTarget, _observe, originalPath, input, policy) => {
|
|
298
299
|
if (turnSignal.aborted) return "aborted";
|
|
299
300
|
const generation = this.turn;
|
|
300
301
|
let observed = false;
|
|
@@ -314,7 +315,7 @@ export class LocalPeer extends BasePeer {
|
|
|
314
315
|
const finish = (decision: ToolApproval) => { turnSignal.removeEventListener("abort", onAbort); resolve(decision); };
|
|
315
316
|
const onAbort = () => finish("aborted");
|
|
316
317
|
turnSignal.addEventListener("abort", onAbort, { once: true });
|
|
317
|
-
this.opts.tools.permit(title, tool, turnSignal, canonicalTarget, observe, originalPath, input).then(finish, (error) => { turnSignal.removeEventListener("abort", onAbort); reject(error); });
|
|
318
|
+
this.opts.tools.permit(title, tool, turnSignal, canonicalTarget, observe, originalPath, input, policy).then(finish, (error) => { turnSignal.removeEventListener("abort", onAbort); reject(error); });
|
|
318
319
|
});
|
|
319
320
|
if (generation !== this.turn || (turnSignal.aborted && picked !== "expired")) return "aborted";
|
|
320
321
|
// Legacy permit callbacks can represent a person's denial without provenance. A true
|
|
@@ -323,6 +324,7 @@ export class LocalPeer extends BasePeer {
|
|
|
323
324
|
if (!observed && picked === "expired") observe({ source: "expired", answerEpoch: this.approvalAnswerEpoch ?? 0, eligibleExpiry: true });
|
|
324
325
|
return picked;
|
|
325
326
|
},
|
|
327
|
+
shellExecutionCheck: input=>turnSignal.aborted||executionGeneration!==this.turn?"aborted":this.opts.tools.shellExecutionCheck?.(input)??"valid",
|
|
326
328
|
sandboxProfile: this.sandboxProfile,
|
|
327
329
|
sandboxEnv: { ...proxyEnv(this.opts.tools.bashNetwork ?? false), AGENTHUB_PEER_ID: this.id },
|
|
328
330
|
signal: turnSignal,
|