@namzu/sdk 14.0.4 → 14.0.6

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 CHANGED
@@ -1,5 +1,82 @@
1
1
  # Changelog
2
2
 
3
+ ## 14.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 9ee23f1: Stop the package README naming two providers when seven ship
8
+
9
+ "Provider abstraction. OpenRouter and AWS Bedrock today" has been wrong for a
10
+ long time. Seven driver packages ship — and a reader deciding whether this
11
+ kernel can talk to the service they already pay for was being told, on the
12
+ registry page, that it probably cannot.
13
+
14
+ That is the expensive direction for this particular sentence to be wrong in:
15
+ it does not cause a bug, it causes someone to close the tab.
16
+
17
+ Also removed the third-party product names from "What Namzu Is Not". That
18
+ section explained namzu's scope by listing other people's products, which is
19
+ the one thing this repository's own naming rule refuses — a design explained
20
+ by reference to somebody else's has borrowed its shape, and the borrowing
21
+ outlives the sentence. The scope boundaries are unchanged and now stated as
22
+ categories: no front-end framework bindings, no web-framework or edge-runtime
23
+ plumbing, no embedded vector engine.
24
+
25
+ Prose only. No runtime change.
26
+
27
+ ## 14.0.5
28
+
29
+ ### Patch Changes
30
+
31
+ - a2a7aed: `LocalTaskGateway` stops remembering every task it ever launched.
32
+
33
+ `trackedTaskIds` and `settledHandles` had `add` and `set` and no removal
34
+ anywhere in the file. The comment above them said "bounded by the number the
35
+ gateway itself launched", which is true and is not a bound: a gateway built per
36
+ run is bounded by that run, but `SupervisorAgentConfig.gateway` lets a host
37
+ supply its own, and a long-lived host reusing one accumulates an id and a
38
+ settled handle for every task it ever launched, for the life of the process.
39
+
40
+ Both are now capped at 1000 and evicted oldest-first. The cap is far above any
41
+ realistic single run — a fan-out is eight, a long supervisory run is dozens —
42
+ so the listing a supervisor reads at the end of its run is unchanged.
43
+
44
+ The two are evicted **together**. Dropping a tracked id while keeping its
45
+ handle would leave memory nothing can reach, since `listTasks` walks the ids;
46
+ dropping a handle while keeping its id would make a task that ran read as one
47
+ that never launched, which is the exact defect the settled-handle map exists to
48
+ fix. Removing either half of the paired delete fails a test.
49
+
50
+ - 688d6af: Stop the package README promising filesystem isolation the sandbox refuses to claim
51
+
52
+ The README that ships inside this tarball — and therefore the page on the
53
+ registry — said tools run "inside an OS-enforced jail with deny-default file
54
+ I/O", and attributed that to both sandbox tiers.
55
+
56
+ `src/sandbox/isolation.ts` says otherwise, and says it deliberately. The
57
+ namespace tier reports `filesystem: false`, because it unshares the mount
58
+ namespace and never remounts anything, so the child still sees the whole host
59
+ filesystem. The comment beside that table is explicit that claiming otherwise
60
+ would reintroduce the exact defect the table exists to end.
61
+
62
+ So the code was careful and the README was not, in the one direction that
63
+ costs something. An overclaimed security control is worse than an absent one:
64
+ a reader who believes a boundary is there stops looking for one, and this
65
+ sentence was reachable from the registry page by anyone deciding whether it
66
+ was safe to run untrusted input through a tool call.
67
+
68
+ Nothing about the runtime changes. What changes is that the README now
69
+ reproduces the isolation table per tier, names the tier that does **not**
70
+ enforce filesystem isolation and why, and points at `assertIsolation`,
71
+ `isolationOf`, `missingIsolation` and `describeIsolation` — which have always
72
+ been exported, and which refuse a run whose required control the host cannot
73
+ supply rather than quietly running it at a weaker tier.
74
+
75
+ No action required on upgrade. If you read that sentence and concluded your
76
+ tool calls were filesystem-confined on a namespace host, they were not — call
77
+ `assertIsolation` with the controls you actually require, and it will refuse
78
+ rather than pretend.
79
+
3
80
  ## 14.0.4
4
81
 
5
82
  ### Patch Changes
package/README.md CHANGED
@@ -14,9 +14,9 @@
14
14
 
15
15
  Most "agent frameworks" today are really application frameworks. They ship chat UIs, picking UI layouts, batteries-included hosted dashboards, vendor-specific fast paths, and integration drivers for a handful of databases. You get something you can demo in an hour, and three months later you own a stack where the same framework dictates your frontend, your database, your observability, and your model vendor.
16
16
 
17
- We think agent software should be layered like Unix. At the bottom there needs to be a **kernel**: something to isolate processes, schedule tool calls, manage memory pressure, propagate signals across a call tree, persist checkpoints so a run can resume after a crash, mediate inter-process communication, and produce an auditable event stream. Above the kernel there is user space — shells, editors, IDEs, voice gateways, React apps. The kernel does not care which shell you pick; the shell cannot break the isolation the kernel provides.
17
+ We think agent software should be layered the way an operating system is. At the bottom there needs to be a **kernel**: something to isolate processes, schedule tool calls, manage memory pressure, propagate signals across a call tree, persist checkpoints so a run can resume after a crash, mediate inter-process communication, and produce an auditable event stream. Above the kernel there is user space — shells, editors, IDEs, voice gateways, web front-ends. The kernel does not care which shell you pick; the shell cannot break the isolation the kernel provides.
18
18
 
19
- **Namzu is the kernel.** It runs agents the way Unix runs processes. It does not render UI, it does not pick your database, it does not favor one LLM vendor. It gives you a surface — typed, versioned, documented — that any UI, any storage backend, and any model can plug into. The surface is small and stable; the guts underneath are deep.
19
+ **Namzu is the kernel.** It runs agents the way an operating system runs processes. It does not render UI, it does not pick your database, it does not favor one LLM vendor. It gives you a surface — typed, versioned, documented — that any UI, any storage backend, and any model can plug into. The surface is small and stable; the guts underneath are deep.
20
20
 
21
21
  ---
22
22
 
@@ -24,7 +24,7 @@ We think agent software should be layered like Unix. At the bottom there needs t
24
24
 
25
25
  Namzu is a single-process TypeScript kernel with the following responsibilities:
26
26
 
27
- - **Process execution and isolation.** Tools run inside OS-level sandboxes: Seatbelt (SBPL) on macOS, mount + PID namespaces on Linux. Deny-default file I/O, scoped network access, enforced resource limits. No Docker, no container runtime, no daemon, no sidecar.
27
+ - **Process execution and isolation.** Tools run outside the host process, under OS-level containment whose enforced controls vary by tier and the kernel states per tier which of filesystem, network and process isolation it actually enforces, refusing to start a run whose required control the host cannot supply rather than silently downgrading it. No container runtime, no daemon, no sidecar. See [The Boundary](#1-the-boundary-sandbox-sandbox) for the table.
28
28
  - **Agent lifecycle.** Parent/child agent spawn with depth tracking, budget splitting, and causal trace linkage. A supervisor can fork a subtree of agents and get their results back, with each child isolated from its siblings.
29
29
  - **Scheduling.** Per-run token, cost, wall-clock, and iteration budgets. Limit checker, task router (cheap model for compaction, expensive for coding), tool tiering (LLM learns to prefer cheaper tools first).
30
30
  - **Signals.** `AbortController` tree spanning parent and children. `cancel(taskId)` and `cancelAll(parentRunId)` propagate. Runs can be paused and resumed, aborted cleanly, and emit lifecycle events for every transition.
@@ -40,7 +40,7 @@ Namzu is a single-process TypeScript kernel with the following responsibilities:
40
40
  - **Human-in-the-loop.** Structured plan review, per-tool approval with destructiveness flags, typed decision contracts, checkpoint/resume across sessions.
41
41
  - **Plugin system.** Lifecycle-hooked plugin loader with MCP contributions, tool contributions, and manifest-driven resolution.
42
42
  - **Multi-tenant isolation from day one.** Connector registries, vaults, config, and stores are tenant-scoped. Two organizations can share a process without cross-contamination.
43
- - **Provider abstraction.** OpenRouter and AWS Bedrock today; the `Provider` interface is narrow enough that adding another vendor is an afternoon. BYOK everywhere, no hidden hot paths for any vendor.
43
+ - **Provider abstraction.** Seven drivers ship today, each its own package installed only if you use it, plus a scriptable mock pre-registered in the kernel. The `LLMProvider` interface is narrow enough that adding another is an afternoon. BYOK everywhere, no hidden hot paths for any vendor.
44
44
  - **Telemetry.** OpenTelemetry-native spans and metrics. Cost accounting (input tokens, output tokens, cached tokens, cache write tokens, cache discount) flows from the provider into per-run, per-tenant rollups.
45
45
  - **Prompt cache integration.** Hash-based system-prompt cache per thread, integrated with provider cache controls (OpenRouter `cacheControl` today, more planned), plus full cache telemetry in every run.
46
46
  - **Vault.** BYOK credentials and secrets, tenant-scoped, pluggable backend.
@@ -52,11 +52,11 @@ Every one of those bullets points at code that exists today in `src/`. The archi
52
52
 
53
53
  Equally important for scoping expectations:
54
54
 
55
- - **Not a chat SDK.** There are no React, Svelte, or Vue hooks, no generative UI components, no `useChat`. Your UI framework is your choice; the kernel hands you a typed event stream.
55
+ - **Not a chat SDK.** No front-end framework bindings, no generative UI components, no ready-made chat hook. Your UI framework is your choice; the kernel hands you a typed event stream.
56
56
  - **Not a hosted service.** There is no dashboard, no Namzu Cloud, no billing page. You run it in your own process.
57
- - **Not a deployment adapter.** No Next.js, Hono, Express, or Cloudflare Workers plumbing in the kernel. Those belong in separate packages or your own infra code.
57
+ - **Not a deployment adapter.** No web-framework or edge-runtime plumbing in the kernel. That belongs in separate packages or your own infra code.
58
58
  - **Not a dev studio.** No bundled playground UI. A playground that consumes the kernel's event protocol could exist as a separate tool; it would not live inside `@namzu/sdk`.
59
- - **Not a vector database.** RAG ships with a pluggable `VectorStore` interface, but the kernel does not embed pgvector or Pinecone. Bring your own.
59
+ - **Not a vector database.** RAG ships with a pluggable `VectorStore` interface; the kernel embeds no vector engine of its own. Bring your own.
60
60
  - **Not an LLM router service.** Task routing is an in-process policy, not a hosted service.
61
61
  - **Not a prompt management UI.** Personas are code-defined (YAML files in your repo), not database rows behind a web form.
62
62
 
@@ -106,9 +106,19 @@ Every folder under `src/` maps to a traditional OS concept. This section walks t
106
106
 
107
107
  ### 1. The Boundary: Sandbox (`sandbox/`)
108
108
 
109
- Sandboxing is the foundation. Tools do not execute in the host process; they execute inside an OS-enforced jail with deny-default file I/O and scoped network access. macOS uses Seatbelt profiles in SBPL format (the same mechanism Apple's own apps use). Linux uses lightweight mount + PID namespaces, without requiring Docker, systemd, or any container runtime. The `SandboxProvider` abstraction (`sandbox/factory.ts`, `sandbox/provider/`) means you can swap in a virtualization-backed or userspace-kernel-backed provider later without touching the rest of the kernel.
109
+ Tools do not execute in the host process. What that buys you **depends on the tier the host can supply, and the tiers do not all enforce the same controls** so the kernel keeps an honest table rather than a promise (`sandbox/isolation.ts`):
110
110
 
111
- The kernel enforces memory, timeout, and max-process limits on top of whatever the sandbox gives you. The goal: a rogue or hallucinated tool call should never wipe your filesystem or exfiltrate arbitrary data, even if the LLM tries very hard.
111
+ | Environment | Filesystem | Network | Process |
112
+ |---|---|---|---|
113
+ | `macos-seatbelt` | yes | yes | yes |
114
+ | `linux-namespace` | **no** | yes | yes |
115
+ | `basic` | no | no | no |
116
+
117
+ `linux-namespace` reports `filesystem: false` deliberately. It unshares the mount namespace but never remounts anything, so the child still sees the whole host filesystem — a private mount table is not confinement, and saying otherwise here would be the exact defect the table exists to prevent.
118
+
119
+ This matters because the failure it guards against is silent. If a run requires a control the host cannot enforce, `assertIsolation` **refuses to start it** rather than proceeding at a weaker tier: a security control that is accepted and then quietly not applied is worse than one that was never offered, because the caller stops looking. Use `isolationOf`, `missingIsolation` and `describeIsolation` to ask what you are actually getting before you rely on it.
120
+
121
+ The `SandboxProvider` abstraction (`sandbox/factory.ts`, `sandbox/provider/`) lets you supply a stronger provider without touching the rest of the kernel. The kernel enforces memory, timeout, and max-process limits on top of whatever the sandbox gives you.
112
122
 
113
123
  ### 2. Interprocess Communication: Bridge (`bridge/`) and Bus (`bus/`)
114
124
 
@@ -136,7 +146,7 @@ These exist because the moment you have more than one agent running in parallel
136
146
  - Emits `agent_pending` on the bus with parent/child/depth metadata
137
147
  - Forwards every child event to the parent's run listener so the supervisor sees what its subtree is doing
138
148
 
139
- When the parent is cancelled — by HITL, by a limit breach, or by an external signal — `cancelAll(parentRunId)` walks the subtree and aborts every descendant. This is the Unix `SIGKILL` to a process group.
149
+ When the parent is cancelled — by HITL, by a limit breach, or by an external signal — `cancelAll(parentRunId)` walks the subtree and aborts every descendant. This is the equivalent of signalling a whole process group.
140
150
 
141
151
  `manager/connector/` manages the lifecycle of external connectors (MCP servers, HTTP connectors). `manager/plan/lifecycle.ts` coordinates HITL plan review. `manager/run/persistence.ts` is the run-level persistence surface, and `manager/run/emergency.ts` is the emergency-save subsystem (see §9 below).
142
152
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=a-gateway-that-forgets-the-oldest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a-gateway-that-forgets-the-oldest.test.d.ts","sourceRoot":"","sources":["../../../src/gateway/__tests__/a-gateway-that-forgets-the-oldest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,96 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { LocalTaskGateway } from '../local.js';
3
+ /**
4
+ * The gateway's two ledgers — `trackedTaskIds` and `settledHandles` — had `add`
5
+ * and `set` and no removal anywhere. The doc called them "bounded by the number
6
+ * the gateway itself launched", which is true and is not a bound: a gateway
7
+ * built per run is bounded by that run, but `SupervisorAgentConfig.gateway`
8
+ * lets a host supply its own, and a long-lived host reusing one accumulates an
9
+ * id and a settled handle for every task it ever launched.
10
+ *
11
+ * Eviction is oldest-first and takes both together. Dropping a tracked id while
12
+ * keeping its handle — or the reverse — would make a task that ran read as one
13
+ * that never launched, which is precisely the defect the settled-handle map was
14
+ * added to fix.
15
+ */
16
+ const CAP = 1_000;
17
+ class CountingManager {
18
+ n = 0;
19
+ async sendMessage() {
20
+ this.n++;
21
+ return { taskId: `tsk_${this.n}`, state: 'running' };
22
+ }
23
+ async waitForCompletion() { }
24
+ getInstance() {
25
+ return undefined;
26
+ }
27
+ cancel() { }
28
+ }
29
+ function context() {
30
+ return {
31
+ parentRunId: 'run_p',
32
+ parentAgentId: 'sup',
33
+ parentAbortController: new AbortController(),
34
+ depth: 0,
35
+ budgetTracker: { total: 1_000_000, remaining: 1_000_000 },
36
+ tenantId: 'tnt_g',
37
+ sessionId: 'ses_g',
38
+ projectId: 'prj_g',
39
+ };
40
+ }
41
+ function tracked(gateway) {
42
+ return gateway.trackedTaskIds;
43
+ }
44
+ function settled(gateway) {
45
+ return gateway.settledHandles;
46
+ }
47
+ async function launch(gateway, count) {
48
+ for (let i = 0; i < count; i++) {
49
+ await gateway.createTask({
50
+ agentId: 'w',
51
+ prompt: 'go',
52
+ workingDirectory: '/tmp',
53
+ });
54
+ }
55
+ }
56
+ describe('a gateway that outlives its run forgets the oldest', () => {
57
+ it('keeps every task of a run that never reaches the cap', async () => {
58
+ // The control, and the case that must not change: a supervisor reading
59
+ // its listing at the end of a run sees everything it launched.
60
+ const gateway = new LocalTaskGateway(new CountingManager(), context());
61
+ await launch(gateway, 50);
62
+ expect(tracked(gateway).size).toBe(50);
63
+ });
64
+ it('stops growing once the cap is passed', async () => {
65
+ const gateway = new LocalTaskGateway(new CountingManager(), context());
66
+ await launch(gateway, CAP + 25);
67
+ expect(tracked(gateway).size).toBe(CAP);
68
+ });
69
+ it('drops the oldest, not the newest', async () => {
70
+ // A cap that evicted the most recent would keep the ledger small and
71
+ // answer every question wrongly.
72
+ const gateway = new LocalTaskGateway(new CountingManager(), context());
73
+ await launch(gateway, CAP + 3);
74
+ const ids = tracked(gateway);
75
+ expect(ids.has('tsk_1')).toBe(false);
76
+ expect(ids.has('tsk_3')).toBe(false);
77
+ expect(ids.has(`tsk_${CAP + 3}`)).toBe(true);
78
+ });
79
+ it('never keeps a settled handle for an id it has forgotten', async () => {
80
+ // The two must be evicted together. A handle whose id is gone is
81
+ // unreachable through `listTasks`, which walks the ids — so it would be
82
+ // retained memory serving nothing, and the leak would survive its own
83
+ // fix.
84
+ const gateway = new LocalTaskGateway(new CountingManager(), context());
85
+ await launch(gateway, 10);
86
+ const handles = settled(gateway);
87
+ for (let i = 1; i <= 10; i++)
88
+ handles.set(`tsk_${i}`, { id: i });
89
+ await launch(gateway, CAP + 5);
90
+ const ids = tracked(gateway);
91
+ for (const id of handles.keys()) {
92
+ expect({ id, trackedToo: ids.has(id) }).toEqual({ id, trackedToo: true });
93
+ }
94
+ });
95
+ });
96
+ //# sourceMappingURL=a-gateway-that-forgets-the-oldest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a-gateway-that-forgets-the-oldest.test.js","sourceRoot":"","sources":["../../../src/gateway/__tests__/a-gateway-that-forgets-the-oldest.test.ts"],"names":[],"mappings":"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"}
@@ -59,6 +59,22 @@ export declare class LocalTaskGateway implements TaskGateway {
59
59
  * Called when the task settles, while the manager still holds it.
60
60
  */
61
61
  rememberSettled(taskId: TaskId): void;
62
+ /**
63
+ * Drop the oldest tasks once the ledger passes {@link GATEWAY_TASK_LEDGER_CAP}.
64
+ *
65
+ * A gateway constructed per run is bounded by that run and this never
66
+ * fires. But `SupervisorAgentConfig.gateway` lets a host supply its own,
67
+ * and a long-lived host reusing one accumulates an id and a settled handle
68
+ * per task it ever launched, for the life of the process — the doc above
69
+ * says "bounded by the number the gateway itself launched", which is true
70
+ * and is not a bound when the gateway outlives the run.
71
+ *
72
+ * Both collections are evicted **together and in insertion order**. Losing
73
+ * a tracked id while keeping its handle, or the reverse, would make a task
74
+ * that ran read as one that never launched — which is the exact defect the
75
+ * settled-handle map was added to fix, reintroduced by its own cleanup.
76
+ */
77
+ private forgetOldestBeyondCap;
62
78
  listTasks(): TaskHandle[];
63
79
  /**
64
80
  * Every event a child emits, reduced to "this one is still alive".
@@ -1 +1 @@
1
- {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAI9D,qBAAa,gBAAiB,YAAW,WAAW;IACnD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAE/C,OAAO,CAAC,WAAW,CAAC,CAA2E;IAE/F,OAAO,CAAC,mBAAmB,CAA+C;IAE1E;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAmC;IAC/D,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;gBAGvE,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,gBAAgB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,sBAAsB,GAAG,gBAAgB,CAAC,EACvF,OAAO,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;KAAE;IAWpD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IA6GjE;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kBAAkB;IA0BpB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAShD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAK/C;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKrC,SAAS,IAAI,UAAU,EAAE;IAgBzB;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9D,eAAe,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;CAMnE"}
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,KAAK,EACX,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAe9D,qBAAa,gBAAiB,YAAW,WAAW;IACnD,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,cAAc,CAAyB;IAE/C,OAAO,CAAC,WAAW,CAAC,CAA2E;IAE/F,OAAO,CAAC,mBAAmB,CAA+C;IAE1E;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,cAAc,CAAqC;IAE3D,OAAO,CAAC,oBAAoB,CAAmC;IAC/D,kCAAkC;IAClC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;gBAGvE,YAAY,EAAE,oBAAoB,EAClC,WAAW,EAAE,gBAAgB,EAC7B,QAAQ,CAAC,EAAE,gBAAgB,EAC3B,WAAW,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,GAAG,sBAAsB,GAAG,gBAAgB,CAAC,EACvF,OAAO,CAAC,EAAE;QAAE,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;KAAE;IAWpD,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8GjE;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,kBAAkB;IA0BpB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAShD,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlE,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAIhC,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAK/C;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKrC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;IAS7B,SAAS,IAAI,UAAU,EAAE;IAgBzB;;;;;;;OAOG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9D,eAAe,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;CAMnE"}
@@ -2,6 +2,16 @@ import { taskFailed } from '../tools/coordinator/outcome.js';
2
2
  import { createUserMessage } from '../types/message/index.js';
3
3
  import { toErrorMessage } from '../utils/error.js';
4
4
  import { getRootLogger } from '../utils/logger.js';
5
+ /**
6
+ * How many launched tasks a gateway remembers.
7
+ *
8
+ * High enough that no realistic single run reaches it — a fan-out is eight,
9
+ * a long supervisory run is dozens — so the listing a supervisor reads at the
10
+ * end of its run is always complete. It exists for the host that reuses one
11
+ * gateway across runs, where the alternative is a set and a map that grow for
12
+ * the life of the process.
13
+ */
14
+ const GATEWAY_TASK_LEDGER_CAP = 1_000;
5
15
  export class LocalTaskGateway {
6
16
  agentManager;
7
17
  taskContext;
@@ -114,6 +124,7 @@ export class LocalTaskGateway {
114
124
  });
115
125
  launched.id = task.taskId;
116
126
  this.trackedTaskIds.add(task.taskId);
127
+ this.forgetOldestBeyondCap();
117
128
  this.agentManager
118
129
  .waitForCompletion(task.taskId)
119
130
  .then(() => {
@@ -210,6 +221,30 @@ export class LocalTaskGateway {
210
221
  if (task)
211
222
  this.settledHandles.set(taskId, toHandle(task));
212
223
  }
224
+ /**
225
+ * Drop the oldest tasks once the ledger passes {@link GATEWAY_TASK_LEDGER_CAP}.
226
+ *
227
+ * A gateway constructed per run is bounded by that run and this never
228
+ * fires. But `SupervisorAgentConfig.gateway` lets a host supply its own,
229
+ * and a long-lived host reusing one accumulates an id and a settled handle
230
+ * per task it ever launched, for the life of the process — the doc above
231
+ * says "bounded by the number the gateway itself launched", which is true
232
+ * and is not a bound when the gateway outlives the run.
233
+ *
234
+ * Both collections are evicted **together and in insertion order**. Losing
235
+ * a tracked id while keeping its handle, or the reverse, would make a task
236
+ * that ran read as one that never launched — which is the exact defect the
237
+ * settled-handle map was added to fix, reintroduced by its own cleanup.
238
+ */
239
+ forgetOldestBeyondCap() {
240
+ while (this.trackedTaskIds.size > GATEWAY_TASK_LEDGER_CAP) {
241
+ const oldest = this.trackedTaskIds.values().next().value;
242
+ if (oldest === undefined)
243
+ return;
244
+ this.trackedTaskIds.delete(oldest);
245
+ this.settledHandles.delete(oldest);
246
+ }
247
+ }
213
248
  listTasks() {
214
249
  const handles = [];
215
250
  for (const taskId of this.trackedTaskIds) {
@@ -1 +1 @@
1
- {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/gateway/local.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@namzu/sdk",
3
- "version": "14.0.4",
3
+ "version": "14.0.6",
4
4
  "description": "Open-source AI agent SDK with a built-in runtime. Nothing between you and your agents.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -0,0 +1,130 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import type { AgentManagerContract } from '../../types/agent/manager.js'
4
+ import type { AgentTask, AgentTaskContext } from '../../types/agent/task.js'
5
+ import type { AgentId, TaskId } from '../../types/ids/index.js'
6
+ import { LocalTaskGateway } from '../local.js'
7
+
8
+ /**
9
+ * The gateway's two ledgers — `trackedTaskIds` and `settledHandles` — had `add`
10
+ * and `set` and no removal anywhere. The doc called them "bounded by the number
11
+ * the gateway itself launched", which is true and is not a bound: a gateway
12
+ * built per run is bounded by that run, but `SupervisorAgentConfig.gateway`
13
+ * lets a host supply its own, and a long-lived host reusing one accumulates an
14
+ * id and a settled handle for every task it ever launched.
15
+ *
16
+ * Eviction is oldest-first and takes both together. Dropping a tracked id while
17
+ * keeping its handle — or the reverse — would make a task that ran read as one
18
+ * that never launched, which is precisely the defect the settled-handle map was
19
+ * added to fix.
20
+ */
21
+
22
+ const CAP = 1_000
23
+
24
+ class CountingManager {
25
+ private n = 0
26
+
27
+ async sendMessage(): Promise<AgentTask> {
28
+ this.n++
29
+ return { taskId: `tsk_${this.n}` as TaskId, state: 'running' } as AgentTask
30
+ }
31
+ async waitForCompletion(): Promise<void> {}
32
+ getInstance(): AgentTask | undefined {
33
+ return undefined
34
+ }
35
+ cancel(): void {}
36
+ }
37
+
38
+ function context(): AgentTaskContext {
39
+ return {
40
+ parentRunId: 'run_p' as never,
41
+ parentAgentId: 'sup',
42
+ parentAbortController: new AbortController(),
43
+ depth: 0,
44
+ budgetTracker: { total: 1_000_000, remaining: 1_000_000 },
45
+ tenantId: 'tnt_g' as never,
46
+ sessionId: 'ses_g' as never,
47
+ projectId: 'prj_g' as never,
48
+ } as unknown as AgentTaskContext
49
+ }
50
+
51
+ function tracked(gateway: LocalTaskGateway): Set<TaskId> {
52
+ return (gateway as unknown as { trackedTaskIds: Set<TaskId> }).trackedTaskIds
53
+ }
54
+
55
+ function settled(gateway: LocalTaskGateway): Map<TaskId, unknown> {
56
+ return (gateway as unknown as { settledHandles: Map<TaskId, unknown> }).settledHandles
57
+ }
58
+
59
+ async function launch(gateway: LocalTaskGateway, count: number): Promise<void> {
60
+ for (let i = 0; i < count; i++) {
61
+ await gateway.createTask({
62
+ agentId: 'w' as AgentId,
63
+ prompt: 'go',
64
+ workingDirectory: '/tmp',
65
+ } as never)
66
+ }
67
+ }
68
+
69
+ describe('a gateway that outlives its run forgets the oldest', () => {
70
+ it('keeps every task of a run that never reaches the cap', async () => {
71
+ // The control, and the case that must not change: a supervisor reading
72
+ // its listing at the end of a run sees everything it launched.
73
+ const gateway = new LocalTaskGateway(
74
+ new CountingManager() as unknown as AgentManagerContract,
75
+ context(),
76
+ )
77
+
78
+ await launch(gateway, 50)
79
+
80
+ expect(tracked(gateway).size).toBe(50)
81
+ })
82
+
83
+ it('stops growing once the cap is passed', async () => {
84
+ const gateway = new LocalTaskGateway(
85
+ new CountingManager() as unknown as AgentManagerContract,
86
+ context(),
87
+ )
88
+
89
+ await launch(gateway, CAP + 25)
90
+
91
+ expect(tracked(gateway).size).toBe(CAP)
92
+ })
93
+
94
+ it('drops the oldest, not the newest', async () => {
95
+ // A cap that evicted the most recent would keep the ledger small and
96
+ // answer every question wrongly.
97
+ const gateway = new LocalTaskGateway(
98
+ new CountingManager() as unknown as AgentManagerContract,
99
+ context(),
100
+ )
101
+
102
+ await launch(gateway, CAP + 3)
103
+
104
+ const ids = tracked(gateway)
105
+ expect(ids.has('tsk_1' as TaskId)).toBe(false)
106
+ expect(ids.has('tsk_3' as TaskId)).toBe(false)
107
+ expect(ids.has(`tsk_${CAP + 3}` as TaskId)).toBe(true)
108
+ })
109
+
110
+ it('never keeps a settled handle for an id it has forgotten', async () => {
111
+ // The two must be evicted together. A handle whose id is gone is
112
+ // unreachable through `listTasks`, which walks the ids — so it would be
113
+ // retained memory serving nothing, and the leak would survive its own
114
+ // fix.
115
+ const gateway = new LocalTaskGateway(
116
+ new CountingManager() as unknown as AgentManagerContract,
117
+ context(),
118
+ )
119
+ await launch(gateway, 10)
120
+ const handles = settled(gateway)
121
+ for (let i = 1; i <= 10; i++) handles.set(`tsk_${i}` as TaskId, { id: i })
122
+
123
+ await launch(gateway, CAP + 5)
124
+
125
+ const ids = tracked(gateway)
126
+ for (const id of handles.keys()) {
127
+ expect({ id, trackedToo: ids.has(id) }).toEqual({ id, trackedToo: true })
128
+ }
129
+ })
130
+ })
@@ -14,6 +14,17 @@ import type { RunEventListener } from '../types/run/events.js'
14
14
  import { toErrorMessage } from '../utils/error.js'
15
15
  import { getRootLogger } from '../utils/logger.js'
16
16
 
17
+ /**
18
+ * How many launched tasks a gateway remembers.
19
+ *
20
+ * High enough that no realistic single run reaches it — a fan-out is eight,
21
+ * a long supervisory run is dozens — so the listing a supervisor reads at the
22
+ * end of its run is always complete. It exists for the host that reuses one
23
+ * gateway across runs, where the alternative is a set and a map that grow for
24
+ * the life of the process.
25
+ */
26
+ const GATEWAY_TASK_LEDGER_CAP = 1_000
27
+
17
28
  export class LocalTaskGateway implements TaskGateway {
18
29
  private agentManager: AgentManagerContract
19
30
  private taskContext: AgentTaskContext
@@ -140,6 +151,7 @@ export class LocalTaskGateway implements TaskGateway {
140
151
 
141
152
  launched.id = task.taskId
142
153
  this.trackedTaskIds.add(task.taskId)
154
+ this.forgetOldestBeyondCap()
143
155
 
144
156
  this.agentManager
145
157
  .waitForCompletion(task.taskId)
@@ -242,6 +254,30 @@ export class LocalTaskGateway implements TaskGateway {
242
254
  if (task) this.settledHandles.set(taskId, toHandle(task))
243
255
  }
244
256
 
257
+ /**
258
+ * Drop the oldest tasks once the ledger passes {@link GATEWAY_TASK_LEDGER_CAP}.
259
+ *
260
+ * A gateway constructed per run is bounded by that run and this never
261
+ * fires. But `SupervisorAgentConfig.gateway` lets a host supply its own,
262
+ * and a long-lived host reusing one accumulates an id and a settled handle
263
+ * per task it ever launched, for the life of the process — the doc above
264
+ * says "bounded by the number the gateway itself launched", which is true
265
+ * and is not a bound when the gateway outlives the run.
266
+ *
267
+ * Both collections are evicted **together and in insertion order**. Losing
268
+ * a tracked id while keeping its handle, or the reverse, would make a task
269
+ * that ran read as one that never launched — which is the exact defect the
270
+ * settled-handle map was added to fix, reintroduced by its own cleanup.
271
+ */
272
+ private forgetOldestBeyondCap(): void {
273
+ while (this.trackedTaskIds.size > GATEWAY_TASK_LEDGER_CAP) {
274
+ const oldest = this.trackedTaskIds.values().next().value
275
+ if (oldest === undefined) return
276
+ this.trackedTaskIds.delete(oldest)
277
+ this.settledHandles.delete(oldest)
278
+ }
279
+ }
280
+
245
281
  listTasks(): TaskHandle[] {
246
282
  const handles: TaskHandle[] = []
247
283
  for (const taskId of this.trackedTaskIds) {