@namzu/sdk 6.1.0 → 6.2.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 +121 -0
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +5 -0
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/supervisor-coordinator-registration.test.js +31 -0
- package/dist/agents/__tests__/supervisor-coordinator-registration.test.js.map +1 -1
- package/dist/gateway/__tests__/completion-inbox.test.js +17 -0
- package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -1
- package/dist/gateway/completion-inbox.d.ts.map +1 -1
- package/dist/gateway/completion-inbox.js +17 -3
- package/dist/gateway/completion-inbox.js.map +1 -1
- package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.d.ts +2 -0
- package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.js +122 -0
- package/dist/runtime/query/__tests__/run-survives-its-own-park.proc-test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +43 -13
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/tools/builtins/__tests__/bash.proc-test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/bash.proc-test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/bash.proc-test.js +115 -0
- package/dist/tools/builtins/__tests__/bash.proc-test.js.map +1 -0
- package/dist/tools/builtins/__tests__/bash.test.d.ts +2 -0
- package/dist/tools/builtins/__tests__/bash.test.d.ts.map +1 -0
- package/dist/tools/builtins/__tests__/bash.test.js +65 -0
- package/dist/tools/builtins/__tests__/bash.test.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +89 -17
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/coordinator/__tests__/allow-delegation.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/allow-delegation.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/allow-delegation.test.js +94 -0
- package/dist/tools/coordinator/__tests__/allow-delegation.test.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +9 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +19 -2
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/agent/supervisor.d.ts +27 -0
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agents/SupervisorAgent.ts +5 -0
- package/src/agents/__tests__/supervisor-coordinator-registration.test.ts +37 -0
- package/src/gateway/__tests__/completion-inbox.test.ts +23 -0
- package/src/gateway/completion-inbox.ts +17 -3
- package/src/runtime/query/__tests__/run-survives-its-own-park.proc-test.ts +127 -0
- package/src/runtime/query/iteration/phases/context.ts +41 -15
- package/src/tools/builtins/__tests__/bash.proc-test.ts +142 -0
- package/src/tools/builtins/__tests__/bash.test.ts +79 -0
- package/src/tools/builtins/bash.ts +100 -18
- package/src/tools/coordinator/__tests__/allow-delegation.test.ts +120 -0
- package/src/tools/coordinator/index.ts +29 -2
- package/src/types/agent/supervisor.ts +28 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../../src/runtime/query/iteration/phases/context.ts"],"names":[],"mappings":"AAkKA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,GAAqB,EACrB,UAA+B,EAC/B,OAAqC;IAErC,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,IAAI,oBAAoB,CAAA;IAC3D,MAAM,eAAe,GAAG,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAE1D,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,QAAQ,GAAG,KAAK,CAAA;IAEpB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACxC,IAAI,CAAC;YACJ,MAAM,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;YACjD,QAAQ,GAAG,IAAI,CAAA;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,iEAAiE;YACjE,iEAAiE;YACjE,+DAA+D;YAC/D,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,0EAA0E,EAAE;gBACzF,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU,CAAC,EAAE;gBAC3B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACvD,CAAC,CAAA;QACH,CAAC;IACF,CAAC,CAAA;IAED,MAAM,YAAY,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../../../src/runtime/query/iteration/phases/context.ts"],"names":[],"mappings":"AAkKA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEvC;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,GAAqB,EACrB,UAA+B,EAC/B,OAAqC;IAErC,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,IAAI,oBAAoB,CAAA;IAC3D,MAAM,eAAe,GAAG,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;IAE1D,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,QAAQ,GAAG,KAAK,CAAA;IAEpB,MAAM,MAAM,GAAG,KAAK,IAAmB,EAAE;QACxC,IAAI,CAAC;YACJ,MAAM,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;YACjD,QAAQ,GAAG,IAAI,CAAA;QAChB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,iEAAiE;YACjE,iEAAiE;YACjE,+DAA+D;YAC/D,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,0EAA0E,EAAE;gBACzF,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACpB,YAAY,EAAE,UAAU,CAAC,EAAE;gBAC3B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACvD,CAAC,CAAA;QACH,CAAC;IACF,CAAC,CAAA;IAED,wEAAwE;IACxE,wEAAwE;IACxE,EAAE;IACF,sEAAsE;IACtE,0EAA0E;IAC1E,2EAA2E;IAC3E,uEAAuE;IACvE,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,uEAAuE;IACvE,EAAE;IACF,0EAA0E;IAC1E,wEAAwE;IACxE,sEAAsE;IACtE,iEAAiE;IACjE,EAAE;IACF,yEAAyE;IACzE,uEAAuE;IACvE,mDAAmD;IACnD,IAAI,SAAoD,CAAA;IACxD,yEAAyE;IACzE,2EAA2E;IAC3E,uDAAuD;IACvD,IAAI,SAAS,GAAG,KAAK,CAAA;IACrB,MAAM,YAAY,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClD,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO,EAAE,CAAA;gBACT,OAAM;YACP,CAAC;YACD,SAAS,GAAG,IAAI,CAAA;YAChB,MAAM,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAChC,CAAC,EAAE,KAAK,CAAC,CAAA;IACV,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAA;QACtC,OAAO,GAAG,IAAI,CAAA;QACd,uEAAuE;QACvE,uEAAuE;QACvE,uEAAuE;QACvE,kEAAkE;QAClE,IAAI,SAAS,KAAK,SAAS;YAAE,YAAY,CAAC,SAAS,CAAC,CAAA;QACpD,IAAI,SAAS;YAAE,MAAM,YAAY,CAAA;QAEjC,iEAAiE;QACjE,qEAAqE;QACrE,4DAA4D;QAC5D,kEAAkE;QAClE,kEAAkE;QAClE,cAAc;QACd,IAAI,QAAQ,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;YACjC,IAAI,CAAC,QAAQ;gBAAE,MAAM,MAAM,EAAE,CAAA;YAC7B,OAAO,QAAQ,CAAA;QAChB,CAAC;QAED,qEAAqE;QACrE,mEAAmE;QACnE,IAAI,QAAQ,EAAE,CAAC;YACd,MAAM,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBAC9E,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE;oBACrD,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;oBACpB,YAAY,EAAE,UAAU,CAAC,EAAE;oBAC3B,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;iBACvD,CAAC,CAAA;gBACF,OAAO,IAAI,CAAA;YACZ,CAAC,CAAC,CAAA;QACH,CAAC;QACD,OAAO,QAAQ,CAAA;IAChB,CAAC;YAAS,CAAC;QACV,OAAO,GAAG,IAAI,CAAA;IACf,CAAC;AACF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACzC,GAAqB,EACrB,OAAqC;IAErC,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,EAAE,MAAM,CAAA;IAC1C,4EAA4E;IAC5E,8EAA8E;IAC9E,sDAAsD;IACtD,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;IAC9C,MAAM,aAAa,GAAuB;QACzC,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,mCAAmC;KAC3C,CAAA;IACD,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,aAAa,CAAA;IACxC,OAAO,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;QAClD,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,MAAM,OAAO,GAAG,GAAS,EAAE;YAC1B,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,OAAO,CAAC,aAAa,CAAC,CAAA;QACvB,CAAC,CAAA;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC/C,CAAC,QAAQ,EAAE,EAAE;YACZ,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,OAAO,CAAC,QAAQ,CAAC,CAAA;QAClB,CAAC,EACD,CAAC,GAAG,EAAE,EAAE;YACP,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,OAAO,CAAC;gBACP,MAAM,EAAE,OAAO;gBACf,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,uBAAuB;aACpE,CAAC,CAAA;QACH,CAAC,CACD,CAAA;IACF,CAAC,CAAC,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,kBAAkB,CACxC,GAAqB,EACrB,QAA4B,EAC5B,YAAoB,EACpB,OAAe;IAEf,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC;QACzB,KAAK,OAAO,CAAC,CAAC,CAAC;YACd,MAAM,GAAG,CAAC,SAAS,CAAC;gBACnB,IAAI,EAAE,YAAY;gBAClB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACpB,YAAY,EAAE,YAA8B;gBAC5C,MAAM,EAAE,QAAQ,CAAC,MAAM;aACvB,CAAC,CAAA;YACF,KAAK,CAAC,CAAC,GAAG,CAAC,YAAY,EAAE,CAAA;YACzB,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;YAClC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,OAAO,EAAE,EAAE;gBACxC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACxB,MAAM,EAAE,QAAQ,CAAC,MAAM;aACvB,CAAC,CAAA;YACF,OAAO,MAAM,CAAA;QACd,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACd,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAA;YACrC,GAAG,CAAC,MAAM,CAAC,aAAa,EAAE,CAAA;YAC1B,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,OAAO,EAAE,EAAE;gBACzC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACxB,MAAM,EAAE,QAAQ,CAAC,MAAM;aACvB,CAAC,CAAA;YACF,OAAO,MAAM,CAAA;QACd,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACpB,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAA;YACzC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE;gBACrC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE;gBACxB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC3B,CAAC,CAAA;YACF,OAAO,MAAM,CAAA;QACd,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACrB,IAAI,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;gBAC5B,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,CAAA;gBACzB,GAAG,CAAC,WAAW,CAAC,cAAc,EAAE,CAAA;YACjC,CAAC;YACD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;YACnE,OAAO,UAAU,CAAA;QAClB,CAAC;QACD,KAAK,UAAU,CAAC;QAChB,KAAK,eAAe,CAAC;QACrB,KAAK,cAAc,CAAC;QACpB,KAAK,cAAc,CAAC;QACpB,gEAAgE;QAChE,gEAAgE;QAChE,kDAAkD;QAClD,KAAK,iBAAiB;YACrB,OAAO,UAAU,CAAA;QAClB,OAAO,CAAC,CAAC,CAAC;YACT,MAAM,WAAW,GAAU,QAAQ,CAAA;YACnC,MAAM,IAAI,KAAK,CAAC,4BAA6B,WAAkC,CAAC,MAAM,EAAE,CAAC,CAAA;QAC1F,CAAC;IACF,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash.proc-test.d.ts","sourceRoot":"","sources":["../../../../src/tools/builtins/__tests__/bash.proc-test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
5
|
+
import { BashTool } from '../bash.js';
|
|
6
|
+
/**
|
|
7
|
+
* What a shell command actually tells the model, measured by running one.
|
|
8
|
+
*
|
|
9
|
+
* The only builtin that runs a shell had no test at all, and what that cost
|
|
10
|
+
* is visible in the code it shipped: the host path called `exec` with no
|
|
11
|
+
* `catch`, and `exec` REJECTS on a non-zero exit. So the two things an agent
|
|
12
|
+
* runs a shell for most — a test run and a build — both threw, and the
|
|
13
|
+
* registry turned the throw into "the tool failed" with none of the stdout,
|
|
14
|
+
* stderr or exit code that explains why. The sandbox path beside it reported
|
|
15
|
+
* all three, so the same command told the model two different amounts
|
|
16
|
+
* depending on where it happened to run.
|
|
17
|
+
*
|
|
18
|
+
* These spawn real processes, so they live in the `proc-test` suite rather
|
|
19
|
+
* than the unit one — measured, running them beside 2594 unit tests flaked
|
|
20
|
+
* four unrelated timing-sensitive tests. The assertions that need no shell
|
|
21
|
+
* stayed behind in `bash.test.ts`.
|
|
22
|
+
*
|
|
23
|
+
* Commands are written to behave identically under `cmd.exe` and `sh`,
|
|
24
|
+
* because `exec` picks the platform shell and a test that only passes on one
|
|
25
|
+
* of them is a test that fails for whoever is on the other.
|
|
26
|
+
*/
|
|
27
|
+
const dirs = [];
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
for (const dir of dirs) {
|
|
30
|
+
// A killed child can still hold its working directory for a moment —
|
|
31
|
+
// on Windows that surfaces as EBUSY, and it failed the timeout test
|
|
32
|
+
// from the cleanup rather than the assertion, which is the most
|
|
33
|
+
// misleading way for a test to go red. Retry, then let it go: a temp
|
|
34
|
+
// directory that outlives the run is the operating system's problem,
|
|
35
|
+
// not a result worth reporting.
|
|
36
|
+
try {
|
|
37
|
+
rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Deliberately swallowed. See above.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
dirs.length = 0;
|
|
44
|
+
});
|
|
45
|
+
function ctx() {
|
|
46
|
+
const workingDirectory = mkdtempSync(join(tmpdir(), 'namzu-bash-'));
|
|
47
|
+
dirs.push(workingDirectory);
|
|
48
|
+
return { workingDirectory };
|
|
49
|
+
}
|
|
50
|
+
async function run(input, context = ctx()) {
|
|
51
|
+
const parsed = BashTool.inputSchema.parse(input);
|
|
52
|
+
return (await BashTool.execute(parsed, context));
|
|
53
|
+
}
|
|
54
|
+
describe('a command that succeeds', () => {
|
|
55
|
+
it('returns its stdout', async () => {
|
|
56
|
+
const result = await run({ command: 'echo hello' });
|
|
57
|
+
expect(result.success).toBe(true);
|
|
58
|
+
expect(result.output).toContain('hello');
|
|
59
|
+
expect(result.data?.exitCode).toBe(0);
|
|
60
|
+
});
|
|
61
|
+
it('runs in the working directory it was given', async () => {
|
|
62
|
+
const context = ctx();
|
|
63
|
+
writeFileSync(join(context.workingDirectory, 'marker.txt'), 'x');
|
|
64
|
+
const result = await run({ command: `node -e "console.log(require('fs').readdirSync('.'))"` }, context);
|
|
65
|
+
expect(result.output).toContain('marker.txt');
|
|
66
|
+
});
|
|
67
|
+
it('says so rather than returning an empty string', async () => {
|
|
68
|
+
const result = await run({ command: 'node -e ""' });
|
|
69
|
+
expect(result.output).toBe('(no output)');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('a command that fails still says what happened', () => {
|
|
73
|
+
it('reports the exit code instead of throwing', async () => {
|
|
74
|
+
// The whole defect: this used to reject out of `execute`.
|
|
75
|
+
const result = await run({ command: 'node -e "process.exit(3)"' });
|
|
76
|
+
expect(result.success).toBe(false);
|
|
77
|
+
expect(result.data?.exitCode).toBe(3);
|
|
78
|
+
expect(result.error).toContain('exited with code 3');
|
|
79
|
+
});
|
|
80
|
+
it('keeps the output a failing command produced', async () => {
|
|
81
|
+
// The reason a model runs a shell at all: a failing test prints WHY it
|
|
82
|
+
// failed, on stdout, before exiting non-zero.
|
|
83
|
+
const result = await run({
|
|
84
|
+
command: `node -e "console.log('3 tests failed'); process.exit(1)"`,
|
|
85
|
+
});
|
|
86
|
+
expect(result.success).toBe(false);
|
|
87
|
+
expect(result.output, 'the failure output was discarded').toContain('3 tests failed');
|
|
88
|
+
});
|
|
89
|
+
it('keeps stderr too', async () => {
|
|
90
|
+
const result = await run({
|
|
91
|
+
command: `node -e "console.error('compiler said no'); process.exit(2)"`,
|
|
92
|
+
});
|
|
93
|
+
expect(result.output).toContain('compiler said no');
|
|
94
|
+
expect(result.data?.exitCode).toBe(2);
|
|
95
|
+
});
|
|
96
|
+
it('reports a missing command as a failure, not as success', async () => {
|
|
97
|
+
const result = await run({ command: 'definitely-not-a-real-command-xyz' });
|
|
98
|
+
expect(result.success).toBe(false);
|
|
99
|
+
expect(result.error).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
describe('a command that runs out of time', () => {
|
|
103
|
+
it('says it timed out rather than that it exited', async () => {
|
|
104
|
+
// "Ran out of time" and "exited 1" are different diagnoses and lead to
|
|
105
|
+
// different next moves, so the message has to distinguish them.
|
|
106
|
+
const result = await run({
|
|
107
|
+
command: `node -e "setTimeout(() => {}, 10000)"`,
|
|
108
|
+
timeout: 300,
|
|
109
|
+
});
|
|
110
|
+
expect(result.success).toBe(false);
|
|
111
|
+
expect(result.data?.timedOut).toBe(true);
|
|
112
|
+
expect(result.error).toContain('timed out');
|
|
113
|
+
}, 20_000);
|
|
114
|
+
});
|
|
115
|
+
//# sourceMappingURL=bash.proc-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash.proc-test.js","sourceRoot":"","sources":["../../../../src/tools/builtins/__tests__/bash.proc-test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash.test.d.ts","sourceRoot":"","sources":["../../../../src/tools/builtins/__tests__/bash.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { BashTool } from '../bash.js';
|
|
3
|
+
/**
|
|
4
|
+
* What `bash` promises before it runs anything.
|
|
5
|
+
*
|
|
6
|
+
* These need no shell, so they stay in the unit suite. The ones that actually
|
|
7
|
+
* execute a command live in `bash.proc-test.ts` — spawning real processes
|
|
8
|
+
* beside 2594 unit tests flaked four unrelated timing-sensitive ones, so the
|
|
9
|
+
* process suite is separate and has its own CI step.
|
|
10
|
+
*/
|
|
11
|
+
describe('the two clocks agree', () => {
|
|
12
|
+
it('declares a deadline of its own', () => {
|
|
13
|
+
// The executor reads a tool's `timeoutMs` before falling back to its
|
|
14
|
+
// generic default. With none declared, `bash` inherited that default —
|
|
15
|
+
// the same two minutes as its OWN default — so the two agreed by
|
|
16
|
+
// coincidence and diverged the moment a model asked for longer because
|
|
17
|
+
// it knew a build was slow. It got two minutes, from a clock it had not
|
|
18
|
+
// been told about, reported as an abandoned tool rather than as a
|
|
19
|
+
// command that ran out of time.
|
|
20
|
+
expect(BashTool.timeoutMs).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
it('puts that deadline above the longest the model may request', () => {
|
|
23
|
+
// So this tool's own clock is the one that fires, and the executor's is
|
|
24
|
+
// a backstop rather than a second clock racing it.
|
|
25
|
+
const accepted = BashTool.inputSchema.safeParse({ command: 'true', timeout: 10 * 60 * 1000 });
|
|
26
|
+
expect(accepted.success).toBe(true);
|
|
27
|
+
expect(BashTool.timeoutMs).toBeGreaterThan(10 * 60 * 1000);
|
|
28
|
+
});
|
|
29
|
+
it('refuses an over-long request rather than silently shortening it', () => {
|
|
30
|
+
// Refuse, do not degrade. A number the model was not told had changed
|
|
31
|
+
// is how it learns to distrust its own arguments.
|
|
32
|
+
const overCeiling = BashTool.inputSchema.safeParse({
|
|
33
|
+
command: 'true',
|
|
34
|
+
timeout: 60 * 60 * 1000,
|
|
35
|
+
});
|
|
36
|
+
expect(overCeiling.success, 'the ceiling is not enforced').toBe(false);
|
|
37
|
+
});
|
|
38
|
+
it('refuses a nonsensical deadline', () => {
|
|
39
|
+
expect(BashTool.inputSchema.safeParse({ command: 'true', timeout: 0 }).success).toBe(false);
|
|
40
|
+
expect(BashTool.inputSchema.safeParse({ command: 'true', timeout: -1 }).success).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
it('applies its default when none is given', () => {
|
|
43
|
+
const parsed = BashTool.inputSchema.parse({ command: 'true' });
|
|
44
|
+
expect(parsed.timeout).toBeGreaterThan(0);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('the input is closed before a shell ever sees it', () => {
|
|
48
|
+
it('refuses an empty command', () => {
|
|
49
|
+
expect(BashTool.inputSchema.safeParse({ command: '' }).success).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
it('accepts a numeric timeout sent as a string', () => {
|
|
52
|
+
// Providers do this, and the coercion is deliberate.
|
|
53
|
+
const parsed = BashTool.inputSchema.parse({ command: 'true', timeout: '5000' });
|
|
54
|
+
expect(parsed.timeout).toBe(5000);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('the danger flag reads the command', () => {
|
|
58
|
+
it('marks a destructive command destructive', () => {
|
|
59
|
+
expect(BashTool.isDestructive?.({ command: 'rm -rf /', timeout: 1000 })).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
it('leaves an ordinary command alone', () => {
|
|
62
|
+
expect(BashTool.isDestructive?.({ command: 'ls -la', timeout: 1000 })).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=bash.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash.test.js","sourceRoot":"","sources":["../../../../src/tools/builtins/__tests__/bash.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"AAkEA,eAAO,MAAM,QAAQ;;;EA6JnB,CAAA"}
|
|
@@ -17,14 +17,32 @@ const execAsync = promisify(exec);
|
|
|
17
17
|
// slow; the point is that the DEFAULT is survivable.
|
|
18
18
|
const DEFAULT_BASH_TIMEOUT_MS = readPositiveIntEnv('NAMZU_BASH_TIMEOUT_MS', 2 * 60 * 1000);
|
|
19
19
|
const DEFAULT_BASH_MAX_BUFFER_BYTES = readPositiveIntEnv('NAMZU_BASH_MAX_BUFFER_BYTES', 100 * 1024 * 1024);
|
|
20
|
+
/**
|
|
21
|
+
* The longest deadline this tool will accept from the model.
|
|
22
|
+
*
|
|
23
|
+
* There are two clocks on a bash call and until now only one of them was
|
|
24
|
+
* declared. This tool enforces `input.timeout` itself; the EXECUTOR enforces
|
|
25
|
+
* a separate per-tool deadline, and with none declared here it fell back to
|
|
26
|
+
* its own generic default — also two minutes. The two agreed by coincidence,
|
|
27
|
+
* so a model that asked for five minutes because it knew the build was slow
|
|
28
|
+
* got two, from a clock it had not been told about, reported as an abandoned
|
|
29
|
+
* tool rather than as a command that ran out of time.
|
|
30
|
+
*
|
|
31
|
+
* So the tool declares a ceiling and the executor is given a deadline above
|
|
32
|
+
* it (see `timeoutMs` on the definition), which makes this the only clock
|
|
33
|
+
* that can fire in practice. A request past the ceiling is REFUSED rather
|
|
34
|
+
* than quietly clamped: the model asked for something specific, and silently
|
|
35
|
+
* giving it a different number is how it learns to distrust the answer.
|
|
36
|
+
*/
|
|
37
|
+
const MAX_BASH_TIMEOUT_MS = readPositiveIntEnv('NAMZU_BASH_MAX_TIMEOUT_MS', 10 * 60 * 1000);
|
|
20
38
|
const inputSchema = z.object({
|
|
21
39
|
command: z
|
|
22
40
|
.string()
|
|
23
41
|
.min(1)
|
|
24
42
|
.describe('The bash command to execute. Required, non-empty. Single command per call (use `&&` / `;` chaining for compound commands). Avoid heredocs that span more than a few hundred bytes — large content should be created with `write`, then extended with `edit` insertLine: "end", not piped into bash.'),
|
|
25
43
|
timeout: z
|
|
26
|
-
.preprocess((v) => (typeof v === 'string' ? Number(v) : v), z.number().default(DEFAULT_BASH_TIMEOUT_MS))
|
|
27
|
-
.describe(`Command timeout in milliseconds. Default: ${DEFAULT_BASH_TIMEOUT_MS}
|
|
44
|
+
.preprocess((v) => (typeof v === 'string' ? Number(v) : v), z.number().positive().max(MAX_BASH_TIMEOUT_MS).default(DEFAULT_BASH_TIMEOUT_MS))
|
|
45
|
+
.describe(`Command timeout in milliseconds. Default: ${DEFAULT_BASH_TIMEOUT_MS}, maximum: ${MAX_BASH_TIMEOUT_MS}. For work that legitimately runs longer than the maximum, start it in the background and poll, rather than holding the turn open.`),
|
|
28
46
|
});
|
|
29
47
|
function isDangerousCommand(command) {
|
|
30
48
|
return DANGEROUS_PATTERNS.some((pattern) => pattern.test(command));
|
|
@@ -38,6 +56,12 @@ export const BashTool = defineTool({
|
|
|
38
56
|
readOnly: false,
|
|
39
57
|
destructive: (input) => isDangerousCommand(input.command),
|
|
40
58
|
concurrencySafe: false,
|
|
59
|
+
// Above the ceiling the input schema accepts, so the executor's deadline
|
|
60
|
+
// is a backstop rather than a second clock racing this tool's own. It used
|
|
61
|
+
// to be undefined, which meant the executor's generic default applied —
|
|
62
|
+
// the same two minutes as this tool's DEFAULT, so they agreed by accident
|
|
63
|
+
// and diverged the moment a model asked for longer.
|
|
64
|
+
timeoutMs: MAX_BASH_TIMEOUT_MS + 30_000,
|
|
41
65
|
async execute(input, context) {
|
|
42
66
|
if (isDangerousCommand(input.command)) {
|
|
43
67
|
return {
|
|
@@ -111,23 +135,71 @@ export const BashTool = defineTool({
|
|
|
111
135
|
// a Stop tore down the model stream and left the command running,
|
|
112
136
|
// and the executor's deadline could only ever DETACH from the tool
|
|
113
137
|
// rather than end the work it started.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
// `exec` REJECTS on a non-zero exit, on its own timeout, and on a
|
|
139
|
+
// kill — and the rejection carries `stdout`, `stderr`, `code` and
|
|
140
|
+
// `killed`. Letting it propagate threw all of that away: the registry
|
|
141
|
+
// turned the throw into a structured failure, so the model was told a
|
|
142
|
+
// command failed and not one word about how.
|
|
143
|
+
//
|
|
144
|
+
// That is the common case, not an edge one. A failing test run and a
|
|
145
|
+
// failing build are the two things an agent runs bash for most, and
|
|
146
|
+
// both exit non-zero WITH the output that explains why. The sandbox
|
|
147
|
+
// branch above already reports all of it; this branch did not, so the
|
|
148
|
+
// same command told the model two different amounts depending on where
|
|
149
|
+
// it happened to run.
|
|
150
|
+
try {
|
|
151
|
+
const { stdout, stderr } = await execAsync(input.command, {
|
|
152
|
+
cwd: context.workingDirectory,
|
|
153
|
+
timeout: input.timeout,
|
|
154
|
+
env: { ...process.env, ...context.env },
|
|
155
|
+
maxBuffer: DEFAULT_BASH_MAX_BUFFER_BYTES,
|
|
156
|
+
signal: context.abortSignal,
|
|
157
|
+
});
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
output: formatShellOutput(stdout, stderr) || '(no output)',
|
|
161
|
+
data: { exitCode: 0 },
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const failure = err;
|
|
166
|
+
// A caller-owned Stop is the caller's, not a command failure.
|
|
167
|
+
if (context.abortSignal?.aborted)
|
|
168
|
+
throw err;
|
|
169
|
+
// `exec` reports its own timeout as a kill, and the distinction
|
|
170
|
+
// matters to the model: "ran out of time" is a different next move
|
|
171
|
+
// from "exited 1".
|
|
172
|
+
const timedOut = failure.killed === true && failure.signal === 'SIGTERM';
|
|
173
|
+
const exitCode = typeof failure.code === 'number' ? failure.code : undefined;
|
|
174
|
+
const output = formatShellOutput(failure.stdout, failure.stderr);
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
output: output || '(no output)',
|
|
178
|
+
data: {
|
|
179
|
+
...(exitCode !== undefined ? { exitCode } : {}),
|
|
180
|
+
timedOut,
|
|
181
|
+
...(failure.signal ? { signal: failure.signal } : {}),
|
|
182
|
+
},
|
|
183
|
+
error: timedOut
|
|
184
|
+
? `Command timed out after ${input.timeout}ms. Any output it produced before the deadline is above.`
|
|
185
|
+
: exitCode !== undefined
|
|
186
|
+
? `Command exited with code ${exitCode}`
|
|
187
|
+
: `Command failed: ${failure.message}`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
129
190
|
},
|
|
130
191
|
});
|
|
192
|
+
/**
|
|
193
|
+
* The two streams, labelled, with empty ones left out.
|
|
194
|
+
*
|
|
195
|
+
* Shared by the success and failure paths so a command tells the model the
|
|
196
|
+
* same shape either way — the failure path used to tell it nothing at all.
|
|
197
|
+
*/
|
|
198
|
+
function formatShellOutput(stdout, stderr) {
|
|
199
|
+
return [stdout ? `STDOUT:\n${stdout}` : '', stderr ? `STDERR:\n${stderr}` : '']
|
|
200
|
+
.filter(Boolean)
|
|
201
|
+
.join('\n\n');
|
|
202
|
+
}
|
|
131
203
|
function readPositiveIntEnv(key, fallback) {
|
|
132
204
|
const value = process.env[key]?.trim();
|
|
133
205
|
if (!value)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;AACjC,kEAAkE;AAClE,kEAAkE;AAClE,mEAAmE;AACnE,gEAAgE;AAChE,iEAAiE;AACjE,oCAAoC;AACpC,wEAAwE;AACxE,yEAAyE;AACzE,wEAAwE;AACxE,wEAAwE;AACxE,qDAAqD;AACrD,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,uBAAuB,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;AAC1F,MAAM,6BAA6B,GAAG,kBAAkB,CACvD,6BAA6B,EAC7B,GAAG,GAAG,IAAI,GAAG,IAAI,CACjB,CAAA;AAED,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC;SACR,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACR,qSAAqS,CACrS;IACF,OAAO,EAAE,CAAC;SACR,UAAU,CACV,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAC9C,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/tools/builtins/bash.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow-delegation.test.d.ts","sourceRoot":"","sources":["../../../../src/tools/coordinator/__tests__/allow-delegation.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { buildCoordinatorTools } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a run may delegate is not the same question as who it may delegate
|
|
5
|
+
* to, and only the caller can answer the first.
|
|
6
|
+
*
|
|
7
|
+
* The roster answers WHO. It cannot answer WHETHER, because two runs are
|
|
8
|
+
* indistinguishable in it: a supervisor whose roster happens to hold one
|
|
9
|
+
* specialist, where delegating is the point; and a run whose own persona IS
|
|
10
|
+
* that specialist, where delegating is delegating to itself. A host builds the
|
|
11
|
+
* second by putting a specialist's persona into the supervisor shell and its
|
|
12
|
+
* id into the roster — so a predicate comparing the roster against the
|
|
13
|
+
* executing agent sees two different ids and cheerfully says "can delegate".
|
|
14
|
+
*
|
|
15
|
+
* Measured before this existed: such a run carried `create_task`,
|
|
16
|
+
* `wait_for_task`, `cancel_task` and `agent_task_list`, byte-identical to a
|
|
17
|
+
* run that could actually delegate, and the model could only discover the
|
|
18
|
+
* refusal by spending a turn on it.
|
|
19
|
+
*/
|
|
20
|
+
const gateway = {
|
|
21
|
+
listTasks: () => [],
|
|
22
|
+
onTaskCompleted: () => () => { },
|
|
23
|
+
};
|
|
24
|
+
function namesFor(opts) {
|
|
25
|
+
return buildCoordinatorTools({
|
|
26
|
+
gateway,
|
|
27
|
+
workingDirectory: '/tmp/test',
|
|
28
|
+
allowedAgentIds: opts.agentIds,
|
|
29
|
+
...(opts.allowDelegation !== undefined ? { allowDelegation: opts.allowDelegation } : {}),
|
|
30
|
+
...(opts.withHitl
|
|
31
|
+
? { resumeHandler: (async () => ({ action: 'continue' })), runId: 'run_1' }
|
|
32
|
+
: {}),
|
|
33
|
+
}).map((t) => t.name);
|
|
34
|
+
}
|
|
35
|
+
describe('a run can decline to delegate while still naming who it would have called', () => {
|
|
36
|
+
it('withholds the delegation tools when delegation is off', () => {
|
|
37
|
+
const names = namesFor({ agentIds: ['specialist'], allowDelegation: false });
|
|
38
|
+
expect(names).not.toContain('create_task');
|
|
39
|
+
expect(names).not.toContain('wait_for_task');
|
|
40
|
+
expect(names).not.toContain('cancel_task');
|
|
41
|
+
});
|
|
42
|
+
it('produces exactly the empty-roster surface', () => {
|
|
43
|
+
// The two reasons differ but the outcome is the same one tool, so a
|
|
44
|
+
// reader does not have to hold two shapes in their head.
|
|
45
|
+
expect(namesFor({ agentIds: ['specialist'], allowDelegation: false })).toEqual(namesFor({ agentIds: [] }));
|
|
46
|
+
});
|
|
47
|
+
it('keeps the listing, because a run may still want to see what is running', () => {
|
|
48
|
+
expect(namesFor({ agentIds: ['specialist'], allowDelegation: false })).toContain('agent_task_list');
|
|
49
|
+
});
|
|
50
|
+
it('leaves the human channel alone — that is not delegation', () => {
|
|
51
|
+
// `approve_plan` and `ask_user_question` are the HITL park surface. A
|
|
52
|
+
// run that must not delegate needs them as much as any other.
|
|
53
|
+
const names = namesFor({ agentIds: ['specialist'], allowDelegation: false, withHitl: true });
|
|
54
|
+
expect(names).toContain('ask_user_question');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('an absent flag changes nothing', () => {
|
|
58
|
+
it('mounts the full surface, as it always did', () => {
|
|
59
|
+
const names = namesFor({ agentIds: ['specialist'] });
|
|
60
|
+
expect(names).toContain('create_task');
|
|
61
|
+
expect(names).toContain('wait_for_task');
|
|
62
|
+
expect(names).toContain('cancel_task');
|
|
63
|
+
expect(names).toContain('agent_task_list');
|
|
64
|
+
});
|
|
65
|
+
it('is identical to opting in explicitly', () => {
|
|
66
|
+
// So adopting the flag cannot change a caller that says yes out loud.
|
|
67
|
+
expect(namesFor({ agentIds: ['a', 'b'], allowDelegation: true })).toEqual(namesFor({ agentIds: ['a', 'b'] }));
|
|
68
|
+
});
|
|
69
|
+
it('still withholds everything on an empty roster, flag or no flag', () => {
|
|
70
|
+
expect(namesFor({ agentIds: [], allowDelegation: true })).toEqual(['agent_task_list']);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('the flag is absolute', () => {
|
|
74
|
+
it('cannot be overridden back on', () => {
|
|
75
|
+
// Worth a test precisely because the opposite is the intuitive guess:
|
|
76
|
+
// "explicit beats implicit" would say a runtime override should win.
|
|
77
|
+
// It cannot, mechanically — the override pass in SupervisorAgent runs
|
|
78
|
+
// over the array this builder returns, and there is no entry for it to
|
|
79
|
+
// act on. And it should not: both values come from the same caller in
|
|
80
|
+
// the same call, so "this run must not delegate" plus "give it
|
|
81
|
+
// create_task" is a caller contradicting itself, not one who knows
|
|
82
|
+
// something extra.
|
|
83
|
+
//
|
|
84
|
+
// Same rule the empty roster has always had.
|
|
85
|
+
const tools = buildCoordinatorTools({
|
|
86
|
+
gateway,
|
|
87
|
+
workingDirectory: '/tmp/test',
|
|
88
|
+
allowedAgentIds: ['specialist'],
|
|
89
|
+
allowDelegation: false,
|
|
90
|
+
});
|
|
91
|
+
expect(tools.find((t) => t.name === 'create_task')).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//# sourceMappingURL=allow-delegation.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"allow-delegation.test.js","sourceRoot":"","sources":["../../../../src/tools/coordinator/__tests__/allow-delegation.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -24,6 +24,15 @@ export interface CoordinatorToolsOptions {
|
|
|
24
24
|
workingDirectory: string;
|
|
25
25
|
runtimeContext?: AgentRuntimeContext;
|
|
26
26
|
allowedAgentIds: string[];
|
|
27
|
+
/**
|
|
28
|
+
* May this run delegate at all? Defaults to true.
|
|
29
|
+
*
|
|
30
|
+
* Same field, same name, as SupervisorAgentConfig.allowDelegation — the
|
|
31
|
+
* name is kept identical deliberately. This options bag already renames
|
|
32
|
+
* agentIds to allowedAgentIds, and a second rename on the road between
|
|
33
|
+
* the config and the decision would make the road untraceable.
|
|
34
|
+
*/
|
|
35
|
+
allowDelegation?: boolean;
|
|
27
36
|
taskStore?: TaskStore;
|
|
28
37
|
runId?: RunId;
|
|
29
38
|
getPlanManager?: () => PlanManager | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/coordinator/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAChG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAK/D,MAAM,MAAM,oBAAoB,GAAG,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IACL,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC1B,KACG,IAAI,CAAA;AAET,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,WAAW,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAA;IACpC,eAAe,EAAE,MAAM,EAAE,CAAA;IAEzB,SAAS,CAAC,EAAE,SAAS,CAAA;IAErB,KAAK,CAAC,EAAE,KAAK,CAAA;IAEb,cAAc,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAA;IAE9C,cAAc,CAAC,EAAE,oBAAoB,CAAA;IAErC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,eAAe,CAAA;IAEjC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAE7B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAA;IAEpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;CAC/B;AA+ID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,qBAAqB,QAAiB,CAAA;AAEnD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,uBAAuB,GAAG,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/coordinator/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAA;AAChG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AACpE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,2BAA2B,CAAA;AAClF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAA;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAK/D,MAAM,MAAM,oBAAoB,GAAG,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IACL,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC1B,KACG,IAAI,CAAA;AAET,MAAM,WAAW,uBAAuB;IACvC,OAAO,EAAE,WAAW,CAAA;IACpB,gBAAgB,EAAE,MAAM,CAAA;IACxB,cAAc,CAAC,EAAE,mBAAmB,CAAA;IACpC,eAAe,EAAE,MAAM,EAAE,CAAA;IAEzB;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IAEzB,SAAS,CAAC,EAAE,SAAS,CAAA;IAErB,KAAK,CAAC,EAAE,KAAK,CAAA;IAEb,cAAc,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAA;IAE9C,cAAc,CAAC,EAAE,oBAAoB,CAAA;IAErC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,eAAe,CAAA;IAEjC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAA;IAE7B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAA;IAEpC;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;CAC/B;AA+ID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,qBAAqB,QAAiB,CAAA;AAEnD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,uBAAuB,GAAG,cAAc,EAAE,CAmwBrF"}
|
|
@@ -158,7 +158,7 @@ const LISTED_RESULT_LIMIT = 2_000;
|
|
|
158
158
|
*/
|
|
159
159
|
export const DELEGATION_TIMEOUT_MS = 60 * 60 * 1000;
|
|
160
160
|
export function buildCoordinatorTools(opts) {
|
|
161
|
-
const { gateway, allowedAgentIds: agentIds, taskStore, runId, getPlanManager, resumeHandler, questionParks, pendingAnswers, completionInbox,
|
|
161
|
+
const { gateway, allowedAgentIds: agentIds, allowDelegation, taskStore, runId, getPlanManager, resumeHandler, questionParks, pendingAnswers, completionInbox,
|
|
162
162
|
// `onTaskLaunched` was the entry point for the old
|
|
163
163
|
// non-blocking + envelope-injection flow. create_task is now
|
|
164
164
|
// blocking, so the callback is no longer wired here.
|
|
@@ -578,7 +578,24 @@ export function buildCoordinatorTools(opts) {
|
|
|
578
578
|
// had to invent something to say — and one that would not do that was left
|
|
579
579
|
// calling `agent_task_list` in a sleep loop. That was never the model
|
|
580
580
|
// misbehaving; it was the only move available.
|
|
581
|
-
|
|
581
|
+
// Two independent reasons not to mount the delegation surface, and the
|
|
582
|
+
// second one is not derivable from the first.
|
|
583
|
+
//
|
|
584
|
+
// An empty roster answers WHO may be called: nobody, so the tools have
|
|
585
|
+
// nothing to act on. `allowDelegation: false` answers WHETHER this run may
|
|
586
|
+
// call anyone, which a non-empty roster cannot settle — a host that runs a
|
|
587
|
+
// specialist by putting its persona into the supervisor shell and its id
|
|
588
|
+
// into the roster has a list of one and must still delegate to nobody.
|
|
589
|
+
// From inside this function that run is indistinguishable from a
|
|
590
|
+
// supervisor whose roster happens to hold a single specialist, so the
|
|
591
|
+
// caller states the fact rather than the SDK guessing it.
|
|
592
|
+
//
|
|
593
|
+
// `!== false` rather than truthiness, so an absent flag keeps today's
|
|
594
|
+
// behaviour exactly.
|
|
595
|
+
const canDelegate = agentIds.length > 0 && allowDelegation !== false;
|
|
596
|
+
const tools = canDelegate
|
|
597
|
+
? [createTask, waitForTaskTool, cancelTask, agentTaskList]
|
|
598
|
+
: [agentTaskList];
|
|
582
599
|
if (getPlanManager) {
|
|
583
600
|
const approvePlan = defineTool({
|
|
584
601
|
name: 'approve_plan',
|