@namzu/cli 18.1.0 → 19.0.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 +5898 -0
- package/README.md +23 -24
- package/dist/bin.js +12 -1
- package/dist/bin.js.map +1 -1
- package/dist/checkpoints/store.d.ts +75 -0
- package/dist/checkpoints/store.d.ts.map +1 -0
- package/dist/checkpoints/store.js +177 -0
- package/dist/checkpoints/store.js.map +1 -0
- package/dist/checkpoints/wrap.d.ts +15 -0
- package/dist/checkpoints/wrap.d.ts.map +1 -0
- package/dist/checkpoints/wrap.js +37 -0
- package/dist/checkpoints/wrap.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +2 -0
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +15 -2
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/provider-wait.d.ts +44 -0
- package/dist/commands/provider-wait.d.ts.map +1 -0
- package/dist/commands/provider-wait.js +65 -0
- package/dist/commands/provider-wait.js.map +1 -0
- package/dist/commands/run-flags.d.ts +13 -1
- package/dist/commands/run-flags.d.ts.map +1 -1
- package/dist/commands/run-flags.js +41 -0
- package/dist/commands/run-flags.js.map +1 -1
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +33 -13
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +132 -78
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/stdin.d.ts +17 -0
- package/dist/commands/stdin.d.ts.map +1 -0
- package/dist/commands/stdin.js +69 -0
- package/dist/commands/stdin.js.map +1 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +107 -2
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +80 -5
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/context/doctrine.d.ts +16 -38
- package/dist/context/doctrine.d.ts.map +1 -1
- package/dist/context/doctrine.js +16 -75
- package/dist/context/doctrine.js.map +1 -1
- package/dist/context/environment.d.ts +2 -0
- package/dist/context/environment.d.ts.map +1 -1
- package/dist/context/environment.js +3 -0
- package/dist/context/environment.js.map +1 -1
- package/dist/integrations/mcp/servers.d.ts +19 -0
- package/dist/integrations/mcp/servers.d.ts.map +1 -1
- package/dist/integrations/mcp/servers.js +23 -2
- package/dist/integrations/mcp/servers.js.map +1 -1
- package/dist/integrations/plugins/runtime.d.ts +8 -2
- package/dist/integrations/plugins/runtime.d.ts.map +1 -1
- package/dist/integrations/plugins/runtime.js +24 -12
- package/dist/integrations/plugins/runtime.js.map +1 -1
- package/dist/integrations/providers/chain-capabilities.js +1 -1
- package/dist/integrations/providers/chain-capabilities.js.map +1 -1
- package/dist/integrations/providers/credential-store.d.ts +1 -1
- package/dist/integrations/providers/credential-store.js +1 -1
- package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
- package/dist/integrations/providers/harness-credentials.js +7 -0
- package/dist/integrations/providers/harness-credentials.js.map +1 -1
- package/dist/integrations/providers/registry.d.ts +2 -2
- package/dist/integrations/providers/registry.js +2 -2
- package/dist/integrations/sessions/store.d.ts +14 -22
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +78 -175
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/sessions/transcript-export.d.ts.map +1 -1
- package/dist/integrations/sessions/transcript-export.js +6 -2
- package/dist/integrations/sessions/transcript-export.js.map +1 -1
- package/dist/integrations/state/identity.d.ts +21 -0
- package/dist/integrations/state/identity.d.ts.map +1 -0
- package/dist/integrations/state/identity.js +49 -0
- package/dist/integrations/state/identity.js.map +1 -0
- package/dist/integrations/state/immutable-json.d.ts +10 -0
- package/dist/integrations/state/immutable-json.d.ts.map +1 -0
- package/dist/integrations/state/immutable-json.js +38 -0
- package/dist/integrations/state/immutable-json.js.map +1 -0
- package/dist/integrations/state/project.d.ts +10 -0
- package/dist/integrations/state/project.d.ts.map +1 -0
- package/dist/integrations/state/project.js +15 -0
- package/dist/integrations/state/project.js.map +1 -0
- package/dist/integrations/state/report.d.ts.map +1 -1
- package/dist/integrations/state/report.js +42 -12
- package/dist/integrations/state/report.js.map +1 -1
- package/dist/integrations/subagents/definitions.d.ts +19 -0
- package/dist/integrations/subagents/definitions.d.ts.map +1 -0
- package/dist/integrations/subagents/definitions.js +26 -0
- package/dist/integrations/subagents/definitions.js.map +1 -0
- package/dist/integrations/subagents/parent.d.ts +13 -0
- package/dist/integrations/subagents/parent.d.ts.map +1 -0
- package/dist/integrations/subagents/parent.js +57 -0
- package/dist/integrations/subagents/parent.js.map +1 -0
- package/dist/integrations/subagents/runtime.d.ts +24 -13
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +282 -122
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/memory/store.d.ts +35 -15
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +79 -25
- package/dist/memory/store.js.map +1 -1
- package/dist/permissions/mode.d.ts +12 -45
- package/dist/permissions/mode.d.ts.map +1 -1
- package/dist/permissions/mode.js +18 -13
- package/dist/permissions/mode.js.map +1 -1
- package/dist/release-notes.d.ts +20 -0
- package/dist/release-notes.d.ts.map +1 -0
- package/dist/release-notes.js +64 -0
- package/dist/release-notes.js.map +1 -0
- package/dist/tui/AgentExplorer.d.ts.map +1 -1
- package/dist/tui/AgentExplorer.js +14 -18
- package/dist/tui/AgentExplorer.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +606 -207
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/BrandHeader.d.ts +12 -0
- package/dist/tui/BrandHeader.d.ts.map +1 -0
- package/dist/tui/BrandHeader.js +17 -0
- package/dist/tui/BrandHeader.js.map +1 -0
- package/dist/tui/ChoicePicker.d.ts +11 -1
- package/dist/tui/ChoicePicker.d.ts.map +1 -1
- package/dist/tui/ChoicePicker.js +46 -13
- package/dist/tui/ChoicePicker.js.map +1 -1
- package/dist/tui/Composer.d.ts +4 -1
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/Composer.js +4 -4
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/ComposerFrame.d.ts +10 -0
- package/dist/tui/ComposerFrame.d.ts.map +1 -0
- package/dist/tui/ComposerFrame.js +93 -0
- package/dist/tui/ComposerFrame.js.map +1 -0
- package/dist/tui/EditPromptPicker.d.ts.map +1 -1
- package/dist/tui/EditPromptPicker.js +1 -1
- package/dist/tui/EditPromptPicker.js.map +1 -1
- package/dist/tui/LiveActivity.d.ts +12 -5
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/LiveActivity.js +17 -32
- package/dist/tui/LiveActivity.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +26 -2
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +108 -16
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.d.ts +2 -2
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +79 -52
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/StatusBar.d.ts.map +1 -1
- package/dist/tui/StatusBar.js +5 -2
- package/dist/tui/StatusBar.js.map +1 -1
- package/dist/tui/TaskList.d.ts +3 -1
- package/dist/tui/TaskList.d.ts.map +1 -1
- package/dist/tui/TaskList.js +13 -4
- package/dist/tui/TaskList.js.map +1 -1
- package/dist/tui/Transcript.d.ts +3 -3
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +16 -34
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +113 -50
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +512 -293
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/choice-selection.d.ts +17 -0
- package/dist/tui/choice-selection.d.ts.map +1 -0
- package/dist/tui/choice-selection.js +38 -0
- package/dist/tui/choice-selection.js.map +1 -0
- package/dist/tui/exit-summary.d.ts +7 -1
- package/dist/tui/exit-summary.d.ts.map +1 -1
- package/dist/tui/exit-summary.js +12 -3
- package/dist/tui/exit-summary.js.map +1 -1
- package/dist/tui/index.d.ts +3 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +5 -3
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/logo.d.ts +7 -22
- package/dist/tui/logo.d.ts.map +1 -1
- package/dist/tui/logo.js +8 -34
- package/dist/tui/logo.js.map +1 -1
- package/dist/tui/model-choices.d.ts.map +1 -1
- package/dist/tui/model-choices.js +21 -6
- package/dist/tui/model-choices.js.map +1 -1
- package/dist/tui/permission-review.d.ts +15 -4
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +48 -9
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/provider-selection.d.ts +4 -0
- package/dist/tui/provider-selection.d.ts.map +1 -0
- package/dist/tui/provider-selection.js +16 -0
- package/dist/tui/provider-selection.js.map +1 -0
- package/dist/tui/run-interruption.d.ts +3 -2
- package/dist/tui/run-interruption.d.ts.map +1 -1
- package/dist/tui/run-interruption.js +2 -1
- package/dist/tui/run-interruption.js.map +1 -1
- package/dist/tui/shell-escape.d.ts +42 -0
- package/dist/tui/shell-escape.d.ts.map +1 -0
- package/dist/tui/shell-escape.js +110 -0
- package/dist/tui/shell-escape.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +77 -31
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +343 -303
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/terminal-choice-text.d.ts +8 -0
- package/dist/tui/terminal-choice-text.d.ts.map +1 -0
- package/dist/tui/terminal-choice-text.js +37 -0
- package/dist/tui/terminal-choice-text.js.map +1 -0
- package/dist/tui/theme.d.ts +3 -4
- package/dist/tui/theme.d.ts.map +1 -1
- package/dist/tui/theme.js +16 -17
- package/dist/tui/theme.js.map +1 -1
- package/dist/tui/types.d.ts +6 -1
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +6 -4
|
@@ -11,25 +11,27 @@
|
|
|
11
11
|
* store backs the AgentManager, so sub-agent bookkeeping never touches the
|
|
12
12
|
* CLI's on-disk `/resume` conversation store.
|
|
13
13
|
*/
|
|
14
|
-
import { type AuthorizationGateConfig, type LLMProvider, type PathBuilder, type ProjectInstructionContext, type ResumeHandler, type RunEvent, type SandboxProvider, type TaskScheduler, type ToolContext, type ToolDefinition, type ToolRegistryContract } from '@namzu/sdk';
|
|
14
|
+
import { type AgentFileDefinition, type AuthorizationGateConfig, type LLMProvider, type PathBuilder, type Project, type ProjectInstructionContext, type ResumeHandler, type RunEvent, type RunId, type SandboxProvider, type SessionId, type TaskScheduler, type ToolContext, type ToolDefinition, type ToolRegistryContract, type Topic } from '@namzu/sdk';
|
|
15
15
|
import { type SubagentActivitySource } from './activity.js';
|
|
16
16
|
export declare const GENERAL_PURPOSE_SUBAGENT = "general-purpose";
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the delegations a parent makes most, and every one of them ran with
|
|
22
|
-
* `write`, `edit` and `bash` in the child's roster — so a lookup needed the
|
|
23
|
-
* same approvals as a change, and an operator in `accept-edits` mode was
|
|
24
|
-
* still asked about a child's shell command that only wanted to `cat`. This
|
|
25
|
-
* definition's registry is the parent's working set filtered to tools that
|
|
26
|
-
* declare themselves read-only, so a child that cannot change anything is
|
|
27
|
-
* never asked whether it may.
|
|
18
|
+
* The kernel's read-only delegate, with this application's doctrine under
|
|
19
|
+
* its prompt. Roster and prompt come from `@namzu/sdk`; what is added here is
|
|
20
|
+
* the same working rules every namzu child gets.
|
|
28
21
|
*/
|
|
29
22
|
export declare const EXPLORE_SUBAGENT = "explore";
|
|
23
|
+
export interface SubagentParent {
|
|
24
|
+
readonly project: Project;
|
|
25
|
+
readonly topic: Topic;
|
|
26
|
+
readonly sessionId: SessionId;
|
|
27
|
+
}
|
|
30
28
|
export interface SubagentRuntimeOptions {
|
|
29
|
+
/** Resolve the actual invoking run; reject calls whose parent no longer exists. */
|
|
30
|
+
readonly resolveParent: (runId: RunId) => Promise<SubagentParent>;
|
|
31
31
|
readonly cwd: string;
|
|
32
32
|
readonly model: string;
|
|
33
|
+
/** Aggregate parent-and-descendant limit; defaults to the CLI's one million tokens. */
|
|
34
|
+
readonly tokenBudget?: number;
|
|
33
35
|
/** Durable layout for child runs; omitted preserves the SDK default. */
|
|
34
36
|
readonly pathBuilder?: PathBuilder;
|
|
35
37
|
/** Root every child allocation at the session workspace or a fresh temp tree. */
|
|
@@ -62,9 +64,18 @@ export interface SubagentRuntimeOptions {
|
|
|
62
64
|
readonly readEnvironment?: () => Promise<string>;
|
|
63
65
|
/** Receives the child's RunEvents (lineage-stamped) — for the tree view. */
|
|
64
66
|
readonly onEvent?: (event: RunEvent) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Agents a project or user defined in `.namzu/agents/<name>.md`. Each
|
|
69
|
+
* becomes a type the `Agent` tool offers beside the built-in two, with
|
|
70
|
+
* its own prompt, roster and model. See `definitions.ts`.
|
|
71
|
+
*/
|
|
72
|
+
readonly definitions?: readonly AgentFileDefinition[];
|
|
65
73
|
}
|
|
66
74
|
export interface SubagentRuntime {
|
|
67
|
-
|
|
75
|
+
/** One immutable scheduler context per actual parent run. */
|
|
76
|
+
gatewayForRun(runId: RunId): Promise<TaskScheduler>;
|
|
77
|
+
/** Release a settled parent's bookkeeping and cancel children it still owns. */
|
|
78
|
+
releaseRun(runId: RunId): Promise<void>;
|
|
68
79
|
readonly agentTool: ToolDefinition;
|
|
69
80
|
readonly allowedAgentIds: readonly string[];
|
|
70
81
|
/** Live, bounded observation of children created by this CLI session. */
|
|
@@ -74,7 +85,7 @@ export interface SubagentRuntime {
|
|
|
74
85
|
}
|
|
75
86
|
/**
|
|
76
87
|
* Stand up the AgentManager + gateway + `Agent` tool. Returns the tool to
|
|
77
|
-
* register on the parent and the gateway to pass to `query({
|
|
88
|
+
* register on the parent and the gateway to pass to `query({ taskScheduler })`.
|
|
78
89
|
*/
|
|
79
90
|
export declare function createSubagentRuntime(opts: SubagentRuntimeOptions): Promise<SubagentRuntime>;
|
|
80
91
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/integrations/subagents/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/integrations/subagents/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,48 +11,28 @@
|
|
|
11
11
|
* store backs the AgentManager, so sub-agent bookkeeping never touches the
|
|
12
12
|
* CLI's on-disk `/resume` conversation store.
|
|
13
13
|
*/
|
|
14
|
-
import { AgentManager, AgentRegistry, DefaultCapacityValidator, InMemorySessionStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent, RunCancelled, SessionSummaryMaterializer,
|
|
14
|
+
import { AgentManager, AgentRegistry, DefaultCapacityValidator, EXPLORE_AGENT_DESCRIPTION, EXPLORE_AGENT_ID, EXPLORE_AGENT_PROMPT, InMemorySessionStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent, RunCancelled, SessionSummaryMaterializer, TopicArchivedError, TopicManager, WorkspaceBackendRegistry, defineTool, filterReadOnlyTools, filterToolsNamed, generateSummaryId, mcpJsonSchemaToZod, openTokenBudget, requireOpenProject, } from '@namzu/sdk';
|
|
15
15
|
import { NAMZU_WORKING_DOCTRINE } from '../../context/doctrine.js';
|
|
16
16
|
import { MAX_AGENT_ACTIVITY_LABEL_CODE_UNITS, MAX_AGENT_PHASE_ORDER, SubagentActivityMonitor, } from './activity.js';
|
|
17
17
|
import { CLI_INTERACTIVE_RUN_TIMEOUT_MS } from './policy.js';
|
|
18
18
|
export const GENERAL_PURPOSE_SUBAGENT = 'general-purpose';
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the delegations a parent makes most, and every one of them ran with
|
|
24
|
-
* `write`, `edit` and `bash` in the child's roster — so a lookup needed the
|
|
25
|
-
* same approvals as a change, and an operator in `accept-edits` mode was
|
|
26
|
-
* still asked about a child's shell command that only wanted to `cat`. This
|
|
27
|
-
* definition's registry is the parent's working set filtered to tools that
|
|
28
|
-
* declare themselves read-only, so a child that cannot change anything is
|
|
29
|
-
* never asked whether it may.
|
|
20
|
+
* The kernel's read-only delegate, with this application's doctrine under
|
|
21
|
+
* its prompt. Roster and prompt come from `@namzu/sdk`; what is added here is
|
|
22
|
+
* the same working rules every namzu child gets.
|
|
30
23
|
*/
|
|
31
|
-
export const EXPLORE_SUBAGENT =
|
|
32
|
-
const EXPLORE_PROMPT = [
|
|
33
|
-
'You are a read-only sub-agent dispatched by namzu to find things out: where something is defined, which files reference it, how a piece of code works, what a directory contains.',
|
|
34
|
-
'You cannot see the parent conversation — work only from the prompt you were given.',
|
|
35
|
-
'You have reading and searching tools only. You cannot edit, write or run commands, and you must not describe a change as made.',
|
|
36
|
-
'Search broadly first, then read what matters. Report file paths with line numbers, quote the relevant lines, and say plainly what you did not find.',
|
|
37
|
-
'End with a concise answer the parent can act on; do not ask the parent questions.',
|
|
38
|
-
'',
|
|
39
|
-
NAMZU_WORKING_DOCTRINE,
|
|
40
|
-
].join('\n');
|
|
24
|
+
export const EXPLORE_SUBAGENT = EXPLORE_AGENT_ID;
|
|
25
|
+
const EXPLORE_PROMPT = [EXPLORE_AGENT_PROMPT, '', NAMZU_WORKING_DOCTRINE].join('\n');
|
|
41
26
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* `defineTool` records — rather than by a name list here, so a new read-only
|
|
46
|
-
* builtin joins the explore roster without this file learning its name and
|
|
47
|
-
* a tool that stops being read-only leaves it the same way.
|
|
27
|
+
* A file-defined agent's roster: the parent's working set, kept to the
|
|
28
|
+
* file's `tools` allowlist when there is one, and to read-only tools when
|
|
29
|
+
* the file says `readOnly: true`. Intersection, never union.
|
|
48
30
|
*/
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
return filtered;
|
|
31
|
+
function fileAgentTools(definition, opts) {
|
|
32
|
+
return () => {
|
|
33
|
+
const source = definition.readOnly ? filterReadOnlyTools(opts.buildTools()) : opts.buildTools();
|
|
34
|
+
return definition.tools ? filterToolsNamed(source, definition.tools) : source;
|
|
35
|
+
};
|
|
56
36
|
}
|
|
57
37
|
const SUBAGENT_PROMPT = [
|
|
58
38
|
'You are a focused sub-agent dispatched by namzu to complete one self-contained task and report back.',
|
|
@@ -70,6 +50,39 @@ const SUBAGENT_PROMPT = [
|
|
|
70
50
|
// the parent does not is the same defect in the other direction.
|
|
71
51
|
NAMZU_WORKING_DOCTRINE,
|
|
72
52
|
].join('\n');
|
|
53
|
+
/** Recheck the invoking run before admitting work through a retained gateway. */
|
|
54
|
+
class ParentTaskScheduler extends LocalTaskScheduler {
|
|
55
|
+
validateParent;
|
|
56
|
+
constructor(manager, context, onEvent, validateParent) {
|
|
57
|
+
super(manager, context, onEvent);
|
|
58
|
+
this.validateParent = validateParent;
|
|
59
|
+
}
|
|
60
|
+
async createTask(options) {
|
|
61
|
+
await this.validateParent();
|
|
62
|
+
return super.createTask(options);
|
|
63
|
+
}
|
|
64
|
+
owns(taskId) {
|
|
65
|
+
return super.listTasks().some((task) => task.taskId === taskId);
|
|
66
|
+
}
|
|
67
|
+
getTask(taskId) {
|
|
68
|
+
return this.owns(taskId) ? super.getTask(taskId) : undefined;
|
|
69
|
+
}
|
|
70
|
+
cancelTask(taskId, cause) {
|
|
71
|
+
if (this.owns(taskId))
|
|
72
|
+
super.cancelTask(taskId, cause);
|
|
73
|
+
}
|
|
74
|
+
async waitForTask(taskId) {
|
|
75
|
+
if (!this.owns(taskId))
|
|
76
|
+
throw new Error(`Task ${taskId} does not belong to this parent run`);
|
|
77
|
+
return super.waitForTask(taskId);
|
|
78
|
+
}
|
|
79
|
+
async continueTask(taskId, message) {
|
|
80
|
+
if (!this.owns(taskId))
|
|
81
|
+
throw new Error(`Task ${taskId} does not belong to this parent run`);
|
|
82
|
+
await this.validateParent();
|
|
83
|
+
await super.continueTask(taskId, message);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
73
86
|
/** A delegated run may never inherit the SDK's headless auto-approval fallback. */
|
|
74
87
|
const refuseUnownedChildReview = async () => ({
|
|
75
88
|
action: 'abort',
|
|
@@ -77,63 +90,204 @@ const refuseUnownedChildReview = async () => ({
|
|
|
77
90
|
});
|
|
78
91
|
/**
|
|
79
92
|
* Stand up the AgentManager + gateway + `Agent` tool. Returns the tool to
|
|
80
|
-
* register on the parent and the gateway to pass to `query({
|
|
93
|
+
* register on the parent and the gateway to pass to `query({ taskScheduler })`.
|
|
81
94
|
*/
|
|
82
95
|
export async function createSubagentRuntime(opts) {
|
|
83
|
-
const tenantId = asTenantId('tnt_namzu-cli');
|
|
84
|
-
const store = new InMemorySessionStore();
|
|
85
|
-
const topicStore = new InMemoryTopicStore();
|
|
86
|
-
const userActor = {
|
|
87
|
-
kind: 'user',
|
|
88
|
-
userId: asUserId('usr_namzu'),
|
|
89
|
-
tenantId,
|
|
90
|
-
};
|
|
91
|
-
const project = await store.createProject({ tenantId, name: 'namzu-cli' }, tenantId);
|
|
92
|
-
const thread = await topicStore.createTopic({ projectId: project.id, title: 'namzu-cli' }, tenantId);
|
|
93
|
-
// No workspace gate here, and that is a finding rather than an omission.
|
|
94
|
-
//
|
|
95
|
-
// A direct `createSession` bypasses `requireOpenProject`, which is why the
|
|
96
|
-
// CLI's persistent conversation store now calls it explicitly. This site is
|
|
97
|
-
// the other shape: the store is a fresh `InMemorySessionStore` built four
|
|
98
|
-
// lines up, the project was created two lines up, and neither outlives this
|
|
99
|
-
// runtime — so the id can never be one an owner has closed. A gate on a path
|
|
100
|
-
// that always creates its own project checks a condition that cannot be
|
|
101
|
-
// false, and a check that cannot fail teaches the next reader nothing except
|
|
102
|
-
// that gates here are decoration.
|
|
103
|
-
//
|
|
104
|
-
// The trigger to add one is the day this store is replaced by a persistent
|
|
105
|
-
// one, or the project id starts arriving from a caller.
|
|
106
|
-
const parentSession = await store.createSession({ topicId: thread.id, projectId: project.id, currentActor: userActor }, tenantId);
|
|
107
|
-
await store.updateSession({ ...parentSession, status: 'active' }, tenantId);
|
|
108
|
-
let summaryCounter = 0;
|
|
109
|
-
const materializer = new SessionSummaryMaterializer({
|
|
110
|
-
store,
|
|
111
|
-
generateSummaryId: () => asSummaryId(`sum_namzu_${++summaryCounter}`),
|
|
112
|
-
});
|
|
113
96
|
const registry = new AgentRegistry();
|
|
114
97
|
registry.register(buildDefinition(GENERAL_PURPOSE_SUBAGENT, 'A general-purpose sub-agent.', SUBAGENT_PROMPT, opts));
|
|
115
|
-
registry.register(buildDefinition(EXPLORE_SUBAGENT,
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
98
|
+
registry.register(buildDefinition(EXPLORE_SUBAGENT, EXPLORE_AGENT_DESCRIPTION, EXPLORE_PROMPT, opts, () => filterReadOnlyTools(opts.buildTools())));
|
|
99
|
+
// Agents a project or user defined in a file, each a type of its own.
|
|
100
|
+
// The roster is the file's allowlist intersected with the parent's set —
|
|
101
|
+
// a file cannot grant a tool the parent does not have — narrowed further
|
|
102
|
+
// to read-only when the file says so. The prompt is the file's body over
|
|
103
|
+
// the same sub-agent base every child gets.
|
|
104
|
+
const fileAgents = new Map();
|
|
105
|
+
for (const definition of opts.definitions ?? []) {
|
|
106
|
+
fileAgents.set(definition.name, definition);
|
|
107
|
+
registry.register(buildDefinition(definition.name, definition.description, definition.prompt, opts, fileAgentTools(definition, opts), definition.model ?? opts.model));
|
|
108
|
+
}
|
|
109
|
+
const agentTypeIds = [GENERAL_PURPOSE_SUBAGENT, EXPLORE_SUBAGENT, ...fileAgents.keys()];
|
|
110
|
+
const fileAgentSummary = [...fileAgents.values()]
|
|
111
|
+
.map((definition) => `"${definition.name}" — ${definition.description}`)
|
|
112
|
+
.join('; ');
|
|
113
|
+
const parents = new Map();
|
|
114
|
+
const sessions = new Map();
|
|
115
|
+
let closed = false;
|
|
116
|
+
const sessionKey = ({ project, sessionId }) => JSON.stringify([project.tenantId, project.id, sessionId]);
|
|
117
|
+
const resolveParent = async (runId) => {
|
|
118
|
+
const parent = structuredClone(await opts.resolveParent(runId));
|
|
119
|
+
const { project, topic } = parent;
|
|
120
|
+
if (topic.projectId !== project.id || topic.tenantId !== project.tenantId) {
|
|
121
|
+
throw new Error('Delegation parent topic does not belong to its project and tenant');
|
|
122
|
+
}
|
|
123
|
+
await requireOpenProject({ getProject: async () => project }, project.id, project.tenantId, 'spawn');
|
|
124
|
+
if (topic.status === 'archived')
|
|
125
|
+
throw new TopicArchivedError({ topicId: topic.id, op: 'spawn' });
|
|
126
|
+
return parent;
|
|
127
|
+
};
|
|
128
|
+
const createSessionRuntime = async (parent) => {
|
|
129
|
+
const { project, topic, sessionId } = parent;
|
|
130
|
+
const tenantId = project.tenantId;
|
|
131
|
+
const store = new InMemorySessionStore([project]);
|
|
132
|
+
const topicStore = new InMemoryTopicStore([topic]);
|
|
133
|
+
const parentActor = { kind: 'agent', agentId: 'namzu', tenantId };
|
|
134
|
+
const session = await store.createSession({
|
|
135
|
+
id: sessionId,
|
|
136
|
+
topicId: topic.id,
|
|
137
|
+
projectId: project.id,
|
|
138
|
+
currentActor: parentActor,
|
|
139
|
+
}, tenantId);
|
|
140
|
+
await store.updateSession({ ...session, status: 'active' }, tenantId);
|
|
141
|
+
const manager = new AgentManager(registry, { childTimeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS }, {
|
|
142
|
+
sessionStore: store,
|
|
143
|
+
summaryMaterializer: new SessionSummaryMaterializer({
|
|
144
|
+
store,
|
|
145
|
+
generateSummaryId,
|
|
146
|
+
}),
|
|
147
|
+
workspaceRegistry: new WorkspaceBackendRegistry(),
|
|
148
|
+
capacity: new DefaultCapacityValidator(store),
|
|
149
|
+
topicManager: new TopicManager({ topicStore, sessionStore: store }),
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
manager,
|
|
153
|
+
store,
|
|
154
|
+
topicId: topic.id,
|
|
155
|
+
projectUpdatedAt: project.updatedAt.getTime(),
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
const refreshLimits = async (shared, parent) => {
|
|
159
|
+
if (shared.topicId !== parent.topic.id)
|
|
160
|
+
throw new Error('Delegation parent changed its topic while runs are active');
|
|
161
|
+
// A slow metadata read must not overwrite a newer project snapshot.
|
|
162
|
+
const updatedAt = parent.project.updatedAt.getTime();
|
|
163
|
+
if (updatedAt < shared.projectUpdatedAt)
|
|
164
|
+
return;
|
|
165
|
+
shared.projectUpdatedAt = updatedAt;
|
|
166
|
+
await shared.store.updateProject(parent.project.id, parent.project.config, parent.project.tenantId);
|
|
167
|
+
};
|
|
168
|
+
const acquireSession = async (parent) => {
|
|
169
|
+
const key = sessionKey(parent);
|
|
170
|
+
let entry = sessions.get(key);
|
|
171
|
+
if (!entry) {
|
|
172
|
+
entry = { ready: createSessionRuntime(parent), owners: 0 };
|
|
173
|
+
sessions.set(key, entry);
|
|
174
|
+
}
|
|
175
|
+
entry.owners++;
|
|
176
|
+
const ownedEntry = entry;
|
|
177
|
+
let released = false;
|
|
178
|
+
const release = () => {
|
|
179
|
+
if (released)
|
|
180
|
+
return;
|
|
181
|
+
released = true;
|
|
182
|
+
ownedEntry.owners--;
|
|
183
|
+
if (ownedEntry.owners !== 0)
|
|
184
|
+
return;
|
|
185
|
+
if (sessions.get(key) === ownedEntry)
|
|
186
|
+
sessions.delete(key);
|
|
187
|
+
void ownedEntry.ready.then((shared) => shared.manager.dispose(), () => undefined);
|
|
188
|
+
};
|
|
189
|
+
try {
|
|
190
|
+
const shared = await entry.ready;
|
|
191
|
+
await refreshLimits(shared, parent);
|
|
192
|
+
return { shared, release };
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
release();
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const gatewayForRun = async (runId) => {
|
|
200
|
+
if (closed)
|
|
201
|
+
throw new Error('Sub-agent runtime is closed');
|
|
202
|
+
let pending = parents.get(runId);
|
|
203
|
+
if (!pending) {
|
|
204
|
+
pending = (async () => {
|
|
205
|
+
const parent = await resolveParent(runId);
|
|
206
|
+
const budget = await openTokenBudget({
|
|
207
|
+
scope: {
|
|
208
|
+
tenantId: parent.project.tenantId,
|
|
209
|
+
projectId: parent.project.id,
|
|
210
|
+
sessionId: parent.sessionId,
|
|
211
|
+
runId,
|
|
212
|
+
},
|
|
213
|
+
limit: opts.tokenBudget ?? 1_000_000,
|
|
214
|
+
pathBuilder: opts.pathBuilder,
|
|
215
|
+
workingDirectory: opts.cwd,
|
|
216
|
+
});
|
|
217
|
+
const lease = await acquireSession(parent);
|
|
218
|
+
const { manager } = lease.shared;
|
|
219
|
+
const parentAbortController = new AbortController();
|
|
220
|
+
let released = false;
|
|
221
|
+
const assertOwned = () => {
|
|
222
|
+
if (closed ||
|
|
223
|
+
released ||
|
|
224
|
+
parentAbortController.signal.aborted ||
|
|
225
|
+
parents.get(runId) !== pending) {
|
|
226
|
+
throw new Error(`Parent run ${runId} was released`);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const taskContext = {
|
|
230
|
+
parentRunId: runId,
|
|
231
|
+
parentAgentId: 'namzu',
|
|
232
|
+
parentAbortController,
|
|
233
|
+
depth: 0,
|
|
234
|
+
budget,
|
|
235
|
+
tenantId: parent.project.tenantId,
|
|
236
|
+
topicId: parent.topic.id,
|
|
237
|
+
sessionId: parent.sessionId,
|
|
238
|
+
projectId: parent.project.id,
|
|
239
|
+
parentActor: {
|
|
240
|
+
kind: 'agent',
|
|
241
|
+
agentId: 'namzu',
|
|
242
|
+
tenantId: parent.project.tenantId,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
const runtime = {
|
|
246
|
+
gateway: new ParentTaskScheduler(manager, taskContext, opts.onEvent, async () => {
|
|
247
|
+
assertOwned();
|
|
248
|
+
const current = await resolveParent(runId);
|
|
249
|
+
assertOwned();
|
|
250
|
+
if (sessionKey(current) !== sessionKey(parent))
|
|
251
|
+
throw new Error('Delegation run changed its parent scope');
|
|
252
|
+
await refreshLimits(lease.shared, current);
|
|
253
|
+
assertOwned();
|
|
254
|
+
}),
|
|
255
|
+
close() {
|
|
256
|
+
if (released)
|
|
257
|
+
return;
|
|
258
|
+
released = true;
|
|
259
|
+
parentAbortController.abort(new RunCancelled('parent'));
|
|
260
|
+
manager.cancelAll(runId, 'parent');
|
|
261
|
+
lease.release();
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
if (closed) {
|
|
265
|
+
runtime.close();
|
|
266
|
+
throw new Error('Sub-agent runtime is closed');
|
|
267
|
+
}
|
|
268
|
+
return runtime;
|
|
269
|
+
})();
|
|
270
|
+
parents.set(runId, pending);
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
const runtime = await pending;
|
|
274
|
+
if (closed || parents.get(runId) !== pending)
|
|
275
|
+
throw new Error(`Parent run ${runId} was released`);
|
|
276
|
+
return runtime.gateway;
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
if (parents.get(runId) === pending)
|
|
280
|
+
parents.delete(runId);
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
const releaseRun = async (runId) => {
|
|
285
|
+
const pending = parents.get(runId);
|
|
286
|
+
if (!pending)
|
|
287
|
+
return;
|
|
288
|
+
parents.delete(runId);
|
|
289
|
+
await pending.then((runtime) => runtime.close(), () => undefined);
|
|
135
290
|
};
|
|
136
|
-
const gateway = new LocalTaskScheduler(manager, taskContext, opts.onEvent);
|
|
137
291
|
const activity = new SubagentActivityMonitor();
|
|
138
292
|
// Dynamic `Agent` tool: the model passes an optional `role` (the persona /
|
|
139
293
|
// system prompt) and we register + spawn a fresh specialist for it at call
|
|
@@ -141,19 +295,21 @@ export async function createSubagentRuntime(opts) {
|
|
|
141
295
|
let dynCounter = 0;
|
|
142
296
|
const agentTool = defineTool({
|
|
143
297
|
name: 'Agent',
|
|
144
|
-
description:
|
|
145
|
-
'
|
|
146
|
-
'files reference Y, how does Z work — it has reading and searching tools only and never asks for '
|
|
147
|
-
'
|
|
148
|
-
'Define a specialist inline with `role` — a system prompt describing who the sub-agent '
|
|
149
|
-
'
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
|
|
156
|
-
|
|
298
|
+
description: [
|
|
299
|
+
'Delegate a self-contained task to a sub-agent and get its result back (BLOCKING).',
|
|
300
|
+
'Pick `subagent_type: "explore"` for anything that only needs to look — where is X defined, which files reference Y, how does Z work — it has reading and searching tools only and never asks for permission.',
|
|
301
|
+
'Use the default "general-purpose" when the task must change files or run commands.',
|
|
302
|
+
'Define a specialist inline with `role` — a system prompt describing who the sub-agent is and how to behave (e.g.',
|
|
303
|
+
'"You are a security auditor; flag vulnerabilities and rate severity"); with `subagent_type: "explore"` the role keeps the read-only roster.',
|
|
304
|
+
'Omit `role` for the plain sub-agent.',
|
|
305
|
+
'The sub-agent runs in its own context with its own tools and cannot see this conversation — put everything it needs in `prompt`.',
|
|
306
|
+
'Call this multiple times in one response to run specialists in parallel.',
|
|
307
|
+
'When coordinating several specialists, give them the same `workflow` label and an explicit `phase` plus `phase_order` so the operator can follow the work in the agent cockpit.',
|
|
308
|
+
'These fields are display annotations only; they do not create dependencies, barriers, or serial execution.',
|
|
309
|
+
fileAgentSummary.length > 0 ? `Project-defined types: ${fileAgentSummary}.` : '',
|
|
310
|
+
]
|
|
311
|
+
.filter((part) => part.length > 0)
|
|
312
|
+
.join(' '),
|
|
157
313
|
inputSchema: mcpJsonSchemaToZod({
|
|
158
314
|
type: 'object',
|
|
159
315
|
properties: {
|
|
@@ -167,8 +323,10 @@ export async function createSubagentRuntime(opts) {
|
|
|
167
323
|
},
|
|
168
324
|
subagent_type: {
|
|
169
325
|
type: 'string',
|
|
170
|
-
enum:
|
|
171
|
-
description:
|
|
326
|
+
enum: agentTypeIds,
|
|
327
|
+
description: `"explore" for read-only lookups (find, search, explain); "general-purpose" (default) when the task changes files or runs commands.${fileAgentSummary.length > 0
|
|
328
|
+
? ` This project also defines: ${fileAgentSummary}. Prefer a project-defined type when its description fits the task.`
|
|
329
|
+
: ''}`,
|
|
172
330
|
},
|
|
173
331
|
role: {
|
|
174
332
|
type: 'string',
|
|
@@ -202,15 +360,22 @@ export async function createSubagentRuntime(opts) {
|
|
|
202
360
|
async execute(input, context) {
|
|
203
361
|
const { description, prompt, subagent_type, role, workflow, phase, phase_order } = input;
|
|
204
362
|
const explore = subagent_type === EXPLORE_SUBAGENT;
|
|
205
|
-
|
|
363
|
+
const fileAgent = subagent_type !== undefined ? fileAgents.get(subagent_type) : undefined;
|
|
364
|
+
let agentId = fileAgent?.name ?? (explore ? EXPLORE_SUBAGENT : GENERAL_PURPOSE_SUBAGENT);
|
|
206
365
|
const persona = typeof role === 'string' ? role.trim() : '';
|
|
207
366
|
const dynamic = persona.length > 0;
|
|
208
367
|
if (dynamic) {
|
|
209
|
-
// A role on top of
|
|
210
|
-
// who the child is, the type says what it may touch,
|
|
211
|
-
// must not be a way to hand a read-only child a `write`
|
|
368
|
+
// A role on top of a type keeps that type's roster and model: the
|
|
369
|
+
// persona says who the child is, the type says what it may touch,
|
|
370
|
+
// and a role must not be a way to hand a read-only child a `write`
|
|
371
|
+
// tool. Over a file-defined agent the role is appended to the
|
|
372
|
+
// file's prompt rather than replacing it.
|
|
212
373
|
agentId = `dyn-${++dynCounter}`;
|
|
213
|
-
registry.register(buildDefinition(agentId, `Dynamic specialist: ${agentId}`,
|
|
374
|
+
registry.register(buildDefinition(agentId, `Dynamic specialist: ${agentId}`, fileAgent ? `${fileAgent.prompt}\n\n${persona}` : persona, opts, fileAgent
|
|
375
|
+
? fileAgentTools(fileAgent, opts)
|
|
376
|
+
: explore
|
|
377
|
+
? () => filterReadOnlyTools(opts.buildTools())
|
|
378
|
+
: opts.buildTools, fileAgent?.model ?? opts.model));
|
|
214
379
|
}
|
|
215
380
|
const tracker = activity.begin({
|
|
216
381
|
agentId,
|
|
@@ -236,7 +401,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
236
401
|
};
|
|
237
402
|
try {
|
|
238
403
|
const completed = await runBlockingAgentTask({
|
|
239
|
-
gateway,
|
|
404
|
+
gateway: await gatewayForRun(context.runId),
|
|
240
405
|
signal: context.abortSignal,
|
|
241
406
|
create: {
|
|
242
407
|
agentId,
|
|
@@ -289,22 +454,15 @@ export async function createSubagentRuntime(opts) {
|
|
|
289
454
|
const close = () => {
|
|
290
455
|
if (closePromise)
|
|
291
456
|
return closePromise;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
// Abort it before disposing bookkeeping so a live child keeps the
|
|
295
|
-
// structured reason even if its blocking tool wait already detached.
|
|
296
|
-
if (!taskContext.parentAbortController.signal.aborted) {
|
|
297
|
-
taskContext.parentAbortController.abort(new RunCancelled('parent'));
|
|
298
|
-
}
|
|
299
|
-
manager.dispose();
|
|
300
|
-
activity.close();
|
|
301
|
-
});
|
|
457
|
+
closed = true;
|
|
458
|
+
closePromise = Promise.all([...parents.keys()].map(releaseRun)).then(() => activity.close());
|
|
302
459
|
return closePromise;
|
|
303
460
|
};
|
|
304
461
|
return {
|
|
305
|
-
|
|
462
|
+
gatewayForRun,
|
|
463
|
+
releaseRun,
|
|
306
464
|
agentTool,
|
|
307
|
-
allowedAgentIds:
|
|
465
|
+
allowedAgentIds: agentTypeIds,
|
|
308
466
|
activity,
|
|
309
467
|
close,
|
|
310
468
|
};
|
|
@@ -371,7 +529,9 @@ async function runBlockingAgentTask(input) {
|
|
|
371
529
|
*/
|
|
372
530
|
function buildDefinition(id, description, systemPrompt, opts,
|
|
373
531
|
/** This definition's own roster; absent means the parent's working set. */
|
|
374
|
-
tools = opts.buildTools
|
|
532
|
+
tools = opts.buildTools,
|
|
533
|
+
/** This definition's model; absent means the session's. */
|
|
534
|
+
model = opts.model) {
|
|
375
535
|
const agent = new ReactiveAgent({
|
|
376
536
|
id,
|
|
377
537
|
name: id,
|
|
@@ -391,7 +551,7 @@ tools = opts.buildTools) {
|
|
|
391
551
|
category: 'general',
|
|
392
552
|
description,
|
|
393
553
|
tools: [],
|
|
394
|
-
defaults: { model
|
|
554
|
+
defaults: { model, tokenBudget: 200_000 },
|
|
395
555
|
},
|
|
396
556
|
// ReactiveAgent is Agent<ReactiveAgentConfig,…>; the registry stores the
|
|
397
557
|
// erased Agent<BaseAgentConfig,…>. configBuilder supplies the richer config.
|
|
@@ -403,7 +563,7 @@ tools = opts.buildTools) {
|
|
|
403
563
|
// is worse than one that was never told.
|
|
404
564
|
const environment = opts.readEnvironment ? await opts.readEnvironment() : null;
|
|
405
565
|
return {
|
|
406
|
-
model: options.model ??
|
|
566
|
+
model: options.model ?? model,
|
|
407
567
|
tokenBudget: options.tokenBudget ?? 200_000,
|
|
408
568
|
timeoutMs: options.timeoutMs ?? CLI_INTERACTIVE_RUN_TIMEOUT_MS,
|
|
409
569
|
maxIterations: 40,
|