@namzu/cli 20.0.0 → 22.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 +371 -0
- package/README.md +71 -10
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +6 -1
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +9 -1
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +15 -12
- package/dist/commands/run.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +44 -2
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +24 -13
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/doctor/checks/chain.d.ts.map +1 -1
- package/dist/doctor/checks/chain.js +11 -5
- package/dist/doctor/checks/chain.js.map +1 -1
- package/dist/doctor/checks/credentials.d.ts.map +1 -1
- package/dist/doctor/checks/credentials.js +7 -1
- package/dist/doctor/checks/credentials.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/installation.d.ts +5 -0
- package/dist/installation.d.ts.map +1 -0
- package/dist/installation.js +48 -0
- package/dist/installation.js.map +1 -0
- package/dist/integrations/providers/access.d.ts +7 -0
- package/dist/integrations/providers/access.d.ts.map +1 -0
- package/dist/integrations/providers/access.js +17 -0
- package/dist/integrations/providers/access.js.map +1 -0
- package/dist/integrations/providers/current-credential.d.ts +4 -0
- package/dist/integrations/providers/current-credential.d.ts.map +1 -0
- package/dist/integrations/providers/current-credential.js +45 -0
- package/dist/integrations/providers/current-credential.js.map +1 -0
- package/dist/integrations/providers/discover.d.ts +28 -54
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/discover.js +62 -58
- package/dist/integrations/providers/discover.js.map +1 -1
- package/dist/integrations/providers/gemini-credentials.d.ts +13 -0
- package/dist/integrations/providers/gemini-credentials.d.ts.map +1 -0
- package/dist/integrations/providers/gemini-credentials.js +148 -0
- package/dist/integrations/providers/gemini-credentials.js.map +1 -0
- package/dist/integrations/providers/harness-credentials.d.ts +14 -0
- package/dist/integrations/providers/harness-credentials.d.ts.map +1 -1
- package/dist/integrations/providers/harness-credentials.js +46 -0
- package/dist/integrations/providers/harness-credentials.js.map +1 -1
- package/dist/integrations/providers/model-reasoning.d.ts +4 -0
- package/dist/integrations/providers/model-reasoning.d.ts.map +1 -0
- package/dist/integrations/providers/model-reasoning.js +46 -0
- package/dist/integrations/providers/model-reasoning.js.map +1 -0
- package/dist/integrations/providers/register.d.ts +1 -1
- package/dist/integrations/providers/register.d.ts.map +1 -1
- package/dist/integrations/providers/register.js +16 -1
- package/dist/integrations/providers/register.js.map +1 -1
- package/dist/integrations/providers/registry.d.ts +4 -4
- package/dist/integrations/providers/registry.d.ts.map +1 -1
- package/dist/integrations/providers/registry.js +33 -0
- package/dist/integrations/providers/registry.js.map +1 -1
- package/dist/integrations/providers/setup.d.ts +49 -0
- package/dist/integrations/providers/setup.d.ts.map +1 -0
- package/dist/integrations/providers/setup.js +105 -0
- package/dist/integrations/providers/setup.js.map +1 -0
- package/dist/integrations/sessions/context-inventory.d.ts +4 -0
- package/dist/integrations/sessions/context-inventory.d.ts.map +1 -0
- package/dist/integrations/sessions/context-inventory.js +44 -0
- package/dist/integrations/sessions/context-inventory.js.map +1 -0
- package/dist/integrations/sessions/conversation-search.d.ts +60 -0
- package/dist/integrations/sessions/conversation-search.d.ts.map +1 -0
- package/dist/integrations/sessions/conversation-search.js +461 -0
- package/dist/integrations/sessions/conversation-search.js.map +1 -0
- package/dist/integrations/sessions/store.d.ts +1 -0
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +17 -4
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/sessions/task-context.d.ts +4 -0
- package/dist/integrations/sessions/task-context.d.ts.map +1 -0
- package/dist/integrations/sessions/task-context.js +73 -0
- package/dist/integrations/sessions/task-context.js.map +1 -0
- package/dist/integrations/subagents/activity.d.ts +3 -1
- package/dist/integrations/subagents/activity.d.ts.map +1 -1
- package/dist/integrations/subagents/activity.js +32 -5
- package/dist/integrations/subagents/activity.js.map +1 -1
- package/dist/integrations/subagents/history.d.ts +26 -0
- package/dist/integrations/subagents/history.d.ts.map +1 -0
- package/dist/integrations/subagents/history.js +76 -0
- package/dist/integrations/subagents/history.js.map +1 -0
- package/dist/integrations/subagents/model-effort.d.ts +4 -0
- package/dist/integrations/subagents/model-effort.d.ts.map +1 -0
- package/dist/integrations/subagents/model-effort.js +12 -0
- package/dist/integrations/subagents/model-effort.js.map +1 -0
- package/dist/integrations/subagents/runtime.d.ts +27 -7
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +305 -35
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/integrations/web/search.d.ts +11 -0
- package/dist/integrations/web/search.d.ts.map +1 -0
- package/dist/integrations/web/search.js +73 -0
- package/dist/integrations/web/search.js.map +1 -0
- package/dist/memory/io.d.ts +12 -0
- package/dist/memory/io.d.ts.map +1 -0
- package/dist/memory/io.js +177 -0
- package/dist/memory/io.js.map +1 -0
- package/dist/memory/presentation.d.ts +4 -1
- package/dist/memory/presentation.d.ts.map +1 -1
- package/dist/memory/presentation.js +16 -2
- package/dist/memory/presentation.js.map +1 -1
- package/dist/memory/store.d.ts +17 -3
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +61 -35
- package/dist/memory/store.js.map +1 -1
- package/dist/output-schema.d.ts +4 -0
- package/dist/output-schema.d.ts.map +1 -0
- package/dist/output-schema.js +18 -0
- package/dist/output-schema.js.map +1 -0
- package/dist/tui/AgentExplorer.d.ts +13 -2
- package/dist/tui/AgentExplorer.d.ts.map +1 -1
- package/dist/tui/AgentExplorer.js +85 -23
- package/dist/tui/AgentExplorer.js.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +694 -150
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/BrandHeader.d.ts +1 -4
- package/dist/tui/BrandHeader.d.ts.map +1 -1
- package/dist/tui/BrandHeader.js +4 -8
- package/dist/tui/BrandHeader.js.map +1 -1
- package/dist/tui/Composer.d.ts +2 -1
- package/dist/tui/Composer.d.ts.map +1 -1
- package/dist/tui/Composer.js +26 -3
- package/dist/tui/Composer.js.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +5 -2
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +14 -6
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.d.ts +2 -1
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +104 -24
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/ProviderSetup.d.ts +6 -0
- package/dist/tui/ProviderSetup.d.ts.map +1 -0
- package/dist/tui/ProviderSetup.js +125 -0
- package/dist/tui/ProviderSetup.js.map +1 -0
- package/dist/tui/ResumePicker.d.ts.map +1 -1
- package/dist/tui/ResumePicker.js +5 -3
- package/dist/tui/ResumePicker.js.map +1 -1
- package/dist/tui/StatusPanel.d.ts +5 -0
- package/dist/tui/StatusPanel.d.ts.map +1 -0
- package/dist/tui/StatusPanel.js +11 -0
- package/dist/tui/StatusPanel.js.map +1 -0
- package/dist/tui/TextPrompt.d.ts +3 -1
- package/dist/tui/TextPrompt.d.ts.map +1 -1
- package/dist/tui/TextPrompt.js +55 -9
- package/dist/tui/TextPrompt.js.map +1 -1
- package/dist/tui/ToolOutputViewer.d.ts +11 -0
- package/dist/tui/ToolOutputViewer.d.ts.map +1 -0
- package/dist/tui/ToolOutputViewer.js +39 -0
- package/dist/tui/ToolOutputViewer.js.map +1 -0
- package/dist/tui/Transcript.d.ts.map +1 -1
- package/dist/tui/Transcript.js +10 -4
- package/dist/tui/Transcript.js.map +1 -1
- package/dist/tui/agent.d.ts +24 -3
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +362 -97
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/live-window.d.ts.map +1 -1
- package/dist/tui/live-window.js +5 -1
- package/dist/tui/live-window.js.map +1 -1
- package/dist/tui/logo.d.ts +4 -4
- package/dist/tui/logo.d.ts.map +1 -1
- package/dist/tui/logo.js +4 -4
- package/dist/tui/logo.js.map +1 -1
- package/dist/tui/model-catalogue-view.d.ts +3 -0
- package/dist/tui/model-catalogue-view.d.ts.map +1 -0
- package/dist/tui/model-catalogue-view.js +50 -0
- package/dist/tui/model-catalogue-view.js.map +1 -0
- package/dist/tui/model-choices.d.ts +3 -1
- package/dist/tui/model-choices.d.ts.map +1 -1
- package/dist/tui/model-choices.js +12 -1
- package/dist/tui/model-choices.js.map +1 -1
- package/dist/tui/model-search.d.ts +4 -0
- package/dist/tui/model-search.d.ts.map +1 -0
- package/dist/tui/model-search.js +11 -0
- package/dist/tui/model-search.js.map +1 -0
- package/dist/tui/model-selection-intent.d.ts +8 -0
- package/dist/tui/model-selection-intent.d.ts.map +1 -0
- package/dist/tui/model-selection-intent.js +96 -0
- package/dist/tui/model-selection-intent.js.map +1 -0
- package/dist/tui/model-switch-tool.d.ts +7 -0
- package/dist/tui/model-switch-tool.d.ts.map +1 -0
- package/dist/tui/model-switch-tool.js +74 -0
- package/dist/tui/model-switch-tool.js.map +1 -0
- package/dist/tui/model-switch.d.ts +35 -0
- package/dist/tui/model-switch.d.ts.map +1 -0
- package/dist/tui/model-switch.js +106 -0
- package/dist/tui/model-switch.js.map +1 -0
- package/dist/tui/permission-review.d.ts +2 -0
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +82 -15
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/run-interruption.d.ts +7 -0
- package/dist/tui/run-interruption.d.ts.map +1 -1
- package/dist/tui/run-interruption.js +41 -0
- package/dist/tui/run-interruption.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +7 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +77 -2
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/status-card.d.ts +3 -0
- package/dist/tui/status-card.d.ts.map +1 -0
- package/dist/tui/status-card.js +30 -0
- package/dist/tui/status-card.js.map +1 -0
- package/dist/tui/status-panel-layout.d.ts +11 -0
- package/dist/tui/status-panel-layout.d.ts.map +1 -0
- package/dist/tui/status-panel-layout.js +42 -0
- package/dist/tui/status-panel-layout.js.map +1 -0
- package/dist/tui/types.d.ts +7 -1
- package/dist/tui/types.d.ts.map +1 -1
- package/package.json +9 -7
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
* DEFINE a specialist on the fly — pass a `role` (the persona / system prompt)
|
|
4
4
|
* and namzu spins up a fresh sub-agent with that role at runtime, no
|
|
5
5
|
* pre-registered definition needed. Omit `role` for a general-purpose one.
|
|
6
|
-
* The call waits for a final result unless
|
|
6
|
+
* The call waits for a final result unless background execution is requested
|
|
7
|
+
* or operator input releases the wait.
|
|
7
8
|
* The child then stays owned by the parent run, and its completion reaches the
|
|
8
|
-
* same query's inbox. Headless callers
|
|
9
|
+
* same query's inbox. Headless callers also support explicit background work.
|
|
9
10
|
*
|
|
10
11
|
* The runtime is fully self-contained: a dedicated in-memory session/thread
|
|
11
12
|
* store backs the AgentManager, so sub-agent bookkeeping never touches the
|
|
12
|
-
* CLI's on-disk `/resume` conversation store.
|
|
13
|
+
* CLI's on-disk `/resume` conversation store. A separate session-scoped
|
|
14
|
+
* receipt archive preserves observed outcomes without restoring execution authority.
|
|
13
15
|
*/
|
|
14
|
-
import { AgentManager, AgentRegistry, CompletionInbox, DefaultCapacityValidator, EXPLORE_AGENT_DESCRIPTION, EXPLORE_AGENT_ID, EXPLORE_AGENT_PROMPT, InMemorySessionStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent, RunCancelled, SessionSummaryMaterializer, TopicArchivedError, TopicManager, WorkspaceBackendRegistry, asTaskId, defineTool, filterReadOnlyTools, filterToolsNamed, generateSummaryId, isTerminalAgentTaskState, mcpJsonSchemaToZod, openTokenBudget, requireOpenProject, } from '@namzu/sdk';
|
|
16
|
+
import { AgentManager, AgentRegistry, CompletionInbox, DefaultCapacityValidator, EXPLORE_AGENT_DESCRIPTION, EXPLORE_AGENT_ID, EXPLORE_AGENT_PROMPT, InMemorySessionStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent, RunCancelled, SessionSummaryMaterializer, TopicArchivedError, TopicManager, WorkspaceBackendRegistry, asRunId, asTaskId, defineTool, filterReadOnlyTools, filterToolsNamed, generateSummaryId, isTerminalAgentTaskState, mcpJsonSchemaToZod, openTokenBudget, requireOpenProject, } from '@namzu/sdk';
|
|
15
17
|
import { NAMZU_WORKING_DOCTRINE } from '../../context/doctrine.js';
|
|
16
18
|
import { MAX_AGENT_ACTIVITY_LABEL_CODE_UNITS, MAX_AGENT_PHASE_ORDER, SubagentActivityMonitor, } from './activity.js';
|
|
19
|
+
import { DelegationHistory, HISTORY_GUIDANCE } from './history.js';
|
|
17
20
|
import { CLI_INTERACTIVE_RUN_TIMEOUT_MS } from './policy.js';
|
|
18
21
|
export const GENERAL_PURPOSE_SUBAGENT = 'general-purpose';
|
|
19
22
|
/**
|
|
@@ -59,21 +62,32 @@ class ParentTaskScheduler extends LocalTaskScheduler {
|
|
|
59
62
|
}
|
|
60
63
|
async createTask(options) {
|
|
61
64
|
await this.validateParent();
|
|
62
|
-
return super.createTask(
|
|
65
|
+
return super.createTask({
|
|
66
|
+
...options,
|
|
67
|
+
beforeStart: async () => {
|
|
68
|
+
await options.beforeStart?.();
|
|
69
|
+
await this.validateParent();
|
|
70
|
+
},
|
|
71
|
+
});
|
|
63
72
|
}
|
|
64
73
|
owns(taskId) {
|
|
65
74
|
return super.listTasks().some((task) => task.taskId === taskId);
|
|
66
75
|
}
|
|
67
76
|
getTask(taskId) {
|
|
68
|
-
|
|
77
|
+
// The parent ledger retains terminal results after the manager evicts
|
|
78
|
+
// its live record. Lookup and waiting must read the same owned history.
|
|
79
|
+
return super.listTasks().find((task) => task.taskId === taskId);
|
|
69
80
|
}
|
|
70
81
|
cancelTask(taskId, cause) {
|
|
71
82
|
if (this.owns(taskId))
|
|
72
83
|
super.cancelTask(taskId, cause);
|
|
73
84
|
}
|
|
74
85
|
async waitForTask(taskId) {
|
|
75
|
-
|
|
86
|
+
const task = this.getTask(taskId);
|
|
87
|
+
if (!task)
|
|
76
88
|
throw new Error(`Task ${taskId} does not belong to this parent run`);
|
|
89
|
+
if (isTerminalAgentTaskState(task.state))
|
|
90
|
+
return task;
|
|
77
91
|
return super.waitForTask(taskId);
|
|
78
92
|
}
|
|
79
93
|
async continueTask(taskId, message) {
|
|
@@ -138,7 +152,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
138
152
|
currentActor: parentActor,
|
|
139
153
|
}, tenantId);
|
|
140
154
|
await store.updateSession({ ...session, status: 'active' }, tenantId);
|
|
141
|
-
const manager = new AgentManager(registry, { childTimeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS }, {
|
|
155
|
+
const manager = new AgentManager(registry, { childTimeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS, capacityBehavior: 'queue' }, {
|
|
142
156
|
sessionStore: store,
|
|
143
157
|
summaryMaterializer: new SessionSummaryMaterializer({
|
|
144
158
|
store,
|
|
@@ -210,7 +224,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
210
224
|
sessionId: parent.sessionId,
|
|
211
225
|
runId,
|
|
212
226
|
},
|
|
213
|
-
limit: opts.tokenBudget ??
|
|
227
|
+
limit: opts.tokenBudget ?? 0,
|
|
214
228
|
pathBuilder: opts.pathBuilder,
|
|
215
229
|
workingDirectory: opts.cwd,
|
|
216
230
|
});
|
|
@@ -308,14 +322,15 @@ export async function createSubagentRuntime(opts) {
|
|
|
308
322
|
const agentTool = defineTool({
|
|
309
323
|
name: 'Agent',
|
|
310
324
|
description: [
|
|
311
|
-
'Delegate a self-contained task to a sub-agent and wait for its result.
|
|
325
|
+
'Delegate a self-contained task to a sub-agent. Set run_in_background: true to receive its task ID immediately and continue independent work; completion arrives as a task notification. Otherwise wait for its result. Operator input can also release a blocking wait while the child keeps working. Use agent_task_list for current agent status (task_list is only the planning list), send_message to correct a running task, cancel_agent to stop only one owned task, and wait_for_task to retrieve its result; never launch duplicate work.',
|
|
326
|
+
'For a request to run multiple independent tasks in parallel, issue all Agent calls together in one response, or set run_in_background: true on each launch before waiting. A single blocking Agent call followed by another call runs them sequentially.',
|
|
312
327
|
'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.',
|
|
313
328
|
'Use the default "general-purpose" when the task must change files or run commands.',
|
|
314
329
|
'Define a specialist inline with `role` — a system prompt describing who the sub-agent is and how to behave (e.g.',
|
|
315
330
|
'"You are a security auditor; flag vulnerabilities and rate severity"); with `subagent_type: "explore"` the role keeps the read-only roster.',
|
|
316
331
|
'Omit `role` for the plain sub-agent.',
|
|
317
332
|
'The sub-agent runs in its own context with its own tools and cannot see this conversation — put everything it needs in `prompt`.',
|
|
318
|
-
|
|
333
|
+
"Call this multiple times in one response to run specialists in parallel. Tasks beyond the project's live-agent capacity wait in a queue and start as slots become available. Queued tasks have not started execution; do not relaunch them.",
|
|
319
334
|
'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.',
|
|
320
335
|
'These fields are display annotations only; they do not create dependencies, barriers, or serial execution.',
|
|
321
336
|
fileAgentSummary.length > 0 ? `Project-defined types: ${fileAgentSummary}.` : '',
|
|
@@ -333,6 +348,10 @@ export async function createSubagentRuntime(opts) {
|
|
|
333
348
|
type: 'string',
|
|
334
349
|
description: 'Self-contained task with all the context the sub-agent needs.',
|
|
335
350
|
},
|
|
351
|
+
run_in_background: {
|
|
352
|
+
type: 'boolean',
|
|
353
|
+
description: 'Return after launch so the parent can continue independent work. Defaults to false.',
|
|
354
|
+
},
|
|
336
355
|
subagent_type: {
|
|
337
356
|
type: 'string',
|
|
338
357
|
enum: agentTypeIds,
|
|
@@ -340,6 +359,18 @@ export async function createSubagentRuntime(opts) {
|
|
|
340
359
|
? ` This project also defines: ${fileAgentSummary}. Prefer a project-defined type when its description fits the task.`
|
|
341
360
|
: ''}`,
|
|
342
361
|
},
|
|
362
|
+
model: {
|
|
363
|
+
type: 'string',
|
|
364
|
+
description: 'Exact child model ID. Omit to inherit the session model. Use agent_models to discover available IDs; do not search repository files.',
|
|
365
|
+
},
|
|
366
|
+
provider: {
|
|
367
|
+
type: 'string',
|
|
368
|
+
description: 'Provider for this child only. Requires model; does not switch the parent conversation.',
|
|
369
|
+
},
|
|
370
|
+
effort: {
|
|
371
|
+
type: 'string',
|
|
372
|
+
description: 'Exact reasoning effort supported by the child model. Requires model. Omit for its provider default.',
|
|
373
|
+
},
|
|
343
374
|
role: {
|
|
344
375
|
type: 'string',
|
|
345
376
|
description: 'Optional persona / system prompt that defines this specialist sub-agent. Omit for general-purpose.',
|
|
@@ -370,12 +401,19 @@ export async function createSubagentRuntime(opts) {
|
|
|
370
401
|
concurrencySafe: true,
|
|
371
402
|
timeoutMs: CLI_INTERACTIVE_RUN_TIMEOUT_MS,
|
|
372
403
|
async execute(input, context) {
|
|
373
|
-
const { description, prompt, subagent_type, role, workflow, phase, phase_order } = input;
|
|
404
|
+
const { description, prompt, subagent_type, role, workflow, phase, phase_order, run_in_background, model: requestedModel, provider: requestedProvider, effort: requestedEffort, } = input;
|
|
405
|
+
if (!requestedModel && (requestedProvider || requestedEffort))
|
|
406
|
+
throw new Error('Supply model when selecting a child provider or effort.');
|
|
407
|
+
if (requestedModel && !opts.resolveModel)
|
|
408
|
+
throw new Error('Child model selection is unavailable in this host.');
|
|
409
|
+
const selection = requestedModel
|
|
410
|
+
? await opts.resolveModel?.({ model: requestedModel, provider: requestedProvider, effort: requestedEffort }, context.abortSignal)
|
|
411
|
+
: undefined;
|
|
374
412
|
const explore = subagent_type === EXPLORE_SUBAGENT;
|
|
375
413
|
const fileAgent = subagent_type !== undefined ? fileAgents.get(subagent_type) : undefined;
|
|
376
414
|
let agentId = fileAgent?.name ?? (explore ? EXPLORE_SUBAGENT : GENERAL_PURPOSE_SUBAGENT);
|
|
377
415
|
const persona = typeof role === 'string' ? role.trim() : '';
|
|
378
|
-
const dynamic = persona.length > 0;
|
|
416
|
+
const dynamic = persona.length > 0 || selection !== undefined;
|
|
379
417
|
if (dynamic) {
|
|
380
418
|
// A role on top of a type keeps that type's roster and model: the
|
|
381
419
|
// persona says who the child is, the type says what it may touch,
|
|
@@ -383,11 +421,13 @@ export async function createSubagentRuntime(opts) {
|
|
|
383
421
|
// tool. Over a file-defined agent the role is appended to the
|
|
384
422
|
// file's prompt rather than replacing it.
|
|
385
423
|
agentId = `dyn-${++dynCounter}`;
|
|
386
|
-
registry.register(buildDefinition(agentId, `Dynamic specialist: ${agentId}`, fileAgent
|
|
424
|
+
registry.register(buildDefinition(agentId, `Dynamic specialist: ${agentId}`, fileAgent
|
|
425
|
+
? `${fileAgent.prompt}\n\n${persona}`
|
|
426
|
+
: persona || (explore ? EXPLORE_PROMPT : SUBAGENT_PROMPT), opts, fileAgent
|
|
387
427
|
? fileAgentTools(fileAgent, opts)
|
|
388
428
|
: explore
|
|
389
429
|
? () => filterReadOnlyTools(opts.buildTools())
|
|
390
|
-
: opts.buildTools, fileAgent?.model ?? opts.model));
|
|
430
|
+
: opts.buildTools, selection?.model ?? fileAgent?.model ?? opts.model, selection));
|
|
391
431
|
}
|
|
392
432
|
const tracker = activity.begin({
|
|
393
433
|
agentId,
|
|
@@ -408,6 +448,8 @@ export async function createSubagentRuntime(opts) {
|
|
|
408
448
|
// longer be proved.
|
|
409
449
|
const resumeHandler = opts.resolveResumeHandler?.(context.runId) ?? refuseUnownedChildReview;
|
|
410
450
|
const configOverrides = {
|
|
451
|
+
tokenBudget: opts.tokenBudget ?? 0,
|
|
452
|
+
...(selection ? { model: selection.model, effort: selection.effort } : {}),
|
|
411
453
|
...(Object.keys(context.env ?? {}).length > 0 ? { env: context.env } : {}),
|
|
412
454
|
resumeHandler,
|
|
413
455
|
};
|
|
@@ -418,15 +460,33 @@ export async function createSubagentRuntime(opts) {
|
|
|
418
460
|
};
|
|
419
461
|
try {
|
|
420
462
|
const completionInbox = await completionInboxForRun(context.runId);
|
|
463
|
+
const parent = await resolveParent(context.runId);
|
|
464
|
+
const history = opts.historyRoot
|
|
465
|
+
? new DelegationHistory(opts.historyRoot, parent.sessionId)
|
|
466
|
+
: undefined;
|
|
467
|
+
const save = (handle, terminal) => {
|
|
468
|
+
history?.write({
|
|
469
|
+
taskId: handle.taskId,
|
|
470
|
+
parentRunId: context.runId,
|
|
471
|
+
description,
|
|
472
|
+
status: terminal ? agentTaskOutcome(handle) : 'unresolved',
|
|
473
|
+
...(terminal ? { output: String(completedAgentResult(handle).output ?? '') } : {}),
|
|
474
|
+
});
|
|
475
|
+
};
|
|
421
476
|
const outcome = await runBlockingAgentTask({
|
|
422
477
|
gateway: await gatewayForRun(context.runId),
|
|
423
478
|
signal: context.abortSignal,
|
|
424
479
|
waitForInbound: opts.resolveWaitForInbound?.(context.runId),
|
|
480
|
+
background: run_in_background === true,
|
|
425
481
|
completionInbox,
|
|
426
|
-
onCreated: () => {
|
|
482
|
+
onCreated: (handle) => {
|
|
483
|
+
save(handle, false);
|
|
427
484
|
taskOwnsCleanup = true;
|
|
428
485
|
},
|
|
429
|
-
onSettled: (completed) =>
|
|
486
|
+
onSettled: (completed) => {
|
|
487
|
+
save(completed, true);
|
|
488
|
+
tracker.settle(completed);
|
|
489
|
+
},
|
|
430
490
|
onFailed: (error) => tracker.fail(error),
|
|
431
491
|
onFinished: cleanupDefinition,
|
|
432
492
|
create: {
|
|
@@ -444,13 +504,14 @@ export async function createSubagentRuntime(opts) {
|
|
|
444
504
|
},
|
|
445
505
|
});
|
|
446
506
|
if (outcome.kind === 'yielded') {
|
|
507
|
+
const progress = outcome.handle.state === 'pending' ? 'queued for an available slot' : 'still running';
|
|
447
508
|
return {
|
|
448
509
|
success: true,
|
|
449
|
-
output: `Sub-agent ${agentId} is
|
|
510
|
+
output: `Sub-agent ${agentId} for task ${JSON.stringify(description)} is ${progress} as task ${outcome.handle.taskId}; it has not completed. ${run_in_background ? 'Continue independent work while this task runs in the background.' : 'Waiting was released because the operator sent a message. Answer their question or status request before making further tool calls; preserve existing work unless they ask to cancel or change it.'} Its actual result will arrive as a task notification; do not launch the same work again.`,
|
|
450
511
|
data: {
|
|
451
512
|
task_id: outcome.handle.taskId,
|
|
452
513
|
state: outcome.handle.state,
|
|
453
|
-
wait_released: 'operator_input',
|
|
514
|
+
wait_released: run_in_background ? 'background' : 'operator_input',
|
|
454
515
|
},
|
|
455
516
|
};
|
|
456
517
|
}
|
|
@@ -468,6 +529,57 @@ export async function createSubagentRuntime(opts) {
|
|
|
468
529
|
}
|
|
469
530
|
},
|
|
470
531
|
});
|
|
532
|
+
const agentTaskListTool = defineTool({
|
|
533
|
+
name: 'agent_task_list',
|
|
534
|
+
description: 'List the agent invocations launched by this run and their current status, without waiting or starting work. Use this for agent progress questions. task_list contains planning items, not agent invocations. Use wait_for_task with a live ID for its result. Set history: true to inspect saved receipts from this conversation, optionally task_id for one saved result; archives do not prove liveness.',
|
|
535
|
+
inputSchema: mcpJsonSchemaToZod({
|
|
536
|
+
type: 'object',
|
|
537
|
+
properties: { history: { type: 'boolean' }, task_id: { type: 'string' } },
|
|
538
|
+
additionalProperties: false,
|
|
539
|
+
}),
|
|
540
|
+
category: 'custom',
|
|
541
|
+
permissions: [],
|
|
542
|
+
readOnly: true,
|
|
543
|
+
destructive: false,
|
|
544
|
+
concurrencySafe: true,
|
|
545
|
+
async execute(input, context) {
|
|
546
|
+
const request = input;
|
|
547
|
+
if (request.history || request.task_id) {
|
|
548
|
+
if (!opts.historyRoot)
|
|
549
|
+
return { success: false, output: 'Saved delegation history is unavailable in this host.' };
|
|
550
|
+
const parent = await resolveParent(context.runId);
|
|
551
|
+
const history = new DelegationHistory(opts.historyRoot, parent.sessionId);
|
|
552
|
+
const saved = request.task_id
|
|
553
|
+
? { tasks: [history.read(request.task_id)], omitted: 0 }
|
|
554
|
+
: history.list();
|
|
555
|
+
const tasks = saved.tasks.map(({ output, ...row }) => request.task_id ? { ...row, output } : row);
|
|
556
|
+
return {
|
|
557
|
+
success: true,
|
|
558
|
+
output: JSON.stringify({ ...saved, tasks, guidance: HISTORY_GUIDANCE }),
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
const gateway = await gatewayForRun(context.runId);
|
|
562
|
+
const tasks = gateway.listTasks();
|
|
563
|
+
const labels = new Map(activity.getSnapshot().map((entry) => [entry.taskId, entry.description]));
|
|
564
|
+
const shown = tasks.slice(-40).map((task) => ({
|
|
565
|
+
description: labels.get(task.taskId) ?? task.agentId,
|
|
566
|
+
task_id: task.taskId,
|
|
567
|
+
agent: task.agentId,
|
|
568
|
+
state: task.state,
|
|
569
|
+
status: agentTaskOutcome(task),
|
|
570
|
+
...(task.result?.stopReason ? { stop_reason: task.result.stopReason } : {}),
|
|
571
|
+
}));
|
|
572
|
+
return {
|
|
573
|
+
success: true,
|
|
574
|
+
output: JSON.stringify({
|
|
575
|
+
tasks: shown,
|
|
576
|
+
total: tasks.length,
|
|
577
|
+
omitted: tasks.length - shown.length,
|
|
578
|
+
}),
|
|
579
|
+
data: { tasks: shown, total: tasks.length, omitted: tasks.length - shown.length },
|
|
580
|
+
};
|
|
581
|
+
},
|
|
582
|
+
});
|
|
471
583
|
const waitForTaskTool = defineTool({
|
|
472
584
|
name: 'wait_for_task',
|
|
473
585
|
description: 'Wait for a task this run already launched, or retrieve its complete result after a task notification. This does not start new work. Operator input releases the wait while the task continues.',
|
|
@@ -476,7 +588,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
476
588
|
properties: {
|
|
477
589
|
task_id: {
|
|
478
590
|
type: 'string',
|
|
479
|
-
description: 'The
|
|
591
|
+
description: 'The task_id from the Agent launch receipt or task notification metadata.',
|
|
480
592
|
},
|
|
481
593
|
},
|
|
482
594
|
required: ['task_id'],
|
|
@@ -520,13 +632,109 @@ export async function createSubagentRuntime(opts) {
|
|
|
520
632
|
});
|
|
521
633
|
if (outcome.kind === 'completed')
|
|
522
634
|
return completedAgentResult(outcome.handle);
|
|
635
|
+
const progress = outcome.handle.state === 'pending' ? 'queued for an available slot' : 'still running';
|
|
523
636
|
return {
|
|
524
637
|
success: true,
|
|
525
|
-
output: `Task ${taskId} is
|
|
638
|
+
output: `Task ${taskId} is ${progress}; it has not completed. Waiting was released for an operator message. Its result will arrive as a task notification.`,
|
|
526
639
|
data: { task_id: taskId, state: outcome.handle.state, wait_released: 'operator_input' },
|
|
527
640
|
};
|
|
528
641
|
},
|
|
529
642
|
});
|
|
643
|
+
const sendMessageTool = defineTool({
|
|
644
|
+
name: 'send_message',
|
|
645
|
+
description: 'Queue a correction or additional context for a running or queued sub-agent task owned by this run. The child receives it at its next request boundary; acceptance is not delivery. This does not start new work or restart finished tasks.',
|
|
646
|
+
inputSchema: mcpJsonSchemaToZod({
|
|
647
|
+
type: 'object',
|
|
648
|
+
properties: {
|
|
649
|
+
task_id: { type: 'string', description: 'Task UUID returned by Agent.' },
|
|
650
|
+
message: {
|
|
651
|
+
type: 'string',
|
|
652
|
+
minLength: 1,
|
|
653
|
+
maxLength: 16000,
|
|
654
|
+
description: 'Correction or additional context for this task.',
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
required: ['task_id', 'message'],
|
|
658
|
+
additionalProperties: false,
|
|
659
|
+
}),
|
|
660
|
+
category: 'custom',
|
|
661
|
+
permissions: [],
|
|
662
|
+
readOnly: false,
|
|
663
|
+
destructive: false,
|
|
664
|
+
concurrencySafe: false,
|
|
665
|
+
async execute(input, context) {
|
|
666
|
+
context.abortSignal.throwIfAborted();
|
|
667
|
+
const { task_id, message } = input;
|
|
668
|
+
if (!message.trim())
|
|
669
|
+
return { success: false, output: '', error: 'Message must not be blank.' };
|
|
670
|
+
const taskId = asTaskId(task_id);
|
|
671
|
+
const gateway = await gatewayForRun(context.runId);
|
|
672
|
+
const task = gateway.getTask(taskId);
|
|
673
|
+
if (!task)
|
|
674
|
+
return {
|
|
675
|
+
success: false,
|
|
676
|
+
output: '',
|
|
677
|
+
error: `Task ${taskId} does not belong to this parent run.`,
|
|
678
|
+
};
|
|
679
|
+
if (isTerminalAgentTaskState(task.state))
|
|
680
|
+
return {
|
|
681
|
+
success: false,
|
|
682
|
+
output: '',
|
|
683
|
+
error: 'This task has finished; send_message cannot restart it.',
|
|
684
|
+
};
|
|
685
|
+
context.abortSignal.throwIfAborted();
|
|
686
|
+
await gateway.continueTask(taskId, message);
|
|
687
|
+
return {
|
|
688
|
+
success: true,
|
|
689
|
+
output: `Message queued for task ${taskId}; it will be available at the child's next request boundary.`,
|
|
690
|
+
data: { task_id: taskId, status: 'queued' },
|
|
691
|
+
};
|
|
692
|
+
},
|
|
693
|
+
});
|
|
694
|
+
const cancelAgentTool = defineTool({
|
|
695
|
+
name: 'cancel_agent',
|
|
696
|
+
description: 'Request cancellation of one running or queued agent task owned by this run. Other agents and the parent continue. Acceptance is not proof of termination; check agent_task_list or wait_for_task for the terminal outcome. Does not restart finished tasks.',
|
|
697
|
+
inputSchema: mcpJsonSchemaToZod({
|
|
698
|
+
type: 'object',
|
|
699
|
+
properties: {
|
|
700
|
+
task_id: {
|
|
701
|
+
type: 'string',
|
|
702
|
+
description: 'Exact task UUID returned by Agent or agent_task_list.',
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
required: ['task_id'],
|
|
706
|
+
additionalProperties: false,
|
|
707
|
+
}),
|
|
708
|
+
category: 'custom',
|
|
709
|
+
permissions: [],
|
|
710
|
+
readOnly: false,
|
|
711
|
+
destructive: false,
|
|
712
|
+
concurrencySafe: false,
|
|
713
|
+
async execute(input, context) {
|
|
714
|
+
context.abortSignal.throwIfAborted();
|
|
715
|
+
const taskId = asTaskId(input.task_id);
|
|
716
|
+
const gateway = await gatewayForRun(context.runId);
|
|
717
|
+
const task = gateway.getTask(taskId);
|
|
718
|
+
if (!task)
|
|
719
|
+
return {
|
|
720
|
+
success: false,
|
|
721
|
+
output: '',
|
|
722
|
+
error: `Task ${taskId} does not belong to this parent run.`,
|
|
723
|
+
};
|
|
724
|
+
if (isTerminalAgentTaskState(task.state))
|
|
725
|
+
return {
|
|
726
|
+
success: true,
|
|
727
|
+
output: `Task ${taskId} already ended: ${agentTaskOutcome(task)}. No cancellation sent.`,
|
|
728
|
+
};
|
|
729
|
+
context.abortSignal.throwIfAborted();
|
|
730
|
+
gateway.cancelTask(taskId, 'user');
|
|
731
|
+
return {
|
|
732
|
+
success: true,
|
|
733
|
+
output: `Cancellation requested for task ${taskId}. Other tasks continue. Use agent_task_list or wait_for_task to confirm its terminal outcome.`,
|
|
734
|
+
data: { task_id: taskId, status: 'cancellation_requested' },
|
|
735
|
+
};
|
|
736
|
+
},
|
|
737
|
+
});
|
|
530
738
|
let closePromise;
|
|
531
739
|
const close = () => {
|
|
532
740
|
if (closePromise)
|
|
@@ -535,12 +743,40 @@ export async function createSubagentRuntime(opts) {
|
|
|
535
743
|
closePromise = Promise.all([...parents.keys()].map(releaseRun)).then(() => activity.close());
|
|
536
744
|
return closePromise;
|
|
537
745
|
};
|
|
746
|
+
const listModels = opts.listModels;
|
|
747
|
+
const modelCatalogueTool = listModels
|
|
748
|
+
? defineTool({
|
|
749
|
+
name: 'agent_models',
|
|
750
|
+
description: 'Discover connected provider/model IDs and published capabilities for delegation. Search here before choosing a child model; capability metadata is not a quality ranking.',
|
|
751
|
+
inputSchema: mcpJsonSchemaToZod({
|
|
752
|
+
type: 'object',
|
|
753
|
+
properties: {
|
|
754
|
+
query: { type: 'string', description: 'Optional provider or model name filter.' },
|
|
755
|
+
},
|
|
756
|
+
}),
|
|
757
|
+
category: 'custom',
|
|
758
|
+
permissions: [],
|
|
759
|
+
readOnly: true,
|
|
760
|
+
destructive: false,
|
|
761
|
+
concurrencySafe: true,
|
|
762
|
+
async execute(input, context) {
|
|
763
|
+
return {
|
|
764
|
+
success: true,
|
|
765
|
+
output: await listModels(input.query ?? '', context.abortSignal),
|
|
766
|
+
};
|
|
767
|
+
},
|
|
768
|
+
})
|
|
769
|
+
: undefined;
|
|
538
770
|
return {
|
|
771
|
+
cancelAgentTool,
|
|
772
|
+
modelCatalogueTool,
|
|
539
773
|
gatewayForRun,
|
|
540
774
|
completionInboxForRun,
|
|
541
775
|
releaseRun,
|
|
542
776
|
agentTool,
|
|
543
777
|
waitForTaskTool,
|
|
778
|
+
agentTaskListTool,
|
|
779
|
+
sendMessageTool,
|
|
544
780
|
allowedAgentIds: agentTypeIds,
|
|
545
781
|
activity,
|
|
546
782
|
close,
|
|
@@ -587,17 +823,32 @@ async function runBlockingAgentTask(input) {
|
|
|
587
823
|
cancel(handle);
|
|
588
824
|
signal.throwIfAborted();
|
|
589
825
|
}
|
|
590
|
-
input.onCreated();
|
|
826
|
+
input.onCreated(handle);
|
|
591
827
|
input.completionInbox.launched(handle.taskId);
|
|
592
828
|
const completion = gateway.waitForTask(handle.taskId).then((completed) => {
|
|
593
|
-
|
|
594
|
-
|
|
829
|
+
try {
|
|
830
|
+
input.onSettled(completed);
|
|
831
|
+
}
|
|
832
|
+
finally {
|
|
833
|
+
input.onFinished();
|
|
834
|
+
}
|
|
595
835
|
return { kind: 'completed', handle: completed };
|
|
596
836
|
}, (error) => {
|
|
597
|
-
|
|
598
|
-
|
|
837
|
+
try {
|
|
838
|
+
input.onFailed(error);
|
|
839
|
+
}
|
|
840
|
+
finally {
|
|
841
|
+
input.onFinished();
|
|
842
|
+
}
|
|
599
843
|
throw error;
|
|
600
844
|
});
|
|
845
|
+
if (input.background && !isTerminalAgentTaskState(handle.state)) {
|
|
846
|
+
// The observer retains activity/definition cleanup after this invocation
|
|
847
|
+
// returns. Parent release still owns cancellation and the tree budget.
|
|
848
|
+
completion.catch(() => { });
|
|
849
|
+
input.completionInbox.expect(handle.taskId);
|
|
850
|
+
return { kind: 'yielded', handle };
|
|
851
|
+
}
|
|
601
852
|
const inbound = input
|
|
602
853
|
.waitForInbound?.(wakeController.signal)
|
|
603
854
|
.then(() => ({ kind: 'inbound' }));
|
|
@@ -610,7 +861,7 @@ async function runBlockingAgentTask(input) {
|
|
|
610
861
|
// Only the wait has ended. Parent runtime ownership, token reservations and
|
|
611
862
|
// the completion observer all outlive this tool invocation's local signal.
|
|
612
863
|
input.completionInbox.expect(handle.taskId);
|
|
613
|
-
return { kind: 'yielded', handle };
|
|
864
|
+
return { kind: 'yielded', handle: current ?? handle };
|
|
614
865
|
}
|
|
615
866
|
catch (error) {
|
|
616
867
|
if (handle)
|
|
@@ -625,10 +876,19 @@ async function runBlockingAgentTask(input) {
|
|
|
625
876
|
wakeController.abort();
|
|
626
877
|
}
|
|
627
878
|
}
|
|
879
|
+
function agentTaskOutcome(task) {
|
|
880
|
+
const run = task.result;
|
|
881
|
+
if (run?.status && run.status !== 'completed')
|
|
882
|
+
return run.status;
|
|
883
|
+
if (task.state === 'completed' && run?.stopReason && run.stopReason !== 'end_turn')
|
|
884
|
+
return 'incomplete';
|
|
885
|
+
return task.state;
|
|
886
|
+
}
|
|
628
887
|
/** Lifecycle completion is not proof the requested task finished successfully. */
|
|
629
888
|
function completedAgentResult(completed) {
|
|
630
889
|
const run = completed.result;
|
|
631
|
-
const
|
|
890
|
+
const status = agentTaskOutcome(completed);
|
|
891
|
+
const succeeded = status === 'completed';
|
|
632
892
|
const value = run?.structuredOutput ?? run?.result;
|
|
633
893
|
let resultText = typeof value === 'string' ? value : value === undefined ? '' : JSON.stringify(value);
|
|
634
894
|
if (!resultText && run?.stopReason && run.stopReason !== 'end_turn') {
|
|
@@ -645,19 +905,22 @@ function completedAgentResult(completed) {
|
|
|
645
905
|
const stopNote = run?.stopReason && run.stopReason !== 'end_turn'
|
|
646
906
|
? `Sub-agent run ended with stop reason "${run.stopReason}". Output may be partial; this does not establish task completion.\n\n`
|
|
647
907
|
: '';
|
|
648
|
-
|
|
908
|
+
// ToolResult.data is host metadata, not necessarily model-visible content.
|
|
909
|
+
// Keep the handle and terminal status separate from arbitrary child output
|
|
910
|
+
// (which may itself contain UUIDs or text such as "Task 1").
|
|
911
|
+
const output = `task_id: ${completed.taskId}\nstatus: ${status}\n\nAgent result:\n${stopNote}${resultText || '(sub-agent returned no text)'}`;
|
|
649
912
|
return {
|
|
650
913
|
success: succeeded,
|
|
651
914
|
output,
|
|
652
915
|
...(!succeeded
|
|
653
916
|
? {
|
|
654
|
-
error: `Sub-agent ${completed.agentId} ${
|
|
917
|
+
error: `Sub-agent ${completed.agentId} ${status}: ${run?.lastError ?? ''}\n${output}`,
|
|
655
918
|
}
|
|
656
919
|
: {}),
|
|
657
920
|
data: {
|
|
658
921
|
task_id: completed.taskId,
|
|
659
922
|
state: completed.state,
|
|
660
|
-
|
|
923
|
+
status,
|
|
661
924
|
...(run?.stopReason ? { stop_reason: run.stopReason } : {}),
|
|
662
925
|
},
|
|
663
926
|
};
|
|
@@ -671,7 +934,7 @@ function buildDefinition(id, description, systemPrompt, opts,
|
|
|
671
934
|
/** This definition's own roster; absent means the parent's working set. */
|
|
672
935
|
tools = opts.buildTools,
|
|
673
936
|
/** This definition's model; absent means the session's. */
|
|
674
|
-
model = opts.model) {
|
|
937
|
+
model = opts.model, selection) {
|
|
675
938
|
const agent = new ReactiveAgent({
|
|
676
939
|
id,
|
|
677
940
|
name: id,
|
|
@@ -691,7 +954,7 @@ model = opts.model) {
|
|
|
691
954
|
category: 'general',
|
|
692
955
|
description,
|
|
693
956
|
tools: [],
|
|
694
|
-
defaults: { model, tokenBudget:
|
|
957
|
+
defaults: { model, tokenBudget: opts.tokenBudget ?? 0 },
|
|
695
958
|
},
|
|
696
959
|
// ReactiveAgent is Agent<ReactiveAgentConfig,…>; the registry stores the
|
|
697
960
|
// erased Agent<BaseAgentConfig,…>. configBuilder supplies the richer config.
|
|
@@ -702,12 +965,19 @@ model = opts.model) {
|
|
|
702
965
|
// since the parent started, and a sub-agent asserting the stale answer
|
|
703
966
|
// is worse than one that was never told.
|
|
704
967
|
const environment = opts.readEnvironment ? await opts.readEnvironment() : null;
|
|
968
|
+
// AgentManager supplies the actual parent run before it stamps the child's
|
|
969
|
+
// own Session ID. All delegated work shares that invoking conversation's
|
|
970
|
+
// upstream billing session, even after the TUI moves to another one.
|
|
971
|
+
const parent = options.parentRunId
|
|
972
|
+
? await opts.resolveParent(asRunId(options.parentRunId))
|
|
973
|
+
: undefined;
|
|
705
974
|
return {
|
|
706
975
|
model: options.model ?? model,
|
|
707
|
-
tokenBudget: options.tokenBudget ??
|
|
976
|
+
tokenBudget: options.tokenBudget ?? opts.tokenBudget ?? 0,
|
|
708
977
|
timeoutMs: options.timeoutMs ?? CLI_INTERACTIVE_RUN_TIMEOUT_MS,
|
|
709
978
|
maxIterations: 40,
|
|
710
|
-
provider: opts.buildProvider(),
|
|
979
|
+
provider: await opts.buildProvider(parent?.sessionId, selection),
|
|
980
|
+
...(selection?.effort ? { effort: selection.effort } : {}),
|
|
711
981
|
tools: tools(),
|
|
712
982
|
systemPrompt: environment ? `${base}\n\n${environment}` : base,
|
|
713
983
|
...(opts.projectInstructionContext
|