@namzu/cli 26.3.0 → 27.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 +214 -0
- package/README.md +49 -34
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -1
- package/dist/checkpoints/store.d.ts +17 -4
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +20 -5
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/acp.d.ts +23 -1
- package/dist/commands/acp.d.ts.map +1 -1
- package/dist/commands/acp.js +41 -2
- package/dist/commands/acp.js.map +1 -1
- package/dist/commands/drain.d.ts +69 -2
- package/dist/commands/drain.d.ts.map +1 -1
- package/dist/commands/drain.js +214 -134
- package/dist/commands/drain.js.map +1 -1
- package/dist/commands/eval.js +1 -1
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/provider-wait.d.ts +5 -5
- package/dist/commands/provider-wait.d.ts.map +1 -1
- package/dist/commands/provider-wait.js +2 -2
- package/dist/commands/resident.d.ts.map +1 -1
- package/dist/commands/resident.js +2 -1
- package/dist/commands/resident.js.map +1 -1
- package/dist/commands/run-flags.d.ts +7 -7
- package/dist/commands/run-flags.js +4 -4
- package/dist/commands/run-stream.d.ts +16 -12
- package/dist/commands/run-stream.d.ts.map +1 -1
- package/dist/commands/run-stream.js +142 -98
- package/dist/commands/run-stream.js.map +1 -1
- package/dist/commands/run.d.ts +6 -4
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +129 -45
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/serve.js +2 -2
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/state.js +8 -8
- package/dist/commands/state.js.map +1 -1
- package/dist/config/load.d.ts +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +17 -5
- package/dist/config/load.js.map +1 -1
- package/dist/config/run-limits.d.ts +20 -8
- package/dist/config/run-limits.d.ts.map +1 -1
- package/dist/config/run-limits.js +28 -46
- package/dist/config/run-limits.js.map +1 -1
- package/dist/config/schema.d.ts +44 -19
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/tool-result-screens.js +1 -1
- package/dist/context/directories.d.ts +37 -0
- package/dist/context/directories.d.ts.map +1 -0
- package/dist/context/directories.js +69 -0
- package/dist/context/directories.js.map +1 -0
- package/dist/context/environment.d.ts +55 -0
- package/dist/context/environment.d.ts.map +1 -1
- package/dist/context/environment.js +96 -0
- package/dist/context/environment.js.map +1 -1
- package/dist/context/project-tracker.d.ts +3 -3
- package/dist/context/project-tracker.d.ts.map +1 -1
- package/dist/context/project-tracker.js +3 -3
- package/dist/context/project-tracker.js.map +1 -1
- package/dist/context/project.d.ts +1 -1
- package/dist/context/project.js +1 -1
- package/dist/context/sandbox.d.ts +22 -3
- package/dist/context/sandbox.d.ts.map +1 -1
- package/dist/context/sandbox.js +42 -4
- package/dist/context/sandbox.js.map +1 -1
- package/dist/doctor/checks/chain.d.ts +1 -1
- package/dist/doctor/checks/chain.js +5 -5
- package/dist/doctor/checks/chain.js.map +1 -1
- package/dist/doctor/checks/invariants.d.ts +2 -2
- package/dist/doctor/checks/invariants.js +5 -5
- package/dist/doctor/checks/invariants.js.map +1 -1
- package/dist/doctor/checks/logging.js +1 -1
- package/dist/doctor/checks/logging.js.map +1 -1
- package/dist/doctor/checks/sandbox.js +1 -1
- package/dist/doctor/checks/sandbox.js.map +1 -1
- package/dist/doctor/checks/session-export.d.ts +1 -1
- package/dist/doctor/checks/session-export.js +2 -2
- package/dist/doctor/checks/session-export.js.map +1 -1
- package/dist/doctor/checks/telemetry.d.ts +1 -1
- package/dist/doctor/checks/telemetry.js +1 -1
- package/dist/doctor/registry.js +2 -2
- package/dist/doctor/registry.js.map +1 -1
- package/dist/exit-codes.d.ts +2 -2
- package/dist/exit-codes.js +2 -2
- package/dist/integrations/mcp/servers.d.ts +1 -1
- package/dist/integrations/providers/chain-capabilities.d.ts +6 -6
- package/dist/integrations/providers/chain-capabilities.js +8 -8
- package/dist/integrations/providers/chain-capabilities.js.map +1 -1
- package/dist/integrations/providers/register.d.ts +1 -1
- package/dist/integrations/providers/register.js +1 -1
- package/dist/integrations/providers/registry.d.ts +1 -1
- package/dist/integrations/resident/inspection.d.ts +4 -1
- package/dist/integrations/resident/inspection.d.ts.map +1 -1
- package/dist/integrations/resident/inspection.js +53 -34
- package/dist/integrations/resident/inspection.js.map +1 -1
- package/dist/integrations/resident/learning-storage.d.ts +7 -2
- package/dist/integrations/resident/learning-storage.d.ts.map +1 -1
- package/dist/integrations/resident/learning-storage.js +12 -8
- package/dist/integrations/resident/learning-storage.js.map +1 -1
- package/dist/integrations/resident/runner-store.d.ts +1 -1
- package/dist/integrations/resident/runner-store.d.ts.map +1 -1
- package/dist/integrations/resident/runner-store.js +5 -2
- package/dist/integrations/resident/runner-store.js.map +1 -1
- package/dist/integrations/resident/runner-worker.js +1 -0
- package/dist/integrations/resident/runner-worker.js.map +1 -1
- package/dist/integrations/resident/session-log-reads.d.ts +37 -0
- package/dist/integrations/resident/session-log-reads.d.ts.map +1 -0
- package/dist/integrations/resident/session-log-reads.js +125 -0
- package/dist/integrations/resident/session-log-reads.js.map +1 -0
- package/dist/integrations/resident/session-step.d.ts +2 -0
- package/dist/integrations/resident/session-step.d.ts.map +1 -1
- package/dist/integrations/resident/session-step.js +15 -13
- package/dist/integrations/resident/session-step.js.map +1 -1
- package/dist/integrations/resident/storage.d.ts +23 -1
- package/dist/integrations/resident/storage.d.ts.map +1 -1
- package/dist/integrations/resident/storage.js +60 -17
- package/dist/integrations/resident/storage.js.map +1 -1
- package/dist/integrations/resident/tool-evidence.d.ts +4 -2
- package/dist/integrations/resident/tool-evidence.d.ts.map +1 -1
- package/dist/integrations/resident/tool-evidence.js +36 -30
- package/dist/integrations/resident/tool-evidence.js.map +1 -1
- package/dist/integrations/resident/verification.d.ts +2 -2
- package/dist/integrations/resident/verification.d.ts.map +1 -1
- package/dist/integrations/resident/verification.js +4 -4
- package/dist/integrations/resident/verification.js.map +1 -1
- package/dist/integrations/sessions/checkpoint-view.d.ts +13 -0
- package/dist/integrations/sessions/checkpoint-view.d.ts.map +1 -0
- package/dist/integrations/sessions/checkpoint-view.js +48 -0
- package/dist/integrations/sessions/checkpoint-view.js.map +1 -0
- package/dist/integrations/sessions/context-inventory.js +1 -1
- package/dist/integrations/sessions/context-inventory.js.map +1 -1
- package/dist/integrations/sessions/conversation-search.d.ts +16 -20
- package/dist/integrations/sessions/conversation-search.d.ts.map +1 -1
- package/dist/integrations/sessions/conversation-search.js +305 -731
- package/dist/integrations/sessions/conversation-search.js.map +1 -1
- package/dist/integrations/sessions/evidence-page-validation.d.ts +4 -4
- package/dist/integrations/sessions/evidence-page-validation.d.ts.map +1 -1
- package/dist/integrations/sessions/evidence-page-validation.js +1 -1
- package/dist/integrations/sessions/evidence-page-validation.js.map +1 -1
- package/dist/integrations/sessions/evidence-recall.d.ts +40 -2
- package/dist/integrations/sessions/evidence-recall.d.ts.map +1 -1
- package/dist/integrations/sessions/evidence-recall.js +41 -97
- package/dist/integrations/sessions/evidence-recall.js.map +1 -1
- package/dist/integrations/sessions/store.d.ts +153 -85
- package/dist/integrations/sessions/store.d.ts.map +1 -1
- package/dist/integrations/sessions/store.js +414 -484
- package/dist/integrations/sessions/store.js.map +1 -1
- package/dist/integrations/sessions/task-context.d.ts +15 -2
- package/dist/integrations/sessions/task-context.d.ts.map +1 -1
- package/dist/integrations/sessions/task-context.js +34 -11
- package/dist/integrations/sessions/task-context.js.map +1 -1
- package/dist/integrations/sessions/transcript-export.d.ts +13 -6
- package/dist/integrations/sessions/transcript-export.d.ts.map +1 -1
- package/dist/integrations/sessions/transcript-export.js +148 -263
- package/dist/integrations/sessions/transcript-export.js.map +1 -1
- package/dist/integrations/state/home.d.ts +4 -16
- package/dist/integrations/state/home.d.ts.map +1 -1
- package/dist/integrations/state/home.js +6 -42
- package/dist/integrations/state/home.js.map +1 -1
- package/dist/integrations/state/project.d.ts +6 -7
- package/dist/integrations/state/project.d.ts.map +1 -1
- package/dist/integrations/state/project.js +7 -9
- package/dist/integrations/state/project.js.map +1 -1
- package/dist/integrations/state/report.d.ts +27 -41
- package/dist/integrations/state/report.d.ts.map +1 -1
- package/dist/integrations/state/report.js +172 -464
- package/dist/integrations/state/report.js.map +1 -1
- package/dist/integrations/state/retention.d.ts +18 -0
- package/dist/integrations/state/retention.d.ts.map +1 -0
- package/dist/integrations/state/retention.js +18 -0
- package/dist/integrations/state/retention.js.map +1 -0
- package/dist/integrations/subagents/activity.d.ts +36 -31
- package/dist/integrations/subagents/activity.d.ts.map +1 -1
- package/dist/integrations/subagents/activity.js +27 -23
- package/dist/integrations/subagents/activity.js.map +1 -1
- package/dist/integrations/subagents/batches.d.ts +91 -0
- package/dist/integrations/subagents/batches.d.ts.map +1 -0
- package/dist/integrations/subagents/batches.js +172 -0
- package/dist/integrations/subagents/batches.js.map +1 -0
- package/dist/integrations/subagents/parent.d.ts +26 -11
- package/dist/integrations/subagents/parent.d.ts.map +1 -1
- package/dist/integrations/subagents/parent.js +13 -28
- package/dist/integrations/subagents/parent.js.map +1 -1
- package/dist/integrations/subagents/policy.d.ts +2 -2
- package/dist/integrations/subagents/policy.d.ts.map +1 -1
- package/dist/integrations/subagents/policy.js +2 -2
- package/dist/integrations/subagents/policy.js.map +1 -1
- package/dist/integrations/subagents/replay.d.ts +91 -26
- package/dist/integrations/subagents/replay.d.ts.map +1 -1
- package/dist/integrations/subagents/replay.js +216 -114
- package/dist/integrations/subagents/replay.js.map +1 -1
- package/dist/integrations/subagents/runtime.d.ts +50 -26
- package/dist/integrations/subagents/runtime.d.ts.map +1 -1
- package/dist/integrations/subagents/runtime.js +132 -126
- package/dist/integrations/subagents/runtime.js.map +1 -1
- package/dist/integrations/subagents/saved-agents.d.ts +57 -0
- package/dist/integrations/subagents/saved-agents.d.ts.map +1 -0
- package/dist/integrations/subagents/saved-agents.js +102 -0
- package/dist/integrations/subagents/saved-agents.js.map +1 -0
- package/dist/integrations/subagents/slash.d.ts +55 -0
- package/dist/integrations/subagents/slash.d.ts.map +1 -0
- package/dist/integrations/subagents/slash.js +45 -0
- package/dist/integrations/subagents/slash.js.map +1 -0
- package/dist/integrations/telemetry/session-export.d.ts +6 -6
- package/dist/integrations/telemetry/session-export.d.ts.map +1 -1
- package/dist/integrations/telemetry/session-export.js +2 -2
- package/dist/memory/presentation.d.ts +11 -0
- package/dist/memory/presentation.d.ts.map +1 -1
- package/dist/memory/presentation.js +25 -0
- package/dist/memory/presentation.js.map +1 -1
- package/dist/memory/store.d.ts +13 -7
- package/dist/memory/store.d.ts.map +1 -1
- package/dist/memory/store.js +37 -11
- package/dist/memory/store.js.map +1 -1
- package/dist/memory/typed.d.ts +116 -0
- package/dist/memory/typed.d.ts.map +1 -0
- package/dist/memory/typed.js +336 -0
- package/dist/memory/typed.js.map +1 -0
- package/dist/permissions/checks.d.ts +1 -1
- package/dist/permissions/checks.js +1 -1
- package/dist/permissions/headless-trust.d.ts +2 -2
- package/dist/permissions/headless-trust.d.ts.map +1 -1
- package/dist/permissions/headless-trust.js +3 -3
- package/dist/permissions/headless-trust.js.map +1 -1
- package/dist/permissions/mode.d.ts +4 -4
- package/dist/permissions/mode.js +1 -1
- package/dist/permissions/rules.d.ts +1 -1
- package/dist/permissions/rules.js +1 -1
- package/dist/termination.d.ts +75 -0
- package/dist/termination.d.ts.map +1 -0
- package/dist/termination.js +143 -0
- package/dist/termination.js.map +1 -0
- package/dist/tui/AgentExplorer.d.ts +2 -2
- package/dist/tui/AgentExplorer.js +4 -4
- package/dist/tui/AgentExplorer.js.map +1 -1
- package/dist/tui/App.d.ts +9 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +452 -303
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/LiveActivity.d.ts +1 -1
- package/dist/tui/LiveActivity.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.d.ts +8 -0
- package/dist/tui/PermissionOverlay.d.ts.map +1 -1
- package/dist/tui/PermissionOverlay.js +25 -2
- package/dist/tui/PermissionOverlay.js.map +1 -1
- package/dist/tui/Picker.js +1 -1
- package/dist/tui/agent.d.ts +208 -132
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +523 -313
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/conversation-evidence-view.js +3 -3
- package/dist/tui/conversation-evidence-view.js.map +1 -1
- package/dist/tui/conversation-history.d.ts +4 -21
- package/dist/tui/conversation-history.d.ts.map +1 -1
- package/dist/tui/conversation-history.js +4 -28
- package/dist/tui/conversation-history.js.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/index.js +27 -1
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/log-pane.js +1 -1
- package/dist/tui/markdownParser.d.ts +1 -1
- package/dist/tui/markdownParser.js +1 -1
- package/dist/tui/permission-review.d.ts +16 -0
- package/dist/tui/permission-review.d.ts.map +1 -1
- package/dist/tui/permission-review.js +25 -2
- package/dist/tui/permission-review.js.map +1 -1
- package/dist/tui/slashCommands.d.ts +29 -40
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +70 -61
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/{run-interruption.d.ts → turn-interruption.d.ts} +4 -4
- package/dist/tui/turn-interruption.d.ts.map +1 -0
- package/dist/tui/{run-interruption.js → turn-interruption.js} +20 -20
- package/dist/tui/turn-interruption.js.map +1 -0
- package/dist/tui/turn-limits-settings.d.ts +39 -0
- package/dist/tui/turn-limits-settings.d.ts.map +1 -0
- package/dist/tui/{run-limits-settings.js → turn-limits-settings.js} +18 -18
- package/dist/tui/turn-limits-settings.js.map +1 -0
- package/dist/tui/types.d.ts +3 -3
- package/dist/tui/types.d.ts.map +1 -1
- package/dist/user-commands/store.d.ts +1 -1
- package/dist/user-commands/store.js +1 -1
- package/package.json +7 -7
- package/dist/integrations/sessions/compaction-evidence.d.ts +0 -5
- package/dist/integrations/sessions/compaction-evidence.d.ts.map +0 -1
- package/dist/integrations/sessions/compaction-evidence.js +0 -59
- package/dist/integrations/sessions/compaction-evidence.js.map +0 -1
- package/dist/integrations/sessions/database.d.ts +0 -5
- package/dist/integrations/sessions/database.d.ts.map +0 -1
- package/dist/integrations/sessions/database.js +0 -34
- package/dist/integrations/sessions/database.js.map +0 -1
- package/dist/integrations/sessions/paths.d.ts +0 -7
- package/dist/integrations/sessions/paths.d.ts.map +0 -1
- package/dist/integrations/sessions/paths.js +0 -14
- package/dist/integrations/sessions/paths.js.map +0 -1
- package/dist/integrations/sessions/run-discovery.d.ts +0 -24
- package/dist/integrations/sessions/run-discovery.d.ts.map +0 -1
- package/dist/integrations/sessions/run-discovery.js +0 -142
- package/dist/integrations/sessions/run-discovery.js.map +0 -1
- package/dist/integrations/sessions/turn-evidence.d.ts +0 -129
- package/dist/integrations/sessions/turn-evidence.d.ts.map +0 -1
- package/dist/integrations/sessions/turn-evidence.js +0 -517
- package/dist/integrations/sessions/turn-evidence.js.map +0 -1
- package/dist/integrations/subagents/history.d.ts +0 -26
- package/dist/integrations/subagents/history.d.ts.map +0 -1
- package/dist/integrations/subagents/history.js +0 -76
- package/dist/integrations/subagents/history.js.map +0 -1
- package/dist/integrations/subagents/runs.d.ts +0 -72
- package/dist/integrations/subagents/runs.d.ts.map +0 -1
- package/dist/integrations/subagents/runs.js +0 -155
- package/dist/integrations/subagents/runs.js.map +0 -1
- package/dist/tui/run-interruption.d.ts.map +0 -1
- package/dist/tui/run-interruption.js.map +0 -1
- package/dist/tui/run-limits-settings.d.ts +0 -39
- package/dist/tui/run-limits-settings.d.ts.map +0 -1
- package/dist/tui/run-limits-settings.js.map +0 -1
|
@@ -5,20 +5,24 @@
|
|
|
5
5
|
* pre-registered definition needed. Omit `role` for a general-purpose one.
|
|
6
6
|
* The call waits for a final result unless background execution is requested
|
|
7
7
|
* or operator input releases the wait.
|
|
8
|
-
* The child then stays owned by the parent
|
|
8
|
+
* The child then stays owned by the parent turn, and its completion reaches the
|
|
9
9
|
* same query's inbox. Headless callers also support explicit background work.
|
|
10
10
|
*
|
|
11
11
|
* The runtime is fully self-contained: a dedicated in-memory session/thread
|
|
12
12
|
* store backs the AgentManager, so sub-agent bookkeeping never touches the
|
|
13
|
-
* CLI's
|
|
14
|
-
*
|
|
13
|
+
* CLI's `/resume` conversation store. Each child is a child session whose log
|
|
14
|
+
* the SDK writes under the parent's `<session-id>/subagents/`, and the parent's
|
|
15
|
+
* log records its spawn and its ending; the saved-agents reader
|
|
16
|
+
* (`saved-agents.ts`) reads those records back as evidence, never as execution
|
|
17
|
+
* authority.
|
|
15
18
|
*/
|
|
16
|
-
import { AgentManager, AgentRegistry, CompletionInbox, DefaultCapacityValidator, EXPLORE_AGENT_DESCRIPTION, EXPLORE_AGENT_ID, EXPLORE_AGENT_PROMPT, InMemorySessionStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent,
|
|
17
|
-
import {
|
|
19
|
+
import { AgentManager, AgentRegistry, CompletionInbox, DefaultCapacityValidator, DiskSessionTokenBudgetStore, EXPLORE_AGENT_DESCRIPTION, EXPLORE_AGENT_ID, EXPLORE_AGENT_PROMPT, InMemorySessionStore, InMemorySessionTokenBudgetStore, InMemoryTopicStore, LocalTaskScheduler, ReactiveAgent, SessionSummaryMaterializer, TopicArchivedError, TopicManager, TurnCancelled, WorkspaceBackendRegistry, asTaskId, asTurnId, defineTool, filterReadOnlyTools, filterToolsNamed, generateSummaryId, isTerminalAgentTaskState, mcpJsonSchemaToZod, openSessionTokenBudget, requireOpenProject, } from '@namzu/sdk';
|
|
20
|
+
import { resolveTurnGuards } from '../../config/run-limits.js';
|
|
18
21
|
import { NAMZU_WORKING_DOCTRINE } from '../../context/doctrine.js';
|
|
22
|
+
import { CLI_CHECKPOINT_RETENTION } from '../state/retention.js';
|
|
19
23
|
import { MAX_AGENT_ACTIVITY_LABEL_CODE_UNITS, MAX_AGENT_PHASE_ORDER, MAX_NARRATION_CODE_UNITS, MAX_RETAINED_NARRATION, SubagentActivityMonitor, } from './activity.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
24
|
+
import { CLI_INTERACTIVE_TURN_TIMEOUT_MS } from './policy.js';
|
|
25
|
+
import { SAVED_AGENTS_GUIDANCE } from './saved-agents.js';
|
|
22
26
|
/**
|
|
23
27
|
* The parent's narration tool, named once.
|
|
24
28
|
*
|
|
@@ -69,12 +73,12 @@ const SUBAGENT_PROMPT = [
|
|
|
69
73
|
'- If you need to research and have no web tool available, say so plainly and answer from your own knowledge with that caveat — do not invent sources, data, or URLs.',
|
|
70
74
|
'- Do not invent command output or results. If you cannot complete the task, say what blocked you.',
|
|
71
75
|
'',
|
|
72
|
-
// The same working rules the parent
|
|
76
|
+
// The same working rules the parent sessions under. A delegated task edits the
|
|
73
77
|
// same repository, and a child that reads a file before editing it while
|
|
74
78
|
// the parent does not is the same defect in the other direction.
|
|
75
79
|
NAMZU_WORKING_DOCTRINE,
|
|
76
80
|
].join('\n');
|
|
77
|
-
/** Recheck the invoking
|
|
81
|
+
/** Recheck the invoking turn before admitting work through a retained gateway. */
|
|
78
82
|
class ParentTaskScheduler extends LocalTaskScheduler {
|
|
79
83
|
validateParent;
|
|
80
84
|
constructor(manager, context, onEvent, validateParent) {
|
|
@@ -106,22 +110,22 @@ class ParentTaskScheduler extends LocalTaskScheduler {
|
|
|
106
110
|
async waitForTask(taskId) {
|
|
107
111
|
const task = this.getTask(taskId);
|
|
108
112
|
if (!task)
|
|
109
|
-
throw new Error(`Task ${taskId} does not belong to this parent
|
|
113
|
+
throw new Error(`Task ${taskId} does not belong to this parent turn`);
|
|
110
114
|
if (isTerminalAgentTaskState(task.state))
|
|
111
115
|
return task;
|
|
112
116
|
return super.waitForTask(taskId);
|
|
113
117
|
}
|
|
114
118
|
async continueTask(taskId, message) {
|
|
115
119
|
if (!this.owns(taskId))
|
|
116
|
-
throw new Error(`Task ${taskId} does not belong to this parent
|
|
120
|
+
throw new Error(`Task ${taskId} does not belong to this parent turn`);
|
|
117
121
|
await this.validateParent();
|
|
118
122
|
await super.continueTask(taskId, message);
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
|
-
/** A delegated
|
|
125
|
+
/** A delegated child may never inherit the SDK's headless auto-approval fallback. */
|
|
122
126
|
const refuseUnownedChildReview = async () => ({
|
|
123
127
|
action: 'abort',
|
|
124
|
-
reason: 'The parent
|
|
128
|
+
reason: 'The parent turn no longer owns an interactive review channel for this sub-agent.',
|
|
125
129
|
});
|
|
126
130
|
/**
|
|
127
131
|
* Stand up the AgentManager + gateway + `Agent` tool. Returns the tool to
|
|
@@ -147,10 +151,18 @@ export async function createSubagentRuntime(opts) {
|
|
|
147
151
|
.join('; ');
|
|
148
152
|
const parents = new Map();
|
|
149
153
|
const sessions = new Map();
|
|
154
|
+
// A ledger this runtime keeps for itself, when neither a shared store nor a
|
|
155
|
+
// project layout was given. Process-local: it bounds the children of one
|
|
156
|
+
// root turn, and the parent's own spend is counted wherever the parent
|
|
157
|
+
// keeps its ledger.
|
|
158
|
+
const tokenBudgetStore = opts.tokenBudgetStore ??
|
|
159
|
+
(opts.paths
|
|
160
|
+
? new DiskSessionTokenBudgetStore({ paths: opts.paths })
|
|
161
|
+
: new InMemorySessionTokenBudgetStore());
|
|
150
162
|
let closed = false;
|
|
151
163
|
const sessionKey = ({ project, sessionId }) => JSON.stringify([project.tenantId, project.id, sessionId]);
|
|
152
|
-
const resolveParent = async (
|
|
153
|
-
const parent = structuredClone(await opts.resolveParent(
|
|
164
|
+
const resolveParent = async (turnId) => {
|
|
165
|
+
const parent = structuredClone(await opts.resolveParent(turnId));
|
|
154
166
|
const { project, topic } = parent;
|
|
155
167
|
if (topic.projectId !== project.id || topic.tenantId !== project.tenantId) {
|
|
156
168
|
throw new Error('Delegation parent topic does not belong to its project and tenant');
|
|
@@ -174,7 +186,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
174
186
|
}, tenantId);
|
|
175
187
|
await store.updateSession({ ...session, status: 'active' }, tenantId);
|
|
176
188
|
const manager = new AgentManager(registry, {
|
|
177
|
-
childTimeoutMs: opts.timeoutMs ??
|
|
189
|
+
childTimeoutMs: opts.timeoutMs ?? CLI_INTERACTIVE_TURN_TIMEOUT_MS,
|
|
178
190
|
capacityBehavior: 'queue',
|
|
179
191
|
}, {
|
|
180
192
|
sessionStore: store,
|
|
@@ -195,7 +207,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
195
207
|
};
|
|
196
208
|
const refreshLimits = async (shared, parent) => {
|
|
197
209
|
if (shared.topicId !== parent.topic.id)
|
|
198
|
-
throw new Error('Delegation parent changed its topic while
|
|
210
|
+
throw new Error('Delegation parent changed its topic while turns are active');
|
|
199
211
|
// A slow metadata read must not overwrite a newer project snapshot.
|
|
200
212
|
const updatedAt = parent.project.updatedAt.getTime();
|
|
201
213
|
if (updatedAt < shared.projectUpdatedAt)
|
|
@@ -234,24 +246,23 @@ export async function createSubagentRuntime(opts) {
|
|
|
234
246
|
throw error;
|
|
235
247
|
}
|
|
236
248
|
};
|
|
237
|
-
const
|
|
249
|
+
const gatewayForTurn = async (turnId) => {
|
|
238
250
|
if (closed)
|
|
239
251
|
throw new Error('Sub-agent runtime is closed');
|
|
240
|
-
let pending = parents.get(
|
|
252
|
+
let pending = parents.get(turnId);
|
|
241
253
|
if (!pending) {
|
|
242
254
|
pending = (async () => {
|
|
243
|
-
const parent = await resolveParent(
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
},
|
|
251
|
-
limit:
|
|
252
|
-
pathBuilder: opts.pathBuilder,
|
|
253
|
-
workingDirectory: opts.cwd,
|
|
255
|
+
const parent = await resolveParent(turnId);
|
|
256
|
+
// The invoking turn is the root of this delegation tree, so its
|
|
257
|
+
// ledger is keyed `(parent session, invoking turn)` — the ledger the
|
|
258
|
+
// parent's own `query()` opened for this turn when it shares the
|
|
259
|
+
// store. Every child spends from an account reserved under it.
|
|
260
|
+
const budget = await openSessionTokenBudget({
|
|
261
|
+
store: tokenBudgetStore,
|
|
262
|
+
scope: { rootSessionId: parent.sessionId, rootTurnId: turnId },
|
|
263
|
+
limit: resolveTurnGuards(opts, opts.resolveLimits?.(turnId)).tokenBudget,
|
|
254
264
|
});
|
|
265
|
+
budget.bindTurn(parent.sessionId, turnId);
|
|
255
266
|
const lease = await acquireSession(parent);
|
|
256
267
|
const { manager } = lease.shared;
|
|
257
268
|
const parentAbortController = new AbortController();
|
|
@@ -260,12 +271,13 @@ export async function createSubagentRuntime(opts) {
|
|
|
260
271
|
if (closed ||
|
|
261
272
|
released ||
|
|
262
273
|
parentAbortController.signal.aborted ||
|
|
263
|
-
parents.get(
|
|
264
|
-
throw new Error(`Parent
|
|
274
|
+
parents.get(turnId) !== pending) {
|
|
275
|
+
throw new Error(`Parent turn ${turnId} was released`);
|
|
265
276
|
}
|
|
266
277
|
};
|
|
267
278
|
const taskContext = {
|
|
268
|
-
|
|
279
|
+
parentSessionId: parent.sessionId,
|
|
280
|
+
parentTurnId: turnId,
|
|
269
281
|
parentAgentId: 'namzu',
|
|
270
282
|
parentAbortController,
|
|
271
283
|
depth: 0,
|
|
@@ -282,10 +294,10 @@ export async function createSubagentRuntime(opts) {
|
|
|
282
294
|
};
|
|
283
295
|
const gateway = new ParentTaskScheduler(manager, taskContext, opts.onEvent, async () => {
|
|
284
296
|
assertOwned();
|
|
285
|
-
const current = await resolveParent(
|
|
297
|
+
const current = await resolveParent(turnId);
|
|
286
298
|
assertOwned();
|
|
287
299
|
if (sessionKey(current) !== sessionKey(parent))
|
|
288
|
-
throw new Error('Delegation
|
|
300
|
+
throw new Error('Delegation turn changed its parent scope');
|
|
289
301
|
await refreshLimits(lease.shared, current);
|
|
290
302
|
assertOwned();
|
|
291
303
|
});
|
|
@@ -298,8 +310,13 @@ export async function createSubagentRuntime(opts) {
|
|
|
298
310
|
if (released)
|
|
299
311
|
return;
|
|
300
312
|
released = true;
|
|
301
|
-
parentAbortController.abort(new
|
|
302
|
-
|
|
313
|
+
parentAbortController.abort(new TurnCancelled('parent'));
|
|
314
|
+
// Only this turn's children. The manager is shared by every
|
|
315
|
+
// turn of the session, and `cancelAll` would reach them all.
|
|
316
|
+
for (const task of manager.listByParent(parent.sessionId)) {
|
|
317
|
+
if (task.context.parentTurnId === turnId)
|
|
318
|
+
manager.cancel(task.taskId, 'parent');
|
|
319
|
+
}
|
|
303
320
|
completionInbox.close();
|
|
304
321
|
lease.release();
|
|
305
322
|
},
|
|
@@ -310,32 +327,32 @@ export async function createSubagentRuntime(opts) {
|
|
|
310
327
|
}
|
|
311
328
|
return runtime;
|
|
312
329
|
})();
|
|
313
|
-
parents.set(
|
|
330
|
+
parents.set(turnId, pending);
|
|
314
331
|
}
|
|
315
332
|
try {
|
|
316
333
|
const runtime = await pending;
|
|
317
|
-
if (closed || parents.get(
|
|
318
|
-
throw new Error(`Parent
|
|
334
|
+
if (closed || parents.get(turnId) !== pending)
|
|
335
|
+
throw new Error(`Parent turn ${turnId} was released`);
|
|
319
336
|
return runtime.gateway;
|
|
320
337
|
}
|
|
321
338
|
catch (error) {
|
|
322
|
-
if (parents.get(
|
|
323
|
-
parents.delete(
|
|
339
|
+
if (parents.get(turnId) === pending)
|
|
340
|
+
parents.delete(turnId);
|
|
324
341
|
throw error;
|
|
325
342
|
}
|
|
326
343
|
};
|
|
327
|
-
const
|
|
328
|
-
const pending = parents.get(
|
|
344
|
+
const releaseTurn = async (turnId) => {
|
|
345
|
+
const pending = parents.get(turnId);
|
|
329
346
|
if (!pending)
|
|
330
347
|
return;
|
|
331
|
-
parents.delete(
|
|
348
|
+
parents.delete(turnId);
|
|
332
349
|
await pending.then((runtime) => runtime.close(), () => undefined);
|
|
333
350
|
};
|
|
334
|
-
const
|
|
335
|
-
await
|
|
336
|
-
const pending = parents.get(
|
|
351
|
+
const completionInboxForTurn = async (turnId) => {
|
|
352
|
+
await gatewayForTurn(turnId);
|
|
353
|
+
const pending = parents.get(turnId);
|
|
337
354
|
if (!pending)
|
|
338
|
-
throw new Error(`Parent
|
|
355
|
+
throw new Error(`Parent turn ${turnId} was released`);
|
|
339
356
|
return (await pending).completionInbox;
|
|
340
357
|
};
|
|
341
358
|
const activity = new SubagentActivityMonitor();
|
|
@@ -428,7 +445,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
428
445
|
readOnly: false,
|
|
429
446
|
destructive: false,
|
|
430
447
|
concurrencySafe: true,
|
|
431
|
-
timeoutMs: opts.resolveLimits ? 0 : (opts.timeoutMs ??
|
|
448
|
+
timeoutMs: opts.resolveLimits ? 0 : (opts.timeoutMs ?? CLI_INTERACTIVE_TURN_TIMEOUT_MS),
|
|
432
449
|
async execute(input, context) {
|
|
433
450
|
const { description, prompt, subagent_type, role, workflow, phase, phase_order, phase_detail, run_in_background, model: requestedModel, provider: requestedProvider, effort: requestedEffort, } = input;
|
|
434
451
|
if (!requestedModel && (requestedProvider || requestedEffort))
|
|
@@ -469,25 +486,25 @@ export async function createSubagentRuntime(opts) {
|
|
|
469
486
|
prompt,
|
|
470
487
|
batchId: context.toolBatchId,
|
|
471
488
|
toolUseId: context.toolUseId,
|
|
472
|
-
workflowId: String(context.
|
|
489
|
+
workflowId: String(context.turnId),
|
|
473
490
|
workflow,
|
|
474
491
|
phase,
|
|
475
492
|
phaseOrder: phase_order,
|
|
476
493
|
phaseDetail: phase_detail,
|
|
477
494
|
});
|
|
478
|
-
// The child is a separate
|
|
495
|
+
// The child is a separate session, but its human authority belongs to the
|
|
479
496
|
// parent turn that invoked Agent. `drainQuery` deliberately auto-approves
|
|
480
497
|
// when a handler is omitted for headless SDK callers; omission here would
|
|
481
498
|
// therefore turn a missing/stale parent mapping into permission to mutate
|
|
482
499
|
// the real project. Always install a handler, and abort if ownership can no
|
|
483
500
|
// longer be proved.
|
|
484
|
-
const resumeHandler = opts.resolveResumeHandler?.(context.
|
|
501
|
+
const resumeHandler = opts.resolveResumeHandler?.(context.turnId) ?? refuseUnownedChildReview;
|
|
485
502
|
const configOverrides = {
|
|
486
|
-
...
|
|
503
|
+
...resolveTurnGuards(opts, opts.resolveLimits?.(context.turnId)),
|
|
487
504
|
...(selection ? { model: selection.model, effort: selection.effort } : {}),
|
|
488
505
|
...(Object.keys(context.env ?? {}).length > 0 ? { env: context.env } : {}),
|
|
489
|
-
// The parent
|
|
490
|
-
// connected answer the way the
|
|
506
|
+
// The parent turn's tool-result screens, so a sub-agent judges a
|
|
507
|
+
// connected answer the way the turn that asked for the work
|
|
491
508
|
// does. Without it the child's executor installs the shipped
|
|
492
509
|
// default, and `toolResultScreens: []` — the operator's off
|
|
493
510
|
// switch — would hold for the parent and not for anything it
|
|
@@ -507,32 +524,19 @@ export async function createSubagentRuntime(opts) {
|
|
|
507
524
|
registry.unregister(agentId);
|
|
508
525
|
};
|
|
509
526
|
try {
|
|
510
|
-
const completionInbox = await
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
? new DelegationHistory(opts.historyRoot, parent.sessionId)
|
|
514
|
-
: undefined;
|
|
515
|
-
const save = (handle, terminal) => {
|
|
516
|
-
history?.write({
|
|
517
|
-
taskId: handle.taskId,
|
|
518
|
-
parentRunId: context.runId,
|
|
519
|
-
description,
|
|
520
|
-
status: terminal ? agentTaskOutcome(handle) : 'unresolved',
|
|
521
|
-
...(terminal ? { output: String(completedAgentResult(handle).output ?? '') } : {}),
|
|
522
|
-
});
|
|
523
|
-
};
|
|
527
|
+
const completionInbox = await completionInboxForTurn(context.turnId);
|
|
528
|
+
// Nothing is saved here: the SDK records the child's spawn and its
|
|
529
|
+
// ending in the parent's log, and the child's own log beside it.
|
|
524
530
|
const outcome = await runBlockingAgentTask({
|
|
525
|
-
gateway: await
|
|
531
|
+
gateway: await gatewayForTurn(context.turnId),
|
|
526
532
|
signal: context.abortSignal,
|
|
527
|
-
waitForInbound: opts.resolveWaitForInbound?.(context.
|
|
533
|
+
waitForInbound: opts.resolveWaitForInbound?.(context.turnId),
|
|
528
534
|
background: run_in_background === true,
|
|
529
535
|
completionInbox,
|
|
530
|
-
onCreated: (
|
|
531
|
-
save(handle, false);
|
|
536
|
+
onCreated: () => {
|
|
532
537
|
taskOwnsCleanup = true;
|
|
533
538
|
},
|
|
534
539
|
onSettled: (completed) => {
|
|
535
|
-
save(completed, true);
|
|
536
540
|
tracker.settle(completed);
|
|
537
541
|
},
|
|
538
542
|
onFailed: (error) => tracker.fail(error),
|
|
@@ -551,7 +555,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
551
555
|
...(phase ? { phase } : {}),
|
|
552
556
|
...(phase_detail ? { phaseDetail: phase_detail } : {}),
|
|
553
557
|
...(phase_order !== undefined ? { phaseOrder: phase_order } : {}),
|
|
554
|
-
// Hang the child
|
|
558
|
+
// Hang the child session off THIS tool's span, so the delegation
|
|
555
559
|
// shows up inside the turn that asked for it. Without it a
|
|
556
560
|
// sub-agent opens its OWN root trace, and the one structure
|
|
557
561
|
// a delegation trace exists to record — who dispatched whom
|
|
@@ -589,10 +593,10 @@ export async function createSubagentRuntime(opts) {
|
|
|
589
593
|
});
|
|
590
594
|
const agentTaskListTool = defineTool({
|
|
591
595
|
name: 'agent_task_list',
|
|
592
|
-
description: 'List the agent invocations launched by this
|
|
596
|
+
description: 'List the agent invocations launched by this turn 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 agents saved from earlier turns of this conversation, optionally session_id for one saved result; saved agents are not live.',
|
|
593
597
|
inputSchema: mcpJsonSchemaToZod({
|
|
594
598
|
type: 'object',
|
|
595
|
-
properties: { history: { type: 'boolean' },
|
|
599
|
+
properties: { history: { type: 'boolean' }, session_id: { type: 'string' } },
|
|
596
600
|
additionalProperties: false,
|
|
597
601
|
}),
|
|
598
602
|
category: 'custom',
|
|
@@ -602,28 +606,27 @@ export async function createSubagentRuntime(opts) {
|
|
|
602
606
|
concurrencySafe: true,
|
|
603
607
|
async execute(input, context) {
|
|
604
608
|
const request = input;
|
|
605
|
-
if (request.history || request.
|
|
606
|
-
if (!opts.
|
|
609
|
+
if (request.history || request.session_id) {
|
|
610
|
+
if (!opts.savedAgents)
|
|
607
611
|
return {
|
|
608
612
|
success: false,
|
|
609
613
|
output: 'Saved delegation history is unavailable in this host.',
|
|
610
614
|
};
|
|
611
|
-
const parent = await resolveParent(context.
|
|
612
|
-
const history =
|
|
613
|
-
const saved = request.
|
|
614
|
-
? {
|
|
615
|
-
: history.list();
|
|
616
|
-
const tasks = saved.tasks.map(({ output, ...row }) => request.task_id ? { ...row, output } : row);
|
|
615
|
+
const parent = await resolveParent(context.turnId);
|
|
616
|
+
const history = opts.savedAgents(parent.sessionId);
|
|
617
|
+
const saved = request.session_id
|
|
618
|
+
? { agents: [await history.read(request.session_id)], omitted: 0 }
|
|
619
|
+
: await history.list();
|
|
617
620
|
return {
|
|
618
621
|
success: true,
|
|
619
622
|
output: JSON.stringify({
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
guidance:
|
|
623
|
+
agents: saved.agents,
|
|
624
|
+
omitted: saved.omitted,
|
|
625
|
+
guidance: SAVED_AGENTS_GUIDANCE,
|
|
623
626
|
}),
|
|
624
627
|
};
|
|
625
628
|
}
|
|
626
|
-
const gateway = await
|
|
629
|
+
const gateway = await gatewayForTurn(context.turnId);
|
|
627
630
|
const tasks = gateway.listTasks();
|
|
628
631
|
const labels = new Map(activity.getSnapshot().map((entry) => [entry.taskId, entry.description]));
|
|
629
632
|
const shown = tasks.slice(-40).map((task) => ({
|
|
@@ -651,7 +654,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
651
654
|
});
|
|
652
655
|
const waitForTaskTool = defineTool({
|
|
653
656
|
name: 'wait_for_task',
|
|
654
|
-
description: 'Wait for a task this
|
|
657
|
+
description: 'Wait for a task this turn 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.',
|
|
655
658
|
inputSchema: mcpJsonSchemaToZod({
|
|
656
659
|
type: 'object',
|
|
657
660
|
properties: {
|
|
@@ -667,9 +670,9 @@ export async function createSubagentRuntime(opts) {
|
|
|
667
670
|
readOnly: true,
|
|
668
671
|
destructive: false,
|
|
669
672
|
concurrencySafe: true,
|
|
670
|
-
timeoutMs: opts.resolveLimits ? 0 : (opts.timeoutMs ??
|
|
673
|
+
timeoutMs: opts.resolveLimits ? 0 : (opts.timeoutMs ?? CLI_INTERACTIVE_TURN_TIMEOUT_MS),
|
|
671
674
|
async execute(input, context) {
|
|
672
|
-
const gateway = await
|
|
675
|
+
const gateway = await gatewayForTurn(context.turnId);
|
|
673
676
|
let taskId;
|
|
674
677
|
try {
|
|
675
678
|
taskId = asTaskId(input.task_id);
|
|
@@ -686,14 +689,14 @@ export async function createSubagentRuntime(opts) {
|
|
|
686
689
|
return {
|
|
687
690
|
success: false,
|
|
688
691
|
output: '',
|
|
689
|
-
error: `Task ${taskId} does not belong to this parent
|
|
692
|
+
error: `Task ${taskId} does not belong to this parent turn.`,
|
|
690
693
|
};
|
|
691
694
|
const outcome = await runBlockingAgentTask({
|
|
692
695
|
gateway,
|
|
693
696
|
task,
|
|
694
697
|
signal: context.abortSignal,
|
|
695
|
-
completionInbox: await
|
|
696
|
-
waitForInbound: opts.resolveWaitForInbound?.(context.
|
|
698
|
+
completionInbox: await completionInboxForTurn(context.turnId),
|
|
699
|
+
waitForInbound: opts.resolveWaitForInbound?.(context.turnId),
|
|
697
700
|
onCreated: () => { },
|
|
698
701
|
onSettled: () => { },
|
|
699
702
|
onFailed: () => { },
|
|
@@ -715,7 +718,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
715
718
|
});
|
|
716
719
|
const sendMessageTool = defineTool({
|
|
717
720
|
name: 'send_message',
|
|
718
|
-
description: 'Queue a correction or additional context for a running or queued sub-agent task owned by this
|
|
721
|
+
description: 'Queue a correction or additional context for a running or queued sub-agent task owned by this turn. The child receives it at its next request boundary; acceptance is not delivery. This does not start new work or restart finished tasks.',
|
|
719
722
|
inputSchema: mcpJsonSchemaToZod({
|
|
720
723
|
type: 'object',
|
|
721
724
|
properties: {
|
|
@@ -748,13 +751,13 @@ export async function createSubagentRuntime(opts) {
|
|
|
748
751
|
error: 'Message must not be blank.',
|
|
749
752
|
};
|
|
750
753
|
const taskId = asTaskId(task_id);
|
|
751
|
-
const gateway = await
|
|
754
|
+
const gateway = await gatewayForTurn(context.turnId);
|
|
752
755
|
const task = gateway.getTask(taskId);
|
|
753
756
|
if (!task)
|
|
754
757
|
return {
|
|
755
758
|
success: false,
|
|
756
759
|
output: '',
|
|
757
|
-
error: `Task ${taskId} does not belong to this parent
|
|
760
|
+
error: `Task ${taskId} does not belong to this parent turn.`,
|
|
758
761
|
};
|
|
759
762
|
if (isTerminalAgentTaskState(task.state))
|
|
760
763
|
return {
|
|
@@ -777,7 +780,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
777
780
|
});
|
|
778
781
|
const cancelAgentTool = defineTool({
|
|
779
782
|
name: 'cancel_agent',
|
|
780
|
-
description: 'Request cancellation of one running or queued agent task owned by this
|
|
783
|
+
description: 'Request cancellation of one running or queued agent task owned by this turn. 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.',
|
|
781
784
|
inputSchema: mcpJsonSchemaToZod({
|
|
782
785
|
type: 'object',
|
|
783
786
|
properties: {
|
|
@@ -797,13 +800,13 @@ export async function createSubagentRuntime(opts) {
|
|
|
797
800
|
async execute(input, context) {
|
|
798
801
|
context.abortSignal.throwIfAborted();
|
|
799
802
|
const taskId = asTaskId(input.task_id);
|
|
800
|
-
const gateway = await
|
|
803
|
+
const gateway = await gatewayForTurn(context.turnId);
|
|
801
804
|
const task = gateway.getTask(taskId);
|
|
802
805
|
if (!task)
|
|
803
806
|
return {
|
|
804
807
|
success: false,
|
|
805
808
|
output: '',
|
|
806
|
-
error: `Task ${taskId} does not belong to this parent
|
|
809
|
+
error: `Task ${taskId} does not belong to this parent turn.`,
|
|
807
810
|
};
|
|
808
811
|
if (isTerminalAgentTaskState(task.state))
|
|
809
812
|
return {
|
|
@@ -855,7 +858,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
855
858
|
// for the operator who would otherwise be asked to approve being shown
|
|
856
859
|
// it — a consent dialog per line of commentary is a tool nobody would
|
|
857
860
|
// call. This is not a claim that the line is ephemeral: the kernel
|
|
858
|
-
// records THIS call in the
|
|
861
|
+
// records THIS call in the session log and checkpoints exactly as
|
|
859
862
|
// it records every other, so the text comes back into the model's own
|
|
860
863
|
// history on a resume even though the band it was shown in does not.
|
|
861
864
|
// Narration's parent-only boundary does not rest on this flag either:
|
|
@@ -901,7 +904,7 @@ export async function createSubagentRuntime(opts) {
|
|
|
901
904
|
if (closePromise)
|
|
902
905
|
return closePromise;
|
|
903
906
|
closed = true;
|
|
904
|
-
closePromise = Promise.all([...parents.keys()].map(
|
|
907
|
+
closePromise = Promise.all([...parents.keys()].map(releaseTurn)).then(() => activity.close());
|
|
905
908
|
return closePromise;
|
|
906
909
|
};
|
|
907
910
|
const listModels = opts.listModels;
|
|
@@ -934,9 +937,9 @@ export async function createSubagentRuntime(opts) {
|
|
|
934
937
|
return {
|
|
935
938
|
cancelAgentTool,
|
|
936
939
|
modelCatalogueTool,
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
+
gatewayForTurn,
|
|
941
|
+
completionInboxForTurn,
|
|
942
|
+
releaseTurn,
|
|
940
943
|
agentTool,
|
|
941
944
|
waitForTaskTool,
|
|
942
945
|
agentTaskListTool,
|
|
@@ -1042,24 +1045,24 @@ async function runBlockingAgentTask(input) {
|
|
|
1042
1045
|
}
|
|
1043
1046
|
}
|
|
1044
1047
|
function agentTaskOutcome(task) {
|
|
1045
|
-
const
|
|
1046
|
-
if (
|
|
1047
|
-
return
|
|
1048
|
-
if (task.state === 'completed' &&
|
|
1048
|
+
const result = task.result;
|
|
1049
|
+
if (result?.status && result.status !== 'completed')
|
|
1050
|
+
return result.status;
|
|
1051
|
+
if (task.state === 'completed' && result?.stopReason && result.stopReason !== 'end_turn')
|
|
1049
1052
|
return 'incomplete';
|
|
1050
1053
|
return task.state;
|
|
1051
1054
|
}
|
|
1052
1055
|
/** Lifecycle completion is not proof the requested task finished successfully. */
|
|
1053
1056
|
function completedAgentResult(completed) {
|
|
1054
|
-
const
|
|
1057
|
+
const turn = completed.result;
|
|
1055
1058
|
const status = agentTaskOutcome(completed);
|
|
1056
1059
|
const succeeded = status === 'completed';
|
|
1057
|
-
const value =
|
|
1060
|
+
const value = turn?.structuredOutput ?? turn?.result;
|
|
1058
1061
|
let resultText = typeof value === 'string' ? value : value === undefined ? '' : JSON.stringify(value);
|
|
1059
|
-
if (!resultText &&
|
|
1060
|
-
// A hard limit can stop between tool rounds without setting
|
|
1062
|
+
if (!resultText && turn?.stopReason && turn.stopReason !== 'end_turn') {
|
|
1063
|
+
// A hard limit can stop between tool rounds without setting Turn.result.
|
|
1061
1064
|
// Keep the child's last visible statement, never its reasoning or tool data.
|
|
1062
|
-
const partial = [...
|
|
1065
|
+
const partial = [...turn.messages]
|
|
1063
1066
|
.reverse()
|
|
1064
1067
|
.find((message) => message.role === 'assistant' &&
|
|
1065
1068
|
typeof message.content === 'string' &&
|
|
@@ -1067,8 +1070,8 @@ function completedAgentResult(completed) {
|
|
|
1067
1070
|
if (typeof partial === 'string')
|
|
1068
1071
|
resultText = partial;
|
|
1069
1072
|
}
|
|
1070
|
-
const stopNote =
|
|
1071
|
-
? `Sub-agent
|
|
1073
|
+
const stopNote = turn?.stopReason && turn.stopReason !== 'end_turn'
|
|
1074
|
+
? `Sub-agent turn ended with stop reason "${turn.stopReason}". Output may be partial; this does not establish task completion.\n\n`
|
|
1072
1075
|
: '';
|
|
1073
1076
|
// ToolResult.data is host metadata, not necessarily model-visible content.
|
|
1074
1077
|
// Keep the handle and terminal status separate from arbitrary child output
|
|
@@ -1079,14 +1082,14 @@ function completedAgentResult(completed) {
|
|
|
1079
1082
|
output,
|
|
1080
1083
|
...(!succeeded
|
|
1081
1084
|
? {
|
|
1082
|
-
error: `Sub-agent ${completed.agentId} ${status}: ${
|
|
1085
|
+
error: `Sub-agent ${completed.agentId} ${status}: ${turn?.lastError ?? ''}\n${output}`,
|
|
1083
1086
|
}
|
|
1084
1087
|
: {}),
|
|
1085
1088
|
data: {
|
|
1086
1089
|
task_id: completed.taskId,
|
|
1087
1090
|
state: completed.state,
|
|
1088
1091
|
status,
|
|
1089
|
-
...(
|
|
1092
|
+
...(turn?.stopReason ? { stop_reason: turn.stopReason } : {}),
|
|
1090
1093
|
},
|
|
1091
1094
|
};
|
|
1092
1095
|
}
|
|
@@ -1125,17 +1128,17 @@ model = opts.model, selection) {
|
|
|
1125
1128
|
// erased Agent<BaseAgentConfig,…>. configBuilder supplies the richer config.
|
|
1126
1129
|
typedAgent: agent,
|
|
1127
1130
|
configBuilder: async (options) => {
|
|
1128
|
-
const limits =
|
|
1131
|
+
const limits = resolveTurnGuards(opts, options.parentTurnId ? opts.resolveLimits?.(asTurnId(options.parentTurnId)) : undefined);
|
|
1129
1132
|
// Resolved HERE, per child, rather than captured once for the session:
|
|
1130
1133
|
// what day it is and which branch is checked out can both have changed
|
|
1131
1134
|
// since the parent started, and a sub-agent asserting the stale answer
|
|
1132
1135
|
// is worse than one that was never told.
|
|
1133
1136
|
const environment = opts.readEnvironment ? await opts.readEnvironment() : null;
|
|
1134
|
-
// AgentManager supplies the actual parent
|
|
1137
|
+
// AgentManager supplies the actual parent turn before it stamps the child's
|
|
1135
1138
|
// own Session ID. All delegated work shares that invoking conversation's
|
|
1136
1139
|
// upstream billing session, even after the TUI moves to another one.
|
|
1137
|
-
const parent = options.
|
|
1138
|
-
? await opts.resolveParent(
|
|
1140
|
+
const parent = options.parentTurnId
|
|
1141
|
+
? await opts.resolveParent(asTurnId(options.parentTurnId))
|
|
1139
1142
|
: undefined;
|
|
1140
1143
|
const provider = await opts.buildProvider(parent?.sessionId, selection);
|
|
1141
1144
|
const registry = tools();
|
|
@@ -1143,8 +1146,9 @@ model = opts.model, selection) {
|
|
|
1143
1146
|
return {
|
|
1144
1147
|
model: options.model ?? model,
|
|
1145
1148
|
tokenBudget: options.tokenBudget ?? opts.tokenBudget ?? 0,
|
|
1146
|
-
timeoutMs: options.timeoutMs ?? opts.timeoutMs ??
|
|
1149
|
+
timeoutMs: options.timeoutMs ?? opts.timeoutMs ?? CLI_INTERACTIVE_TURN_TIMEOUT_MS,
|
|
1147
1150
|
maxIterations: limits.maxIterations,
|
|
1151
|
+
pruneKeepLast: CLI_CHECKPOINT_RETENTION,
|
|
1148
1152
|
provider,
|
|
1149
1153
|
...(webSearch ? { webSearch } : {}),
|
|
1150
1154
|
...(selection?.effort ? { effort: selection.effort } : {}),
|
|
@@ -1169,7 +1173,9 @@ model = opts.model, selection) {
|
|
|
1169
1173
|
...(opts.sandboxTeardownTimeoutMs !== undefined
|
|
1170
1174
|
? { sandboxTeardownTimeoutMs: opts.sandboxTeardownTimeoutMs }
|
|
1171
1175
|
: {}),
|
|
1172
|
-
...(opts.
|
|
1176
|
+
...(opts.outsideRootAccess ? { outsideRootAccess: opts.outsideRootAccess } : {}),
|
|
1177
|
+
...(opts.sandboxEscape ? { sandboxEscape: opts.sandboxEscape } : {}),
|
|
1178
|
+
...(opts.paths ? { paths: opts.paths } : {}),
|
|
1173
1179
|
};
|
|
1174
1180
|
},
|
|
1175
1181
|
};
|