@gajae-code/coding-agent 0.2.5 → 0.3.1
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 +28 -0
- package/dist/types/async/job-manager.d.ts +91 -2
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +10 -4
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/debug/crash-diagnostics.d.ts +45 -0
- package/dist/types/debug/runtime-gauges.d.ts +6 -0
- package/dist/types/deep-interview/render-middleware.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/eval/py/kernel.d.ts +2 -0
- package/dist/types/exec/bash-executor.d.ts +10 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +6 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/gjc-runtime/cli-write-receipt.d.ts +24 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +1 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +33 -0
- package/dist/types/gjc-runtime/state-renderer.d.ts +65 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/state-schema.d.ts +317 -0
- package/dist/types/gjc-runtime/state-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +147 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +43 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +54 -0
- package/dist/types/harness-control-plane/classifier.d.ts +13 -0
- package/dist/types/harness-control-plane/control-endpoint.d.ts +31 -0
- package/dist/types/harness-control-plane/finalize.d.ts +47 -0
- package/dist/types/harness-control-plane/frame-mapper.d.ts +29 -0
- package/dist/types/harness-control-plane/operate.d.ts +35 -0
- package/dist/types/harness-control-plane/owner.d.ts +46 -0
- package/dist/types/harness-control-plane/preserve.d.ts +19 -0
- package/dist/types/harness-control-plane/receipts.d.ts +88 -0
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +66 -0
- package/dist/types/harness-control-plane/seams.d.ts +21 -0
- package/dist/types/harness-control-plane/session-lease.d.ts +65 -0
- package/dist/types/harness-control-plane/state-machine.d.ts +19 -0
- package/dist/types/harness-control-plane/storage.d.ts +53 -0
- package/dist/types/harness-control-plane/types.d.ts +162 -0
- package/dist/types/hooks/skill-keywords.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +23 -29
- package/dist/types/internal-urls/agent-protocol.d.ts +2 -2
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/registry-helpers.d.ts +8 -7
- package/dist/types/internal-urls/types.d.ts +4 -0
- package/dist/types/lsp/index.d.ts +10 -10
- package/dist/types/modes/bridge/auth.d.ts +12 -0
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +9 -0
- package/dist/types/modes/bridge/bridge-mode.d.ts +44 -0
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +88 -0
- package/dist/types/modes/bridge/event-stream.d.ts +8 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +31 -0
- package/dist/types/modes/components/jobs-overlay.d.ts +30 -0
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +8 -0
- package/dist/types/modes/index.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/jobs-observer.d.ts +57 -0
- package/dist/types/modes/rpc/host-tools.d.ts +1 -16
- package/dist/types/modes/rpc/host-uris.d.ts +1 -38
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +20 -0
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +24 -0
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +46 -0
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +16 -0
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +17 -0
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +44 -0
- package/dist/types/modes/shared/agent-wire/responses.d.ts +4 -0
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +18 -0
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +42 -0
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +27 -0
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +19 -1
- package/dist/types/skill-state/active-state.d.ts +2 -0
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +1 -1
- package/dist/types/skill-state/workflow-state-contract.d.ts +25 -2
- package/dist/types/skill-state/workflow-state-version.d.ts +3 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/id.d.ts +7 -0
- package/dist/types/task/index.d.ts +5 -0
- package/dist/types/task/receipt.d.ts +85 -0
- package/dist/types/task/spawn-gate.d.ts +38 -0
- package/dist/types/task/types.d.ts +198 -14
- package/dist/types/tools/cron.d.ts +6 -0
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +26 -1
- package/package.json +7 -7
- package/scripts/build-binary.ts +7 -0
- package/src/async/job-manager.ts +334 -6
- package/src/cli/args.ts +9 -2
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/config-cli.ts +10 -2
- package/src/cli.ts +2 -0
- package/src/commands/deep-interview.ts +1 -0
- package/src/commands/harness.ts +862 -0
- package/src/commands/launch.ts +2 -2
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +54 -39
- package/src/config/keybindings.ts +6 -0
- package/src/config/settings-schema.ts +13 -3
- package/src/config/settings.ts +5 -0
- package/src/dap/client.ts +17 -3
- package/src/debug/crash-diagnostics.ts +223 -0
- package/src/debug/runtime-gauges.ts +20 -0
- package/src/deep-interview/render-middleware.ts +372 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/defaults/gjc/skills/ralplan/SKILL.md +31 -2
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +106 -13
- package/src/eval/py/executor.ts +21 -1
- package/src/eval/py/kernel.ts +15 -0
- package/src/exec/bash-executor.ts +41 -0
- package/src/extensibility/custom-tools/types.ts +1 -0
- package/src/extensibility/extensions/types.ts +6 -0
- package/src/extensibility/shared-events.ts +1 -0
- package/src/gjc-runtime/cli-write-receipt.ts +31 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +98 -42
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +235 -43
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +179 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +1155 -46
- package/src/gjc-runtime/state-schema.ts +192 -0
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +749 -0
- package/src/gjc-runtime/team-runtime.ts +1255 -189
- package/src/gjc-runtime/ultragoal-runtime.ts +460 -43
- package/src/gjc-runtime/workflow-command-ref.ts +239 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +1601 -0
- package/src/gjc-runtime/workflow-manifest.ts +427 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +148 -0
- package/src/harness-control-plane/finalize.ts +222 -0
- package/src/harness-control-plane/frame-mapper.ts +286 -0
- package/src/harness-control-plane/operate.ts +225 -0
- package/src/harness-control-plane/owner.ts +600 -0
- package/src/harness-control-plane/preserve.ts +102 -0
- package/src/harness-control-plane/receipts.ts +216 -0
- package/src/harness-control-plane/rpc-adapter.ts +276 -0
- package/src/harness-control-plane/seams.ts +39 -0
- package/src/harness-control-plane/session-lease.ts +388 -0
- package/src/harness-control-plane/state-machine.ts +98 -0
- package/src/harness-control-plane/storage.ts +257 -0
- package/src/harness-control-plane/types.ts +214 -0
- package/src/hooks/skill-keywords.ts +4 -2
- package/src/hooks/skill-state.ts +197 -64
- package/src/internal-urls/agent-protocol.ts +68 -21
- package/src/internal-urls/artifact-protocol.ts +12 -17
- package/src/internal-urls/docs-index.generated.ts +3 -2
- package/src/internal-urls/registry-helpers.ts +19 -16
- package/src/internal-urls/types.ts +4 -0
- package/src/lsp/client.ts +18 -2
- package/src/main.ts +21 -5
- package/src/modes/bridge/auth.ts +41 -0
- package/src/modes/bridge/bridge-client-bridge.ts +47 -0
- package/src/modes/bridge/bridge-mode.ts +520 -0
- package/src/modes/bridge/bridge-ui-context.ts +200 -0
- package/src/modes/bridge/event-stream.ts +70 -0
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/custom-editor.ts +101 -0
- package/src/modes/components/hook-selector.ts +133 -20
- package/src/modes/components/jobs-overlay-model.ts +109 -0
- package/src/modes/components/jobs-overlay.ts +172 -0
- package/src/modes/components/status-line/presets.ts +7 -5
- package/src/modes/components/status-line/segments.ts +25 -0
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/status-line.ts +9 -1
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +43 -1
- package/src/modes/controllers/input-controller.ts +105 -9
- package/src/modes/controllers/selector-controller.ts +31 -1
- package/src/modes/index.ts +1 -0
- package/src/modes/interactive-mode.ts +28 -0
- package/src/modes/jobs-observer.ts +204 -0
- package/src/modes/rpc/host-tools.ts +1 -186
- package/src/modes/rpc/host-uris.ts +1 -235
- package/src/modes/rpc/rpc-client.ts +25 -10
- package/src/modes/rpc/rpc-mode.ts +12 -381
- package/src/modes/shared/agent-wire/command-dispatch.ts +341 -0
- package/src/modes/shared/agent-wire/command-validation.ts +131 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +108 -0
- package/src/modes/shared/agent-wire/handshake.ts +117 -0
- package/src/modes/shared/agent-wire/host-tool-bridge.ts +194 -0
- package/src/modes/shared/agent-wire/host-uri-bridge.ts +236 -0
- package/src/modes/shared/agent-wire/protocol.ts +96 -0
- package/src/modes/shared/agent-wire/responses.ts +17 -0
- package/src/modes/shared/agent-wire/scopes.ts +89 -0
- package/src/modes/shared/agent-wire/ui-request-broker.ts +150 -0
- package/src/modes/shared/agent-wire/ui-result.ts +48 -0
- package/src/modes/types.ts +2 -0
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/tools/subagent.md +39 -4
- package/src/prompts/tools/task-summary.md +3 -9
- package/src/prompts/tools/task.md +5 -1
- package/src/sdk.ts +8 -0
- package/src/session/agent-session.ts +445 -71
- package/src/session/session-manager.ts +13 -1
- package/src/skill-state/active-state.ts +58 -65
- package/src/skill-state/deep-interview-mutation-guard.ts +114 -17
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +33 -4
- package/src/skill-state/workflow-state-version.ts +3 -0
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/task/executor.ts +79 -13
- package/src/task/id.ts +33 -0
- package/src/task/index.ts +376 -74
- package/src/task/output-manager.ts +5 -4
- package/src/task/receipt.ts +297 -0
- package/src/task/render.ts +54 -134
- package/src/task/spawn-gate.ts +132 -0
- package/src/task/types.ts +104 -10
- package/src/tools/ask.ts +88 -27
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash.ts +1 -1
- package/src/tools/cron.ts +48 -0
- package/src/tools/find.ts +4 -1
- package/src/tools/index.ts +2 -0
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/search.ts +1 -0
- package/src/tools/skill.ts +6 -1
- package/src/tools/subagent.ts +423 -79
package/src/task/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { formatBytes, formatDuration } from "../tools/render-utils";
|
|
|
31
31
|
import {
|
|
32
32
|
type AgentDefinition,
|
|
33
33
|
type AgentProgress,
|
|
34
|
+
type ForkContextMode,
|
|
34
35
|
type ForkContextPolicy,
|
|
35
36
|
getTaskSchema,
|
|
36
37
|
type SingleResult,
|
|
@@ -46,10 +47,13 @@ import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
|
46
47
|
import * as git from "../utils/git";
|
|
47
48
|
import { discoverAgents, filterVisibleAgents, getAgent } from "./discovery";
|
|
48
49
|
import { runSubprocess } from "./executor";
|
|
50
|
+
import { getTaskIdValidationError, validateAllocatedTaskId } from "./id";
|
|
49
51
|
import { AgentOutputManager } from "./output-manager";
|
|
50
52
|
import { mapWithConcurrencyLimit, Semaphore } from "./parallel";
|
|
53
|
+
import { assertNoRawTaskFields, buildTaskReceipt, buildTaskRoiSummary } from "./receipt";
|
|
51
54
|
import { renderResult, renderCall as renderTaskCall } from "./render";
|
|
52
55
|
import { getTaskSimpleModeCapabilities, type TaskSimpleMode } from "./simple-mode";
|
|
56
|
+
import { DEFAULT_SPAWN_THRESHOLD, evaluateReviewerExploreGate, evaluateSpawnGate } from "./spawn-gate";
|
|
53
57
|
import {
|
|
54
58
|
applyNestedPatches,
|
|
55
59
|
captureBaseline,
|
|
@@ -65,6 +69,18 @@ import {
|
|
|
65
69
|
type WorktreeBaseline,
|
|
66
70
|
} from "./worktree";
|
|
67
71
|
|
|
72
|
+
interface TaskResumeDescriptor {
|
|
73
|
+
toolCallId: string;
|
|
74
|
+
params: TaskParams;
|
|
75
|
+
task: TaskItem & { id: string };
|
|
76
|
+
sessionFile: string | null;
|
|
77
|
+
forkContextSeed?: ForkContextSeed;
|
|
78
|
+
agentSource: AgentDefinition["source"];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isTaskResumeDescriptor(value: unknown): value is TaskResumeDescriptor {
|
|
82
|
+
return typeof value === "object" && value !== null && "task" in value && "params" in value;
|
|
83
|
+
}
|
|
68
84
|
function renderSubagentUserPrompt(assignment: string, simpleMode: TaskSimpleMode): string {
|
|
69
85
|
return prompt.render(subagentUserPromptTemplate, {
|
|
70
86
|
assignment: assignment.trim(),
|
|
@@ -110,11 +126,37 @@ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
|
|
|
110
126
|
target.cost.total += cost.total;
|
|
111
127
|
}
|
|
112
128
|
|
|
129
|
+
function validateTaskIdsForScheduling(tasks: readonly TaskItem[]): string | undefined {
|
|
130
|
+
const invalid: string[] = [];
|
|
131
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
132
|
+
const error = getTaskIdValidationError(tasks[i]?.id);
|
|
133
|
+
if (error) invalid.push(`index ${i}: ${error}`);
|
|
134
|
+
}
|
|
135
|
+
return invalid.length > 0 ? `Invalid task ids: ${invalid.join(" ")}` : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
113
138
|
// Re-export types and utilities
|
|
114
139
|
export { loadBundledAgents as BUNDLED_AGENTS } from "./agents";
|
|
115
140
|
export { discoverCommands, expandCommand, getCommand } from "./commands";
|
|
116
141
|
export { discoverAgents, getAgent } from "./discovery";
|
|
142
|
+
export {
|
|
143
|
+
isValidAllocatedTaskId,
|
|
144
|
+
isValidTaskId,
|
|
145
|
+
TASK_ID_DESCRIPTION,
|
|
146
|
+
TASK_ID_PATTERN,
|
|
147
|
+
validateAllocatedTaskId,
|
|
148
|
+
validateTaskId,
|
|
149
|
+
} from "./id";
|
|
117
150
|
export { AgentOutputManager } from "./output-manager";
|
|
151
|
+
export type { TaskResultReceipt } from "./receipt";
|
|
152
|
+
export {
|
|
153
|
+
assertNoRawTaskFields,
|
|
154
|
+
buildTaskReceipt,
|
|
155
|
+
buildTaskRoi,
|
|
156
|
+
buildTaskRoiSummary,
|
|
157
|
+
findRawTaskLeakKeys,
|
|
158
|
+
sanitizeTaskToolDetails,
|
|
159
|
+
} from "./receipt";
|
|
118
160
|
export type {
|
|
119
161
|
AgentDefinition,
|
|
120
162
|
AgentProgress,
|
|
@@ -191,6 +233,14 @@ function validateTaskModeParams(simpleMode: TaskSimpleMode, params: TaskParams):
|
|
|
191
233
|
if (!customSchemaEnabled && params.schema !== undefined) {
|
|
192
234
|
disallowedFields.push("schema");
|
|
193
235
|
}
|
|
236
|
+
if (!contextEnabled) {
|
|
237
|
+
const inheritedTaskIds = (params.tasks ?? [])
|
|
238
|
+
.filter(task => task.inheritContext !== undefined && task.inheritContext !== "none")
|
|
239
|
+
.map(task => task.id);
|
|
240
|
+
if (inheritedTaskIds.length > 0) {
|
|
241
|
+
disallowedFields.push(`inheritContext for task(s) ${inheritedTaskIds.join(", ")}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
194
244
|
if (disallowedFields.length === 0) {
|
|
195
245
|
return undefined;
|
|
196
246
|
}
|
|
@@ -200,22 +250,77 @@ function validateTaskModeParams(simpleMode: TaskSimpleMode, params: TaskParams):
|
|
|
200
250
|
}
|
|
201
251
|
|
|
202
252
|
if (disallowedFields.length === 1) {
|
|
203
|
-
return `task.simple is set to independent, so the task tool does not accept
|
|
253
|
+
return `task.simple is set to independent, so the task tool does not accept ${disallowedFields[0].startsWith("inheritContext") ? disallowedFields[0] : `\`${disallowedFields[0]}\``}. Put everything the subagent needs inside each task assignment.`;
|
|
204
254
|
}
|
|
205
255
|
|
|
206
|
-
return
|
|
256
|
+
return `task.simple is set to independent, so the task tool does not accept ${disallowedFields.map(field => (field.startsWith("inheritContext") ? field : `\`${field}\``)).join(", ")}. Put all required background and output expectations inside each task assignment or the selected agent definition.`;
|
|
207
257
|
}
|
|
208
258
|
|
|
209
259
|
function getForkContextPolicy(agent: AgentDefinition): ForkContextPolicy {
|
|
210
260
|
return agent.forkContext ?? "forbidden";
|
|
211
261
|
}
|
|
262
|
+
const FORK_CONTEXT_MODES = [
|
|
263
|
+
"none",
|
|
264
|
+
"receipt",
|
|
265
|
+
"last-turn",
|
|
266
|
+
"bounded",
|
|
267
|
+
"full",
|
|
268
|
+
] as const satisfies readonly ForkContextMode[];
|
|
269
|
+
const FORK_CONTEXT_MODE_SET = new Set<unknown>(FORK_CONTEXT_MODES);
|
|
270
|
+
const FORK_CONTEXT_REQUEST_MODES = ["receipt", "last-turn", "bounded", "full"] as const satisfies readonly Exclude<
|
|
271
|
+
ForkContextMode,
|
|
272
|
+
"none"
|
|
273
|
+
>[];
|
|
274
|
+
const FORK_CONTEXT_REQUEST_MODE_SET = new Set<unknown>(FORK_CONTEXT_REQUEST_MODES);
|
|
275
|
+
|
|
276
|
+
function isValidForkContextMode(value: unknown): value is ForkContextMode {
|
|
277
|
+
return FORK_CONTEXT_MODE_SET.has(value);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function requestsForkContext(
|
|
281
|
+
task: Pick<TaskItem, "inheritContext">,
|
|
282
|
+
): task is TaskItem & { inheritContext: Exclude<ForkContextMode, "none"> } {
|
|
283
|
+
return FORK_CONTEXT_REQUEST_MODE_SET.has(task.inheritContext);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function resolveForkSeedParamsForMode(
|
|
287
|
+
mode: ForkContextMode,
|
|
288
|
+
configuredMaxMessages: number | undefined,
|
|
289
|
+
configuredMaxTokens: number,
|
|
290
|
+
model: Model | undefined,
|
|
291
|
+
): { maxMessages: number; maxTokens: number } | undefined {
|
|
292
|
+
const capMessages = (defaultMaxMessages: number): number =>
|
|
293
|
+
configuredMaxMessages === undefined
|
|
294
|
+
? defaultMaxMessages
|
|
295
|
+
: Math.min(defaultMaxMessages, Math.max(0, Math.trunc(configuredMaxMessages)));
|
|
296
|
+
switch (mode) {
|
|
297
|
+
case "none":
|
|
298
|
+
return undefined;
|
|
299
|
+
case "receipt":
|
|
300
|
+
return { maxMessages: 1, maxTokens: 64 };
|
|
301
|
+
case "last-turn":
|
|
302
|
+
return { maxMessages: 2, maxTokens: 250 };
|
|
303
|
+
case "bounded":
|
|
304
|
+
return { maxMessages: capMessages(50), maxTokens: 250 };
|
|
305
|
+
case "full":
|
|
306
|
+
return { maxMessages: capMessages(500), maxTokens: resolveForkContextMaxTokens(configuredMaxTokens, model) };
|
|
307
|
+
default:
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
212
311
|
|
|
213
312
|
function validateForkContextRequests(
|
|
214
313
|
tasks: readonly TaskItem[],
|
|
215
314
|
agent: AgentDefinition,
|
|
216
315
|
forkContextEnabled: boolean,
|
|
217
316
|
): string | undefined {
|
|
218
|
-
const
|
|
317
|
+
const invalidTaskIds = tasks
|
|
318
|
+
.filter(task => task.inheritContext !== undefined && !isValidForkContextMode(task.inheritContext as unknown))
|
|
319
|
+
.map(task => task.id);
|
|
320
|
+
if (invalidTaskIds.length > 0) {
|
|
321
|
+
return `Invalid inheritContext for task(s) ${invalidTaskIds.join(", ")}. Allowed modes: ${FORK_CONTEXT_MODES.join(", ")}.`;
|
|
322
|
+
}
|
|
323
|
+
const requested = tasks.filter(requestsForkContext);
|
|
219
324
|
if (requested.length === 0) return undefined;
|
|
220
325
|
const taskIds = requested.map(task => task.id).join(", ");
|
|
221
326
|
if (!forkContextEnabled) {
|
|
@@ -227,10 +332,10 @@ function validateForkContextRequests(
|
|
|
227
332
|
return undefined;
|
|
228
333
|
}
|
|
229
334
|
|
|
230
|
-
function resolveForkContextMaxTokens(configured: number, model: Model | undefined): number {
|
|
335
|
+
export function resolveForkContextMaxTokens(configured: number, model: Model | undefined): number {
|
|
231
336
|
if (configured > 0) return Math.trunc(configured);
|
|
232
337
|
const contextWindow = model?.contextWindow ?? 0;
|
|
233
|
-
return contextWindow > 0 ? Math.max(1, Math.floor(contextWindow * 0.
|
|
338
|
+
return contextWindow > 0 ? Math.max(1, Math.floor(contextWindow * 0.15)) : 15_000;
|
|
234
339
|
}
|
|
235
340
|
|
|
236
341
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -252,6 +357,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
252
357
|
readonly renderResult = renderResult;
|
|
253
358
|
readonly #discoveredAgents: AgentDefinition[];
|
|
254
359
|
readonly #blockedAgent: string | undefined;
|
|
360
|
+
readonly #spawningAgentType: string | undefined;
|
|
255
361
|
|
|
256
362
|
get parameters(): TaskToolSchemaInstance {
|
|
257
363
|
const isolationEnabled = this.session.settings.get("task.isolation.mode") !== "none";
|
|
@@ -283,6 +389,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
283
389
|
discoveredAgents: AgentDefinition[],
|
|
284
390
|
) {
|
|
285
391
|
this.#blockedAgent = $env.PI_BLOCKED_AGENT;
|
|
392
|
+
this.#spawningAgentType = session.currentAgentType;
|
|
286
393
|
this.#discoveredAgents = discoveredAgents;
|
|
287
394
|
}
|
|
288
395
|
|
|
@@ -312,10 +419,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
312
419
|
}
|
|
313
420
|
|
|
314
421
|
const taskItems = params.tasks ?? [];
|
|
422
|
+
const taskIdValidationError = validateTaskIdsForScheduling(taskItems);
|
|
423
|
+
if (taskIdValidationError) {
|
|
424
|
+
return createTaskModeError(taskIdValidationError);
|
|
425
|
+
}
|
|
315
426
|
if (taskItems.length === 0) {
|
|
316
427
|
return this.#executeSync(_toolCallId, params, signal, onUpdate);
|
|
317
428
|
}
|
|
318
|
-
|
|
319
429
|
const agent = getAgent(this.#discoveredAgents, params.agent);
|
|
320
430
|
if (!agent) {
|
|
321
431
|
const available =
|
|
@@ -353,6 +463,36 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
353
463
|
return createTaskModeError(forkContextValidationError);
|
|
354
464
|
}
|
|
355
465
|
|
|
466
|
+
const batchGateDecision = evaluateSpawnGate({ childCount: taskItems.length, plan: params.spawnPlan });
|
|
467
|
+
if (batchGateDecision.outcome === "rejected") {
|
|
468
|
+
return {
|
|
469
|
+
content: [
|
|
470
|
+
{
|
|
471
|
+
type: "text",
|
|
472
|
+
text: `Task spawn gate rejected this batch: ${batchGateDecision.reason}. Batches with more than ${DEFAULT_SPAWN_THRESHOLD} tasks require spawnPlan fields: ${batchGateDecision.missingFields.join(", ")}.`,
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
details: { projectAgentsDir: null, results: [], totalDurationMs: 0 },
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const reviewerExploreDecision = evaluateReviewerExploreGate({
|
|
480
|
+
spawningAgentType: this.#spawningAgentType,
|
|
481
|
+
targetAgent: params.agent,
|
|
482
|
+
plan: params.spawnPlan,
|
|
483
|
+
});
|
|
484
|
+
if (reviewerExploreDecision.outcome === "rejected") {
|
|
485
|
+
return {
|
|
486
|
+
content: [
|
|
487
|
+
{
|
|
488
|
+
type: "text",
|
|
489
|
+
text: `Task spawn gate rejected reviewer->explore: ${reviewerExploreDecision.reason}. Provide spawnPlan fields: ${reviewerExploreDecision.missingFields.join(", ")}.`,
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
details: { projectAgentsDir: null, results: [], totalDurationMs: 0 },
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
356
496
|
const manager = AsyncJobManager.instance();
|
|
357
497
|
if (!manager) {
|
|
358
498
|
return {
|
|
@@ -416,21 +556,75 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
416
556
|
};
|
|
417
557
|
|
|
418
558
|
const maxConcurrency = this.session.settings.get("task.maxConcurrency");
|
|
559
|
+
if (typeof manager.setResumeRunner === "function") {
|
|
560
|
+
manager.setResumeRunner((_subagentId, message, resumeDescriptor) => {
|
|
561
|
+
const descriptor = isTaskResumeDescriptor(resumeDescriptor?.data) ? resumeDescriptor.data : undefined;
|
|
562
|
+
if (!descriptor) return undefined;
|
|
563
|
+
const forkSeeds = descriptor.forkContextSeed
|
|
564
|
+
? new Map([[descriptor.task.id, descriptor.forkContextSeed]])
|
|
565
|
+
: undefined;
|
|
566
|
+
return manager.register(
|
|
567
|
+
"task",
|
|
568
|
+
descriptor.task.id,
|
|
569
|
+
async ({ signal: runSignal }) => {
|
|
570
|
+
const result = await this.#executeSync(
|
|
571
|
+
descriptor.toolCallId,
|
|
572
|
+
{ ...descriptor.params, tasks: [descriptor.task] },
|
|
573
|
+
runSignal,
|
|
574
|
+
undefined,
|
|
575
|
+
[descriptor.task.id],
|
|
576
|
+
forkSeeds,
|
|
577
|
+
{
|
|
578
|
+
runMode: message ? "message" : "resume",
|
|
579
|
+
resumeMessage: message,
|
|
580
|
+
sessionFiles: new Map([[descriptor.task.id, descriptor.sessionFile]]),
|
|
581
|
+
},
|
|
582
|
+
);
|
|
583
|
+
const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
|
|
584
|
+
const singleResult = result.details?.results[0];
|
|
585
|
+
return singleResult?.paused ? { kind: "paused" } : finalText;
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
id: `${descriptor.task.id}-resume-${Snowflake.next()}`,
|
|
589
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
590
|
+
metadata: {
|
|
591
|
+
subagent: {
|
|
592
|
+
id: descriptor.task.id,
|
|
593
|
+
agent: descriptor.params.agent,
|
|
594
|
+
agentSource: descriptor.agentSource,
|
|
595
|
+
description: descriptor.task.description,
|
|
596
|
+
assignment: descriptor.task.assignment.trim(),
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
);
|
|
601
|
+
});
|
|
602
|
+
}
|
|
419
603
|
const semaphore = new Semaphore(maxConcurrency);
|
|
420
604
|
const buildForkContextSeedForTask = async (task: TaskItem): Promise<ForkContextSeed | undefined> => {
|
|
421
|
-
if (task
|
|
605
|
+
if (!requestsForkContext(task)) return undefined;
|
|
422
606
|
if (!this.session.buildForkContextSeed) {
|
|
423
607
|
throw new Error("Current session cannot build fork-context seeds.");
|
|
424
608
|
}
|
|
425
|
-
const
|
|
609
|
+
const configuredMaxMessages = this.session.settings.has("task.forkContext.maxMessages")
|
|
610
|
+
? this.session.settings.get("task.forkContext.maxMessages")
|
|
611
|
+
: undefined;
|
|
426
612
|
const configuredMaxTokens = this.session.settings.get("task.forkContext.maxTokens");
|
|
613
|
+
const params = resolveForkSeedParamsForMode(
|
|
614
|
+
task.inheritContext,
|
|
615
|
+
configuredMaxMessages,
|
|
616
|
+
configuredMaxTokens,
|
|
617
|
+
this.session.model,
|
|
618
|
+
);
|
|
619
|
+
if (!params) return undefined;
|
|
427
620
|
return await this.session.buildForkContextSeed({
|
|
428
|
-
|
|
429
|
-
maxTokens: resolveForkContextMaxTokens(configuredMaxTokens, this.session.model),
|
|
621
|
+
...params,
|
|
430
622
|
signal,
|
|
431
623
|
});
|
|
432
624
|
};
|
|
433
625
|
const frozenForkSeeds = new Map<string, ForkContextSeed>();
|
|
626
|
+
const parentSessionFileForBatch = this.session.getSessionFile();
|
|
627
|
+
const batchArtifactsDir = parentSessionFileForBatch ? parentSessionFileForBatch.slice(0, -6) : null;
|
|
434
628
|
|
|
435
629
|
for (let i = 0; i < taskItems.length; i++) {
|
|
436
630
|
const taskItem = taskItems[i];
|
|
@@ -443,12 +637,13 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
443
637
|
continue;
|
|
444
638
|
}
|
|
445
639
|
|
|
446
|
-
const uniqueId = uniqueIds[i];
|
|
640
|
+
const uniqueId = validateAllocatedTaskId(uniqueIds[i] ?? "");
|
|
447
641
|
const frozenForkSeed = await buildForkContextSeedForTask(taskItem);
|
|
448
642
|
if (frozenForkSeed) frozenForkSeeds.set(uniqueId, frozenForkSeed);
|
|
449
643
|
const singleParams: TaskParams = { ...params, tasks: [taskItem] };
|
|
450
644
|
const label = uniqueId;
|
|
451
645
|
try {
|
|
646
|
+
const subtaskSessionFile = batchArtifactsDir ? path.join(batchArtifactsDir, `${uniqueId}.jsonl`) : null;
|
|
452
647
|
const jobId = manager.register(
|
|
453
648
|
"task",
|
|
454
649
|
label,
|
|
@@ -478,22 +673,32 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
478
673
|
undefined,
|
|
479
674
|
[uniqueId],
|
|
480
675
|
frozenForkSeeds,
|
|
676
|
+
{
|
|
677
|
+
sessionFiles: new Map([[uniqueId, subtaskSessionFile]]),
|
|
678
|
+
},
|
|
481
679
|
);
|
|
482
680
|
const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
|
|
483
681
|
const singleResult = result.details?.results[0];
|
|
484
682
|
if (progress) {
|
|
485
|
-
progress.status = singleResult?.
|
|
486
|
-
? "
|
|
487
|
-
:
|
|
488
|
-
? "
|
|
489
|
-
: "
|
|
683
|
+
progress.status = singleResult?.paused
|
|
684
|
+
? "paused"
|
|
685
|
+
: singleResult?.aborted
|
|
686
|
+
? "aborted"
|
|
687
|
+
: singleResult?.status === "completed"
|
|
688
|
+
? "completed"
|
|
689
|
+
: "failed";
|
|
490
690
|
progress.durationMs = singleResult?.durationMs ?? Math.max(0, Date.now() - startedAt);
|
|
491
691
|
progress.tokens = singleResult?.tokens ?? 0;
|
|
492
692
|
progress.contextTokens = singleResult?.contextTokens;
|
|
493
693
|
progress.contextWindow = singleResult?.contextWindow;
|
|
494
694
|
progress.cost = singleResult?.usage?.cost.total ?? 0;
|
|
495
|
-
progress.extractedToolData =
|
|
496
|
-
progress.retryFailure = singleResult?.retryFailure
|
|
695
|
+
progress.extractedToolData = undefined;
|
|
696
|
+
progress.retryFailure = singleResult?.retryFailure
|
|
697
|
+
? {
|
|
698
|
+
attempt: singleResult.retryFailure.attempt,
|
|
699
|
+
errorMessage: singleResult.retryFailure.errorSummary,
|
|
700
|
+
}
|
|
701
|
+
: undefined;
|
|
497
702
|
progress.retryState = undefined;
|
|
498
703
|
}
|
|
499
704
|
completedJobs += 1;
|
|
@@ -517,6 +722,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
517
722
|
`Background task batch complete: ${completedJobs}/${taskItems.length} finished.`,
|
|
518
723
|
);
|
|
519
724
|
}
|
|
725
|
+
if (singleResult?.paused) {
|
|
726
|
+
return { kind: "paused" };
|
|
727
|
+
}
|
|
520
728
|
return finalText;
|
|
521
729
|
} catch (error) {
|
|
522
730
|
if (progress) {
|
|
@@ -568,6 +776,31 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
568
776
|
},
|
|
569
777
|
);
|
|
570
778
|
startedJobs.push({ jobId, taskId: taskItem.id });
|
|
779
|
+
if (typeof manager.registerResumeDescriptor === "function") {
|
|
780
|
+
manager.registerResumeDescriptor({
|
|
781
|
+
subagentId: uniqueId,
|
|
782
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
783
|
+
data: {
|
|
784
|
+
toolCallId: _toolCallId,
|
|
785
|
+
params,
|
|
786
|
+
task: { ...taskItem, id: uniqueId },
|
|
787
|
+
sessionFile: subtaskSessionFile,
|
|
788
|
+
forkContextSeed: frozenForkSeed,
|
|
789
|
+
agentSource: fallbackAgentSource,
|
|
790
|
+
} satisfies TaskResumeDescriptor,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
if (typeof manager.registerSubagentRecord === "function") {
|
|
794
|
+
manager.registerSubagentRecord({
|
|
795
|
+
subagentId: uniqueId,
|
|
796
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
797
|
+
currentJobId: jobId,
|
|
798
|
+
historicalJobIds: [],
|
|
799
|
+
status: manager.getJob(jobId)?.status ?? "running",
|
|
800
|
+
sessionFile: subtaskSessionFile,
|
|
801
|
+
resumable: !!batchArtifactsDir,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
571
804
|
} catch (error) {
|
|
572
805
|
const message = error instanceof Error ? error.message : String(error);
|
|
573
806
|
failedSchedules.push(`${taskItem.id}: ${message}`);
|
|
@@ -637,6 +870,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
637
870
|
onUpdate?: AgentToolUpdateCallback<TaskToolDetails>,
|
|
638
871
|
preAllocatedIds?: string[],
|
|
639
872
|
prebuiltForkContextSeeds?: ReadonlyMap<string, ForkContextSeed>,
|
|
873
|
+
executionOverrides?: {
|
|
874
|
+
runMode?: "initial" | "resume" | "message";
|
|
875
|
+
resumeMessage?: string;
|
|
876
|
+
sessionFiles?: ReadonlyMap<string, string | null>;
|
|
877
|
+
},
|
|
640
878
|
): Promise<AgentToolResult<TaskToolDetails>> {
|
|
641
879
|
const startTime = Date.now();
|
|
642
880
|
const { agents, projectAgentsDir } = await discoverAgents(this.session.cwd);
|
|
@@ -818,6 +1056,44 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
818
1056
|
};
|
|
819
1057
|
}
|
|
820
1058
|
|
|
1059
|
+
const batchGateDecision = evaluateSpawnGate({ childCount: tasks.length, plan: params.spawnPlan });
|
|
1060
|
+
if (batchGateDecision.outcome === "rejected") {
|
|
1061
|
+
return {
|
|
1062
|
+
content: [
|
|
1063
|
+
{
|
|
1064
|
+
type: "text",
|
|
1065
|
+
text: `Task spawn gate rejected this batch: ${batchGateDecision.reason}. Batches with more than ${DEFAULT_SPAWN_THRESHOLD} tasks require spawnPlan fields: ${batchGateDecision.missingFields.join(", ")}.`,
|
|
1066
|
+
},
|
|
1067
|
+
],
|
|
1068
|
+
details: {
|
|
1069
|
+
projectAgentsDir,
|
|
1070
|
+
results: [],
|
|
1071
|
+
totalDurationMs: Date.now() - startTime,
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
const reviewerExploreDecision = evaluateReviewerExploreGate({
|
|
1077
|
+
spawningAgentType: this.#spawningAgentType,
|
|
1078
|
+
targetAgent: agentName,
|
|
1079
|
+
plan: params.spawnPlan,
|
|
1080
|
+
});
|
|
1081
|
+
if (reviewerExploreDecision.outcome === "rejected") {
|
|
1082
|
+
return {
|
|
1083
|
+
content: [
|
|
1084
|
+
{
|
|
1085
|
+
type: "text",
|
|
1086
|
+
text: `Task spawn gate rejected reviewer->explore: ${reviewerExploreDecision.reason}. Provide spawnPlan fields: ${reviewerExploreDecision.missingFields.join(", ")}.`,
|
|
1087
|
+
},
|
|
1088
|
+
],
|
|
1089
|
+
details: {
|
|
1090
|
+
projectAgentsDir,
|
|
1091
|
+
results: [],
|
|
1092
|
+
totalDurationMs: Date.now() - startTime,
|
|
1093
|
+
},
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
|
|
821
1097
|
let repoRoot: string | null = null;
|
|
822
1098
|
let baseline: WorktreeBaseline | null = null;
|
|
823
1099
|
if (isIsolated) {
|
|
@@ -943,7 +1219,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
943
1219
|
this.session.agentOutputManager ?? new AgentOutputManager(this.session.getArtifactsDir ?? (() => null));
|
|
944
1220
|
uniqueIds = await outputManager.allocateBatch(tasks.map(t => t.id));
|
|
945
1221
|
}
|
|
946
|
-
const tasksWithUniqueIds = tasks.map((t, i) => ({ ...t, id: uniqueIds[i] }));
|
|
1222
|
+
const tasksWithUniqueIds = tasks.map((t, i) => ({ ...t, id: validateAllocatedTaskId(uniqueIds[i] ?? "") }));
|
|
947
1223
|
|
|
948
1224
|
const availableSkills = [...(this.session.skills ?? [])];
|
|
949
1225
|
// Resolve autoload skills from agent definition against available skills
|
|
@@ -983,23 +1259,43 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
983
1259
|
emitProgress();
|
|
984
1260
|
|
|
985
1261
|
const buildForkContextSeed = async (task: (typeof tasksWithUniqueIds)[number]) => {
|
|
986
|
-
if (task
|
|
1262
|
+
if (!requestsForkContext(task)) return undefined;
|
|
987
1263
|
if (!this.session.buildForkContextSeed) {
|
|
988
1264
|
throw new Error("Current session cannot build fork-context seeds.");
|
|
989
1265
|
}
|
|
990
|
-
const
|
|
1266
|
+
const configuredMaxMessages = this.session.settings.has("task.forkContext.maxMessages")
|
|
1267
|
+
? this.session.settings.get("task.forkContext.maxMessages")
|
|
1268
|
+
: undefined;
|
|
991
1269
|
const configuredMaxTokens = this.session.settings.get("task.forkContext.maxTokens");
|
|
1270
|
+
const params = resolveForkSeedParamsForMode(
|
|
1271
|
+
task.inheritContext,
|
|
1272
|
+
configuredMaxMessages,
|
|
1273
|
+
configuredMaxTokens,
|
|
1274
|
+
this.session.model,
|
|
1275
|
+
);
|
|
1276
|
+
if (!params) return undefined;
|
|
992
1277
|
return await this.session.buildForkContextSeed({
|
|
993
|
-
|
|
994
|
-
maxTokens: resolveForkContextMaxTokens(configuredMaxTokens, this.session.model),
|
|
1278
|
+
...params,
|
|
995
1279
|
signal,
|
|
996
1280
|
});
|
|
997
1281
|
};
|
|
998
1282
|
|
|
999
|
-
const runTask = async (
|
|
1283
|
+
const runTask = async (
|
|
1284
|
+
task: (typeof tasksWithUniqueIds)[number],
|
|
1285
|
+
index: number,
|
|
1286
|
+
overrides?: {
|
|
1287
|
+
runMode?: "initial" | "resume" | "message";
|
|
1288
|
+
resumeMessage?: string;
|
|
1289
|
+
sessionFile?: string | null;
|
|
1290
|
+
},
|
|
1291
|
+
) => {
|
|
1000
1292
|
const forkContextSeed = prebuiltForkContextSeeds?.get(task.id) ?? (await buildForkContextSeed(task));
|
|
1293
|
+
const forkContext = requestsForkContext(task)
|
|
1294
|
+
? { mode: task.inheritContext, clonedTokens: forkContextSeed?.metadata.approximateTokens ?? 0 }
|
|
1295
|
+
: undefined;
|
|
1296
|
+
const taskSessionFile = overrides?.sessionFile ?? executionOverrides?.sessionFiles?.get(task.id) ?? null;
|
|
1001
1297
|
if (!isIsolated) {
|
|
1002
|
-
|
|
1298
|
+
const result = await runSubprocess({
|
|
1003
1299
|
cwd: this.session.cwd,
|
|
1004
1300
|
agent: effectiveAgent,
|
|
1005
1301
|
task: renderSubagentUserPrompt(task.assignment, simpleMode),
|
|
@@ -1008,12 +1304,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1008
1304
|
description: task.description,
|
|
1009
1305
|
index,
|
|
1010
1306
|
id: task.id,
|
|
1307
|
+
runMode: overrides?.runMode ?? executionOverrides?.runMode,
|
|
1308
|
+
resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
|
|
1309
|
+
subagentId: task.id,
|
|
1011
1310
|
taskDepth,
|
|
1012
1311
|
modelOverride,
|
|
1013
1312
|
parentActiveModelPattern,
|
|
1014
1313
|
thinkingLevel: thinkingLevelOverride,
|
|
1015
1314
|
outputSchema: effectiveOutputSchema,
|
|
1016
|
-
sessionFile,
|
|
1315
|
+
sessionFile: taskSessionFile,
|
|
1017
1316
|
persistArtifacts: !!artifactsDir,
|
|
1018
1317
|
artifactsDir: effectiveArtifactsDir,
|
|
1019
1318
|
contextFile: contextFilePath,
|
|
@@ -1040,6 +1339,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1040
1339
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
1041
1340
|
forkContextSeed,
|
|
1042
1341
|
});
|
|
1342
|
+
return forkContext ? { ...result, forkContext } : result;
|
|
1043
1343
|
}
|
|
1044
1344
|
|
|
1045
1345
|
const taskStart = Date.now();
|
|
@@ -1063,12 +1363,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1063
1363
|
description: task.description,
|
|
1064
1364
|
index,
|
|
1065
1365
|
id: task.id,
|
|
1366
|
+
runMode: overrides?.runMode ?? executionOverrides?.runMode,
|
|
1367
|
+
resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
|
|
1368
|
+
subagentId: task.id,
|
|
1066
1369
|
taskDepth,
|
|
1067
1370
|
modelOverride,
|
|
1068
1371
|
parentActiveModelPattern,
|
|
1069
1372
|
thinkingLevel: thinkingLevelOverride,
|
|
1070
1373
|
outputSchema: effectiveOutputSchema,
|
|
1071
|
-
sessionFile,
|
|
1374
|
+
sessionFile: taskSessionFile,
|
|
1072
1375
|
persistArtifacts: !!artifactsDir,
|
|
1073
1376
|
artifactsDir: effectiveArtifactsDir,
|
|
1074
1377
|
contextFile: contextFilePath,
|
|
@@ -1095,7 +1398,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1095
1398
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
1096
1399
|
forkContextSeed,
|
|
1097
1400
|
});
|
|
1098
|
-
|
|
1401
|
+
const resultWithForkContext = forkContext ? { ...result, forkContext } : result;
|
|
1402
|
+
if (mergeMode === "branch" && resultWithForkContext.exitCode === 0) {
|
|
1099
1403
|
try {
|
|
1100
1404
|
const commitMsg =
|
|
1101
1405
|
commitStyle === "ai" && this.session.modelRegistry
|
|
@@ -1115,35 +1419,40 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1115
1419
|
task.description,
|
|
1116
1420
|
commitMsg,
|
|
1117
1421
|
);
|
|
1422
|
+
const producedChanges = Boolean(commitResult?.branchName || commitResult?.nestedPatches.length);
|
|
1118
1423
|
return {
|
|
1119
|
-
...
|
|
1424
|
+
...resultWithForkContext,
|
|
1120
1425
|
branchName: commitResult?.branchName,
|
|
1121
1426
|
nestedPatches: commitResult?.nestedPatches,
|
|
1427
|
+
producedChanges,
|
|
1122
1428
|
};
|
|
1123
1429
|
} catch (mergeErr) {
|
|
1124
1430
|
// Agent succeeded but branch commit failed — clean up stale branch
|
|
1125
1431
|
const branchName = `gjc/task/${task.id}`;
|
|
1126
1432
|
await git.branch.tryDelete(repoRoot, branchName);
|
|
1127
1433
|
const msg = mergeErr instanceof Error ? mergeErr.message : String(mergeErr);
|
|
1128
|
-
return { ...
|
|
1434
|
+
return { ...resultWithForkContext, error: `Merge failed: ${msg}` };
|
|
1129
1435
|
}
|
|
1130
1436
|
}
|
|
1131
|
-
if (
|
|
1437
|
+
if (resultWithForkContext.exitCode === 0) {
|
|
1132
1438
|
try {
|
|
1133
1439
|
const delta = await captureDeltaPatch(isolationDir, taskBaseline);
|
|
1134
|
-
const
|
|
1440
|
+
const artifactId = validateAllocatedTaskId(task.id);
|
|
1441
|
+
const patchPath = path.join(effectiveArtifactsDir, `${artifactId}.patch`);
|
|
1135
1442
|
await Bun.write(patchPath, delta.rootPatch);
|
|
1443
|
+
const producedChanges = Boolean(delta.rootPatch.trim() || delta.nestedPatches.length);
|
|
1136
1444
|
return {
|
|
1137
|
-
...
|
|
1445
|
+
...resultWithForkContext,
|
|
1138
1446
|
patchPath,
|
|
1139
1447
|
nestedPatches: delta.nestedPatches,
|
|
1448
|
+
producedChanges,
|
|
1140
1449
|
};
|
|
1141
1450
|
} catch (patchErr) {
|
|
1142
1451
|
const msg = patchErr instanceof Error ? patchErr.message : String(patchErr);
|
|
1143
|
-
return { ...
|
|
1452
|
+
return { ...resultWithForkContext, error: `Patch capture failed: ${msg}` };
|
|
1144
1453
|
}
|
|
1145
1454
|
}
|
|
1146
|
-
return
|
|
1455
|
+
return resultWithForkContext;
|
|
1147
1456
|
} catch (err) {
|
|
1148
1457
|
const message = err instanceof Error ? err.message : String(err);
|
|
1149
1458
|
const assignment = task.assignment.trim();
|
|
@@ -1162,6 +1471,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1162
1471
|
durationMs: Date.now() - taskStart,
|
|
1163
1472
|
tokens: 0,
|
|
1164
1473
|
modelOverride,
|
|
1474
|
+
forkContext,
|
|
1165
1475
|
error: message,
|
|
1166
1476
|
};
|
|
1167
1477
|
} finally {
|
|
@@ -1206,6 +1516,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1206
1516
|
abortReason: "Cancelled before start",
|
|
1207
1517
|
};
|
|
1208
1518
|
});
|
|
1519
|
+
if (!artifactsDir) {
|
|
1520
|
+
for (const result of results) {
|
|
1521
|
+
delete result.outputMeta;
|
|
1522
|
+
delete result.outputPath;
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const forkContextClonedTokens = results.reduce(
|
|
1527
|
+
(total, result) => total + (result.forkContext?.clonedTokens ?? 0),
|
|
1528
|
+
0,
|
|
1529
|
+
);
|
|
1209
1530
|
|
|
1210
1531
|
// Aggregate usage from executor results (already accumulated incrementally)
|
|
1211
1532
|
const aggregatedUsage = createUsageTotals();
|
|
@@ -1217,13 +1538,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1217
1538
|
}
|
|
1218
1539
|
}
|
|
1219
1540
|
|
|
1220
|
-
// Collect output paths (artifacts already written by executor in real-time)
|
|
1221
|
-
const outputPaths: string[] = [];
|
|
1222
1541
|
const patchPaths: string[] = [];
|
|
1223
1542
|
for (const result of results) {
|
|
1224
|
-
if (result.outputPath) {
|
|
1225
|
-
outputPaths.push(result.outputPath);
|
|
1226
|
-
}
|
|
1227
1543
|
if (result.patchPath) {
|
|
1228
1544
|
patchPaths.push(result.patchPath);
|
|
1229
1545
|
}
|
|
@@ -1319,7 +1635,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1319
1635
|
"<system-notification>Patches were not applied and must be handled manually.</system-notification>";
|
|
1320
1636
|
const patchList =
|
|
1321
1637
|
patchPaths.length > 0
|
|
1322
|
-
? `\n\nPatch artifacts
|
|
1638
|
+
? `\n\nPatch artifacts: ${patchPaths.length} preserved for internal merge recovery.`
|
|
1323
1639
|
: "";
|
|
1324
1640
|
mergeSummary = `\n\n${notification}${patchList}`;
|
|
1325
1641
|
}
|
|
@@ -1375,41 +1691,25 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1375
1691
|
const successCount = results.filter(r => r.exitCode === 0 && !r.error && !r.aborted).length;
|
|
1376
1692
|
const totalDuration = Date.now() - startTime;
|
|
1377
1693
|
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
? "merge failed"
|
|
1383
|
-
: r.exitCode === 0
|
|
1384
|
-
? "completed"
|
|
1385
|
-
: `failed (exit ${r.exitCode})`;
|
|
1386
|
-
const output = r.output.trim() || r.stderr.trim() || "(no output)";
|
|
1387
|
-
const outputCharCount = r.outputMeta?.charCount ?? output.length;
|
|
1388
|
-
const fullOutputThreshold = 5000;
|
|
1389
|
-
let preview = output;
|
|
1390
|
-
let truncated = false;
|
|
1391
|
-
if (outputCharCount > fullOutputThreshold) {
|
|
1392
|
-
const slice = output.slice(0, fullOutputThreshold);
|
|
1393
|
-
const lastNewline = slice.lastIndexOf("\n");
|
|
1394
|
-
preview = lastNewline >= 0 ? slice.slice(0, lastNewline) : slice;
|
|
1395
|
-
truncated = true;
|
|
1396
|
-
}
|
|
1694
|
+
const receipts = results.map(buildTaskReceipt);
|
|
1695
|
+
const roiSummary = buildTaskRoiSummary(receipts);
|
|
1696
|
+
const summaries = receipts.map(r => {
|
|
1697
|
+
const status = r.status === "merge_failed" ? "merge failed" : r.status;
|
|
1397
1698
|
return {
|
|
1398
1699
|
agent: r.agent,
|
|
1399
1700
|
status,
|
|
1400
1701
|
id: r.id,
|
|
1401
|
-
preview,
|
|
1402
|
-
|
|
1403
|
-
meta: r.
|
|
1702
|
+
synopsis: r.preview,
|
|
1703
|
+
outputUri: r.outputRef?.uri,
|
|
1704
|
+
meta: r.outputRef
|
|
1404
1705
|
? {
|
|
1405
|
-
lineCount: r.
|
|
1406
|
-
charSize: formatBytes(r.
|
|
1706
|
+
lineCount: r.outputRef.lineCount,
|
|
1707
|
+
charSize: formatBytes(r.outputRef.sizeBytes),
|
|
1407
1708
|
}
|
|
1408
1709
|
: undefined,
|
|
1409
1710
|
};
|
|
1410
1711
|
});
|
|
1411
1712
|
|
|
1412
|
-
const outputIds = results.filter(r => !r.aborted || r.output.trim()).map(r => `agent://${r.id}`);
|
|
1413
1713
|
const summary = prompt.render(taskSummaryTemplate, {
|
|
1414
1714
|
successCount,
|
|
1415
1715
|
totalCount: results.length,
|
|
@@ -1417,7 +1717,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1417
1717
|
hasCancelledNote: aborted && cancelledCount > 0,
|
|
1418
1718
|
duration: formatDuration(totalDuration),
|
|
1419
1719
|
summaries,
|
|
1420
|
-
outputIds,
|
|
1421
1720
|
agentName,
|
|
1422
1721
|
mergeSummary,
|
|
1423
1722
|
});
|
|
@@ -1429,15 +1728,18 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1429
1728
|
await fs.rm(tempArtifactsDir, { recursive: true, force: true });
|
|
1430
1729
|
}
|
|
1431
1730
|
|
|
1731
|
+
const details: TaskToolDetails = {
|
|
1732
|
+
projectAgentsDir,
|
|
1733
|
+
results: receipts,
|
|
1734
|
+
totalDurationMs: totalDuration,
|
|
1735
|
+
usage: hasAggregatedUsage ? aggregatedUsage : undefined,
|
|
1736
|
+
forkContextClonedTokens: forkContextClonedTokens > 0 ? forkContextClonedTokens : undefined,
|
|
1737
|
+
roiSummary,
|
|
1738
|
+
};
|
|
1739
|
+
assertNoRawTaskFields(details, "task.return.details");
|
|
1432
1740
|
return {
|
|
1433
1741
|
content: [{ type: "text", text: summary }],
|
|
1434
|
-
details
|
|
1435
|
-
projectAgentsDir,
|
|
1436
|
-
results: results,
|
|
1437
|
-
totalDurationMs: totalDuration,
|
|
1438
|
-
usage: hasAggregatedUsage ? aggregatedUsage : undefined,
|
|
1439
|
-
outputPaths,
|
|
1440
|
-
},
|
|
1742
|
+
details,
|
|
1441
1743
|
};
|
|
1442
1744
|
} catch (err) {
|
|
1443
1745
|
return {
|