@gajae-code/coding-agent 0.2.5 → 0.3.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 +10 -0
- package/dist/types/async/job-manager.d.ts +84 -2
- package/dist/types/commands/harness.d.ts +37 -0
- package/dist/types/config/settings-schema.d.ts +6 -0
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/deep-interview/render-middleware.d.ts +5 -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/state-graph.d.ts +4 -0
- package/dist/types/gjc-runtime/state-migrations.d.ts +24 -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-validation.d.ts +6 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +137 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +81 -7
- 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 +30 -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 +2 -29
- package/dist/types/modes/components/hook-selector.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +8 -0
- 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 +24 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/types.d.ts +55 -3
- package/dist/types/tools/subagent.d.ts +11 -1
- package/package.json +7 -7
- package/src/async/job-manager.ts +298 -6
- 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/harness.ts +592 -0
- package/src/commands/team.ts +36 -39
- package/src/config/settings-schema.ts +7 -0
- package/src/config/settings.ts +5 -0
- package/src/deep-interview/render-middleware.ts +366 -0
- package/src/defaults/gjc/skills/team/SKILL.md +47 -21
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +78 -11
- 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/deep-interview-runtime.ts +40 -21
- package/src/gjc-runtime/goal-mode-request.ts +11 -3
- package/src/gjc-runtime/ralplan-runtime.ts +25 -10
- package/src/gjc-runtime/state-graph.ts +86 -0
- package/src/gjc-runtime/state-migrations.ts +132 -0
- package/src/gjc-runtime/state-renderer.ts +345 -0
- package/src/gjc-runtime/state-runtime.ts +733 -21
- package/src/gjc-runtime/state-validation.ts +49 -0
- package/src/gjc-runtime/state-writer.ts +718 -0
- package/src/gjc-runtime/team-runtime.ts +1083 -89
- package/src/gjc-runtime/ultragoal-runtime.ts +348 -19
- package/src/gjc-runtime/workflow-manifest.generated.json +1497 -0
- package/src/gjc-runtime/workflow-manifest.ts +425 -0
- package/src/harness-control-plane/classifier.ts +128 -0
- package/src/harness-control-plane/control-endpoint.ts +137 -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 +553 -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 +97 -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 +24 -41
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/modes/components/assistant-message.ts +5 -1
- package/src/modes/components/hook-selector.ts +72 -2
- package/src/modes/controllers/event-controller.ts +71 -6
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +9 -1
- package/src/modes/controllers/selector-controller.ts +2 -1
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/prompts/agents/executor.md +13 -0
- package/src/prompts/tools/subagent.md +33 -3
- package/src/sdk.ts +4 -0
- package/src/session/agent-session.ts +231 -33
- 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 +91 -13
- package/src/skill-state/initial-phase.ts +2 -0
- package/src/skill-state/workflow-state-contract.ts +26 -0
- package/src/task/executor.ts +50 -8
- package/src/task/index.ts +120 -8
- package/src/task/render.ts +6 -3
- package/src/task/types.ts +56 -3
- package/src/tools/ask.ts +28 -7
- package/src/tools/subagent.ts +255 -64
package/src/task/index.ts
CHANGED
|
@@ -65,6 +65,18 @@ import {
|
|
|
65
65
|
type WorktreeBaseline,
|
|
66
66
|
} from "./worktree";
|
|
67
67
|
|
|
68
|
+
interface TaskResumeDescriptor {
|
|
69
|
+
toolCallId: string;
|
|
70
|
+
params: TaskParams;
|
|
71
|
+
task: TaskItem & { id: string };
|
|
72
|
+
sessionFile: string | null;
|
|
73
|
+
forkContextSeed?: ForkContextSeed;
|
|
74
|
+
agentSource: AgentDefinition["source"];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isTaskResumeDescriptor(value: unknown): value is TaskResumeDescriptor {
|
|
78
|
+
return typeof value === "object" && value !== null && "task" in value && "params" in value;
|
|
79
|
+
}
|
|
68
80
|
function renderSubagentUserPrompt(assignment: string, simpleMode: TaskSimpleMode): string {
|
|
69
81
|
return prompt.render(subagentUserPromptTemplate, {
|
|
70
82
|
assignment: assignment.trim(),
|
|
@@ -416,6 +428,50 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
416
428
|
};
|
|
417
429
|
|
|
418
430
|
const maxConcurrency = this.session.settings.get("task.maxConcurrency");
|
|
431
|
+
if (typeof manager.setResumeRunner === "function") {
|
|
432
|
+
manager.setResumeRunner((_subagentId, message, resumeDescriptor) => {
|
|
433
|
+
const descriptor = isTaskResumeDescriptor(resumeDescriptor?.data) ? resumeDescriptor.data : undefined;
|
|
434
|
+
if (!descriptor) return undefined;
|
|
435
|
+
const forkSeeds = descriptor.forkContextSeed
|
|
436
|
+
? new Map([[descriptor.task.id, descriptor.forkContextSeed]])
|
|
437
|
+
: undefined;
|
|
438
|
+
return manager.register(
|
|
439
|
+
"task",
|
|
440
|
+
descriptor.task.id,
|
|
441
|
+
async ({ signal: runSignal }) => {
|
|
442
|
+
const result = await this.#executeSync(
|
|
443
|
+
descriptor.toolCallId,
|
|
444
|
+
{ ...descriptor.params, tasks: [descriptor.task] },
|
|
445
|
+
runSignal,
|
|
446
|
+
undefined,
|
|
447
|
+
[descriptor.task.id],
|
|
448
|
+
forkSeeds,
|
|
449
|
+
{
|
|
450
|
+
runMode: message ? "message" : "resume",
|
|
451
|
+
resumeMessage: message,
|
|
452
|
+
sessionFiles: new Map([[descriptor.task.id, descriptor.sessionFile]]),
|
|
453
|
+
},
|
|
454
|
+
);
|
|
455
|
+
const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
|
|
456
|
+
const singleResult = result.details?.results[0];
|
|
457
|
+
return singleResult?.paused ? { kind: "paused" } : finalText;
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
id: `${descriptor.task.id}-resume-${Snowflake.next()}`,
|
|
461
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
462
|
+
metadata: {
|
|
463
|
+
subagent: {
|
|
464
|
+
id: descriptor.task.id,
|
|
465
|
+
agent: descriptor.params.agent,
|
|
466
|
+
agentSource: descriptor.agentSource,
|
|
467
|
+
description: descriptor.task.description,
|
|
468
|
+
assignment: descriptor.task.assignment.trim(),
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
},
|
|
472
|
+
);
|
|
473
|
+
});
|
|
474
|
+
}
|
|
419
475
|
const semaphore = new Semaphore(maxConcurrency);
|
|
420
476
|
const buildForkContextSeedForTask = async (task: TaskItem): Promise<ForkContextSeed | undefined> => {
|
|
421
477
|
if (task.inheritContext !== true) return undefined;
|
|
@@ -431,6 +487,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
431
487
|
});
|
|
432
488
|
};
|
|
433
489
|
const frozenForkSeeds = new Map<string, ForkContextSeed>();
|
|
490
|
+
const parentSessionFileForBatch = this.session.getSessionFile();
|
|
491
|
+
const batchArtifactsDir = parentSessionFileForBatch ? parentSessionFileForBatch.slice(0, -6) : null;
|
|
434
492
|
|
|
435
493
|
for (let i = 0; i < taskItems.length; i++) {
|
|
436
494
|
const taskItem = taskItems[i];
|
|
@@ -449,6 +507,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
449
507
|
const singleParams: TaskParams = { ...params, tasks: [taskItem] };
|
|
450
508
|
const label = uniqueId;
|
|
451
509
|
try {
|
|
510
|
+
const subtaskSessionFile = batchArtifactsDir ? path.join(batchArtifactsDir, `${uniqueId}.jsonl`) : null;
|
|
452
511
|
const jobId = manager.register(
|
|
453
512
|
"task",
|
|
454
513
|
label,
|
|
@@ -478,15 +537,20 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
478
537
|
undefined,
|
|
479
538
|
[uniqueId],
|
|
480
539
|
frozenForkSeeds,
|
|
540
|
+
{
|
|
541
|
+
sessionFiles: new Map([[uniqueId, subtaskSessionFile]]),
|
|
542
|
+
},
|
|
481
543
|
);
|
|
482
544
|
const finalText = result.content.find(part => part.type === "text")?.text ?? "(no output)";
|
|
483
545
|
const singleResult = result.details?.results[0];
|
|
484
546
|
if (progress) {
|
|
485
|
-
progress.status = singleResult?.
|
|
486
|
-
? "
|
|
487
|
-
:
|
|
488
|
-
? "
|
|
489
|
-
:
|
|
547
|
+
progress.status = singleResult?.paused
|
|
548
|
+
? "paused"
|
|
549
|
+
: singleResult?.aborted
|
|
550
|
+
? "aborted"
|
|
551
|
+
: (singleResult?.exitCode ?? 0) === 0
|
|
552
|
+
? "completed"
|
|
553
|
+
: "failed";
|
|
490
554
|
progress.durationMs = singleResult?.durationMs ?? Math.max(0, Date.now() - startedAt);
|
|
491
555
|
progress.tokens = singleResult?.tokens ?? 0;
|
|
492
556
|
progress.contextTokens = singleResult?.contextTokens;
|
|
@@ -517,6 +581,9 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
517
581
|
`Background task batch complete: ${completedJobs}/${taskItems.length} finished.`,
|
|
518
582
|
);
|
|
519
583
|
}
|
|
584
|
+
if (singleResult?.paused) {
|
|
585
|
+
return { kind: "paused" };
|
|
586
|
+
}
|
|
520
587
|
return finalText;
|
|
521
588
|
} catch (error) {
|
|
522
589
|
if (progress) {
|
|
@@ -568,6 +635,31 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
568
635
|
},
|
|
569
636
|
);
|
|
570
637
|
startedJobs.push({ jobId, taskId: taskItem.id });
|
|
638
|
+
if (typeof manager.registerResumeDescriptor === "function") {
|
|
639
|
+
manager.registerResumeDescriptor({
|
|
640
|
+
subagentId: uniqueId,
|
|
641
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
642
|
+
data: {
|
|
643
|
+
toolCallId: _toolCallId,
|
|
644
|
+
params,
|
|
645
|
+
task: { ...taskItem, id: uniqueId },
|
|
646
|
+
sessionFile: subtaskSessionFile,
|
|
647
|
+
forkContextSeed: frozenForkSeed,
|
|
648
|
+
agentSource: fallbackAgentSource,
|
|
649
|
+
} satisfies TaskResumeDescriptor,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
if (typeof manager.registerSubagentRecord === "function") {
|
|
653
|
+
manager.registerSubagentRecord({
|
|
654
|
+
subagentId: uniqueId,
|
|
655
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
656
|
+
currentJobId: jobId,
|
|
657
|
+
historicalJobIds: [],
|
|
658
|
+
status: manager.getJob(jobId)?.status ?? "running",
|
|
659
|
+
sessionFile: subtaskSessionFile,
|
|
660
|
+
resumable: !!batchArtifactsDir,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
571
663
|
} catch (error) {
|
|
572
664
|
const message = error instanceof Error ? error.message : String(error);
|
|
573
665
|
failedSchedules.push(`${taskItem.id}: ${message}`);
|
|
@@ -637,6 +729,11 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
637
729
|
onUpdate?: AgentToolUpdateCallback<TaskToolDetails>,
|
|
638
730
|
preAllocatedIds?: string[],
|
|
639
731
|
prebuiltForkContextSeeds?: ReadonlyMap<string, ForkContextSeed>,
|
|
732
|
+
executionOverrides?: {
|
|
733
|
+
runMode?: "initial" | "resume" | "message";
|
|
734
|
+
resumeMessage?: string;
|
|
735
|
+
sessionFiles?: ReadonlyMap<string, string | null>;
|
|
736
|
+
},
|
|
640
737
|
): Promise<AgentToolResult<TaskToolDetails>> {
|
|
641
738
|
const startTime = Date.now();
|
|
642
739
|
const { agents, projectAgentsDir } = await discoverAgents(this.session.cwd);
|
|
@@ -996,8 +1093,17 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
996
1093
|
});
|
|
997
1094
|
};
|
|
998
1095
|
|
|
999
|
-
const runTask = async (
|
|
1096
|
+
const runTask = async (
|
|
1097
|
+
task: (typeof tasksWithUniqueIds)[number],
|
|
1098
|
+
index: number,
|
|
1099
|
+
overrides?: {
|
|
1100
|
+
runMode?: "initial" | "resume" | "message";
|
|
1101
|
+
resumeMessage?: string;
|
|
1102
|
+
sessionFile?: string | null;
|
|
1103
|
+
},
|
|
1104
|
+
) => {
|
|
1000
1105
|
const forkContextSeed = prebuiltForkContextSeeds?.get(task.id) ?? (await buildForkContextSeed(task));
|
|
1106
|
+
const taskSessionFile = overrides?.sessionFile ?? executionOverrides?.sessionFiles?.get(task.id) ?? null;
|
|
1001
1107
|
if (!isIsolated) {
|
|
1002
1108
|
return runSubprocess({
|
|
1003
1109
|
cwd: this.session.cwd,
|
|
@@ -1008,12 +1114,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1008
1114
|
description: task.description,
|
|
1009
1115
|
index,
|
|
1010
1116
|
id: task.id,
|
|
1117
|
+
runMode: overrides?.runMode ?? executionOverrides?.runMode,
|
|
1118
|
+
resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
|
|
1119
|
+
subagentId: task.id,
|
|
1011
1120
|
taskDepth,
|
|
1012
1121
|
modelOverride,
|
|
1013
1122
|
parentActiveModelPattern,
|
|
1014
1123
|
thinkingLevel: thinkingLevelOverride,
|
|
1015
1124
|
outputSchema: effectiveOutputSchema,
|
|
1016
|
-
sessionFile,
|
|
1125
|
+
sessionFile: taskSessionFile,
|
|
1017
1126
|
persistArtifacts: !!artifactsDir,
|
|
1018
1127
|
artifactsDir: effectiveArtifactsDir,
|
|
1019
1128
|
contextFile: contextFilePath,
|
|
@@ -1063,12 +1172,15 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1063
1172
|
description: task.description,
|
|
1064
1173
|
index,
|
|
1065
1174
|
id: task.id,
|
|
1175
|
+
runMode: overrides?.runMode ?? executionOverrides?.runMode,
|
|
1176
|
+
resumeMessage: overrides?.resumeMessage ?? executionOverrides?.resumeMessage,
|
|
1177
|
+
subagentId: task.id,
|
|
1066
1178
|
taskDepth,
|
|
1067
1179
|
modelOverride,
|
|
1068
1180
|
parentActiveModelPattern,
|
|
1069
1181
|
thinkingLevel: thinkingLevelOverride,
|
|
1070
1182
|
outputSchema: effectiveOutputSchema,
|
|
1071
|
-
sessionFile,
|
|
1183
|
+
sessionFile: taskSessionFile,
|
|
1072
1184
|
persistArtifacts: !!artifactsDir,
|
|
1073
1185
|
artifactsDir: effectiveArtifactsDir,
|
|
1074
1186
|
contextFile: contextFilePath,
|
package/src/task/render.ts
CHANGED
|
@@ -47,6 +47,8 @@ function getStatusIcon(status: AgentProgress["status"], theme: Theme, spinnerFra
|
|
|
47
47
|
return formatStatusIcon("error", theme);
|
|
48
48
|
case "aborted":
|
|
49
49
|
return formatStatusIcon("aborted", theme);
|
|
50
|
+
case "paused":
|
|
51
|
+
return formatStatusIcon("pending", theme);
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -611,9 +613,10 @@ function renderAgentProgress(
|
|
|
611
613
|
if (progress.retryState && progress.status === "running") {
|
|
612
614
|
const remainingMs = Math.max(0, progress.retryState.startedAtMs + progress.retryState.delayMs - Date.now());
|
|
613
615
|
const waitLabel = remainingMs > 0 ? `in ${formatDuration(remainingMs)}` : "now";
|
|
614
|
-
const
|
|
615
|
-
`
|
|
616
|
-
|
|
616
|
+
const attemptLabel = progress.retryState.unbounded
|
|
617
|
+
? `attempt ${progress.retryState.attempt}`
|
|
618
|
+
: `${progress.retryState.attempt}/${progress.retryState.maxAttempts}`;
|
|
619
|
+
const summary = `retrying ${attemptLabel} ${waitLabel}: ${truncateToWidth(replaceTabs(progress.retryState.errorMessage), 60)}`;
|
|
617
620
|
lines.push(`${continuePrefix}${theme.tree.hook} ${theme.fg("warning", summary)}`);
|
|
618
621
|
} else if (progress.retryFailure && progress.status !== "running") {
|
|
619
622
|
const summary = `auto-retry gave up after ${progress.retryFailure.attempt} attempt${
|
package/src/task/types.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface SubagentLifecyclePayload {
|
|
|
53
53
|
agent: string;
|
|
54
54
|
agentSource: AgentSource;
|
|
55
55
|
description?: string;
|
|
56
|
-
status: "started" | "completed" | "failed" | "aborted";
|
|
56
|
+
status: "started" | "completed" | "failed" | "aborted" | "paused";
|
|
57
57
|
sessionFile?: string;
|
|
58
58
|
index: number;
|
|
59
59
|
}
|
|
@@ -192,7 +192,7 @@ export interface AgentProgress {
|
|
|
192
192
|
id: string;
|
|
193
193
|
agent: string;
|
|
194
194
|
agentSource: AgentSource;
|
|
195
|
-
status: "pending" | "running" | "completed" | "failed" | "aborted";
|
|
195
|
+
status: "pending" | "running" | "completed" | "failed" | "aborted" | "paused";
|
|
196
196
|
task: string;
|
|
197
197
|
assignment?: string;
|
|
198
198
|
description?: string;
|
|
@@ -230,6 +230,7 @@ export interface AgentProgress {
|
|
|
230
230
|
retryState?: {
|
|
231
231
|
attempt: number;
|
|
232
232
|
maxAttempts: number;
|
|
233
|
+
unbounded?: boolean;
|
|
233
234
|
delayMs: number;
|
|
234
235
|
errorMessage: string;
|
|
235
236
|
startedAtMs: number;
|
|
@@ -279,6 +280,7 @@ export interface SingleResult {
|
|
|
279
280
|
error?: string;
|
|
280
281
|
aborted?: boolean;
|
|
281
282
|
abortReason?: string;
|
|
283
|
+
paused?: boolean;
|
|
282
284
|
/** Aggregated usage from the subprocess, accumulated incrementally from message_end events. */
|
|
283
285
|
usage?: Usage;
|
|
284
286
|
/** Output path for the task result */
|
|
@@ -315,8 +317,59 @@ export interface TaskToolDetails {
|
|
|
315
317
|
outputPaths?: string[];
|
|
316
318
|
progress?: AgentProgress[];
|
|
317
319
|
async?: {
|
|
318
|
-
state: "running" | "completed" | "failed";
|
|
320
|
+
state: "running" | "paused" | "queued" | "completed" | "failed";
|
|
319
321
|
jobId: string;
|
|
320
322
|
type: "task";
|
|
321
323
|
};
|
|
322
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Persisted per-turn / per-subagent token record (Phase 0 instrumentation).
|
|
327
|
+
*
|
|
328
|
+
* Additive: this does not alter any existing task result shape. It is the
|
|
329
|
+
* durable, model-independent unit the deterministic orchestration-token
|
|
330
|
+
* benchmark (`@gajae-code/orchestration-token-benchmark`) consumes to measure
|
|
331
|
+
* token efficiency without any live-model calls.
|
|
332
|
+
*/
|
|
333
|
+
export interface TaskTokenLog {
|
|
334
|
+
/** Subagent id, or "root" for the orchestrator's own turn. */
|
|
335
|
+
subagentId: string;
|
|
336
|
+
/** Agent name for attribution, when known. */
|
|
337
|
+
agent?: string;
|
|
338
|
+
/** 1-based turn index within the subagent's session. */
|
|
339
|
+
turn: number;
|
|
340
|
+
/** ISO-8601 timestamp the turn completed. */
|
|
341
|
+
at: string;
|
|
342
|
+
/** Cost-bearing input tokens (excludes cache reads), mirrors `Usage.input`. */
|
|
343
|
+
input: number;
|
|
344
|
+
/** Total output tokens for the turn, mirrors `Usage.output`. */
|
|
345
|
+
output: number;
|
|
346
|
+
/** Tokens read from the prompt cache, mirrors `Usage.cacheRead`. */
|
|
347
|
+
cacheRead: number;
|
|
348
|
+
/** Tokens written to the prompt cache, mirrors `Usage.cacheWrite`. */
|
|
349
|
+
cacheWrite: number;
|
|
350
|
+
/** input + output + cacheRead + cacheWrite. */
|
|
351
|
+
totalTokens: number;
|
|
352
|
+
/** Latest per-turn context-window occupancy, when known. */
|
|
353
|
+
contextTokens?: number;
|
|
354
|
+
/** Estimated USD cost for the turn, when known. */
|
|
355
|
+
cost?: number;
|
|
356
|
+
/** Model id used for the turn, when known. */
|
|
357
|
+
model?: string;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Deterministic aggregate token metrics computed from a set of `TaskTokenLog`
|
|
362
|
+
* entries. The cache-hit-rate field is the primary prompt-cache signal called
|
|
363
|
+
* out by the prefix-stability invariant (see the approved plan).
|
|
364
|
+
*/
|
|
365
|
+
export interface TaskTokenMetrics {
|
|
366
|
+
/** Number of token-log entries aggregated. */
|
|
367
|
+
turns: number;
|
|
368
|
+
inputTokens: number;
|
|
369
|
+
outputTokens: number;
|
|
370
|
+
cacheReadTokens: number;
|
|
371
|
+
cacheWriteTokens: number;
|
|
372
|
+
totalTokens: number;
|
|
373
|
+
/** cacheRead / (input + cacheRead); 0 when there is no input-class traffic. */
|
|
374
|
+
cacheHitRate: number;
|
|
375
|
+
}
|
package/src/tools/ask.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
} from "@gajae-code/tui";
|
|
29
29
|
import { prompt, untilAborted } from "@gajae-code/utils";
|
|
30
30
|
import * as z from "zod/v4";
|
|
31
|
+
import { formatDeepInterviewSelectorPrompt, renderDeepInterviewAskQuestion } from "../deep-interview/render-middleware";
|
|
31
32
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
32
33
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
33
34
|
import askDescription from "../prompts/tools/ask.md" with { type: "text" };
|
|
@@ -84,6 +85,7 @@ export interface AskToolDetails {
|
|
|
84
85
|
|
|
85
86
|
const OTHER_OPTION = "Other (type your own)";
|
|
86
87
|
const RECOMMENDED_SUFFIX = " (Recommended)";
|
|
88
|
+
const DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS = 12;
|
|
87
89
|
|
|
88
90
|
function getDoneOptionLabel(): string {
|
|
89
91
|
return `${theme.status.success} Done selecting`;
|
|
@@ -138,6 +140,7 @@ interface AskSingleQuestionOptions {
|
|
|
138
140
|
signal?: AbortSignal;
|
|
139
141
|
initialSelection?: Pick<SelectionResult, "selectedOptions" | "customInput">;
|
|
140
142
|
navigation?: NavigationControls;
|
|
143
|
+
scrollTitleRows?: number;
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
interface UIContext {
|
|
@@ -150,6 +153,7 @@ interface UIContext {
|
|
|
150
153
|
signal?: AbortSignal;
|
|
151
154
|
outline?: boolean;
|
|
152
155
|
wrapFocused?: boolean;
|
|
156
|
+
scrollTitleRows?: number;
|
|
153
157
|
onTimeout?: () => void;
|
|
154
158
|
onLeft?: () => void;
|
|
155
159
|
onRight?: () => void;
|
|
@@ -171,7 +175,7 @@ async function askSingleQuestion(
|
|
|
171
175
|
multi: boolean,
|
|
172
176
|
options: AskSingleQuestionOptions = {},
|
|
173
177
|
): Promise<SelectionResult> {
|
|
174
|
-
const { recommended, timeout, signal, initialSelection, navigation } = options;
|
|
178
|
+
const { recommended, timeout, signal, initialSelection, navigation, scrollTitleRows } = options;
|
|
175
179
|
const doneLabel = getDoneOptionLabel();
|
|
176
180
|
let selectedOptions = [...(initialSelection?.selectedOptions ?? [])];
|
|
177
181
|
let customInput = initialSelection?.customInput;
|
|
@@ -187,15 +191,17 @@ async function askSingleQuestion(
|
|
|
187
191
|
timeoutTriggered = true;
|
|
188
192
|
};
|
|
189
193
|
let navigationAction: "back" | "forward" | undefined;
|
|
190
|
-
const
|
|
194
|
+
const baseHelpText = navigation
|
|
191
195
|
? "up/down navigate enter select ←/→ question esc cancel"
|
|
192
196
|
: "up/down navigate enter select esc cancel";
|
|
197
|
+
const helpText = scrollTitleRows === undefined ? baseHelpText : `${baseHelpText} PgUp/PgDn scroll question`;
|
|
193
198
|
const dialogOptions = {
|
|
194
199
|
initialIndex,
|
|
195
200
|
timeout,
|
|
196
201
|
signal,
|
|
197
202
|
outline: true,
|
|
198
203
|
wrapFocused: true,
|
|
204
|
+
scrollTitleRows,
|
|
199
205
|
onTimeout,
|
|
200
206
|
helpText,
|
|
201
207
|
onLeft: navigation?.allowBack
|
|
@@ -454,9 +460,11 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
454
460
|
) => {
|
|
455
461
|
const optionLabels = q.options.map(o => o.label);
|
|
456
462
|
try {
|
|
463
|
+
const deepInterviewPrompt = formatDeepInterviewSelectorPrompt(q.question);
|
|
464
|
+
const displayQuestion = deepInterviewPrompt ?? q.question;
|
|
457
465
|
const { selectedOptions, customInput, navigation, cancelled, timedOut } = await askSingleQuestion(
|
|
458
466
|
ui,
|
|
459
|
-
|
|
467
|
+
displayQuestion,
|
|
460
468
|
optionLabels,
|
|
461
469
|
q.multi ?? false,
|
|
462
470
|
{
|
|
@@ -465,6 +473,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
465
473
|
signal,
|
|
466
474
|
initialSelection: options?.previous,
|
|
467
475
|
navigation: options?.navigation,
|
|
476
|
+
scrollTitleRows: deepInterviewPrompt === null ? undefined : DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS,
|
|
468
477
|
},
|
|
469
478
|
);
|
|
470
479
|
return { optionLabels, selectedOptions, customInput, navigation, cancelled, timedOut };
|
|
@@ -659,7 +668,10 @@ export const askToolRenderer = {
|
|
|
659
668
|
container.addChild(
|
|
660
669
|
new Text(` ${uiTheme.fg("dim", qBranch)} ${uiTheme.fg("dim", `[${q.id}]`)}${metaStr}`, 0, 0),
|
|
661
670
|
);
|
|
662
|
-
container.addChild(
|
|
671
|
+
container.addChild(
|
|
672
|
+
renderDeepInterviewAskQuestion(q.question, uiTheme) ??
|
|
673
|
+
new Markdown(q.question, 3, 0, mdTheme, accentStyle),
|
|
674
|
+
);
|
|
663
675
|
|
|
664
676
|
const qOptions = q.options;
|
|
665
677
|
if (qOptions?.length) {
|
|
@@ -688,7 +700,10 @@ export const askToolRenderer = {
|
|
|
688
700
|
if (args.multi) meta.push("multi");
|
|
689
701
|
if (args.options?.length) meta.push(`options:${args.options.length}`);
|
|
690
702
|
container.addChild(new Text(`${label}${formatMeta(meta, uiTheme)}`, 0, 0));
|
|
691
|
-
container.addChild(
|
|
703
|
+
container.addChild(
|
|
704
|
+
renderDeepInterviewAskQuestion(args.question, uiTheme) ??
|
|
705
|
+
new Markdown(args.question, 1, 0, mdTheme, accentStyle),
|
|
706
|
+
);
|
|
692
707
|
|
|
693
708
|
const options = args.options;
|
|
694
709
|
if (options?.length) {
|
|
@@ -752,7 +767,10 @@ export const askToolRenderer = {
|
|
|
752
767
|
container.addChild(
|
|
753
768
|
new Text(` ${uiTheme.fg("dim", branch)} ${statusIcon} ${uiTheme.fg("dim", `[${r.id}]`)}`, 0, 0),
|
|
754
769
|
);
|
|
755
|
-
container.addChild(
|
|
770
|
+
container.addChild(
|
|
771
|
+
renderDeepInterviewAskQuestion(r.question, uiTheme) ??
|
|
772
|
+
new Markdown(r.question, 3, 0, mdTheme, accentStyle),
|
|
773
|
+
);
|
|
756
774
|
|
|
757
775
|
const answerLines: string[] = [];
|
|
758
776
|
for (let j = 0; j < r.selectedOptions.length; j++) {
|
|
@@ -795,7 +813,10 @@ export const askToolRenderer = {
|
|
|
795
813
|
const header = renderStatusLine({ icon: hasSelection ? "success" : "warning", title: "Ask" }, uiTheme);
|
|
796
814
|
const container = new Container();
|
|
797
815
|
container.addChild(new Text(header, 0, 0));
|
|
798
|
-
container.addChild(
|
|
816
|
+
container.addChild(
|
|
817
|
+
renderDeepInterviewAskQuestion(details.question, uiTheme) ??
|
|
818
|
+
new Markdown(details.question, 1, 0, mdTheme, accentStyle),
|
|
819
|
+
);
|
|
799
820
|
|
|
800
821
|
const answerLines: string[] = [];
|
|
801
822
|
if (details.selectedOptions && details.selectedOptions.length > 0) {
|