@gajae-code/coding-agent 0.5.0 → 0.5.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 +19 -0
- package/dist/types/async/job-manager.d.ts +26 -0
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/list-models.d.ts +6 -0
- package/dist/types/commands/gc.d.ts +26 -0
- package/dist/types/config/file-lock-gc.d.ts +5 -0
- package/dist/types/config/file-lock.d.ts +7 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/defaults/gjc/extensions/grok-build/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/index.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.d.ts +25 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +27 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.d.ts +8 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.d.ts +5 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +10 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.d.ts +38 -0
- package/dist/types/defaults/gjc-grok-cli.d.ts +5 -0
- package/dist/types/extensibility/extensions/index.d.ts +1 -0
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +35 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +103 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +112 -0
- package/dist/types/gjc-runtime/gc-render.d.ts +6 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +134 -0
- package/dist/types/gjc-runtime/ledger-event-renderer.d.ts +68 -0
- package/dist/types/gjc-runtime/team-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +11 -0
- package/dist/types/gjc-runtime/tmux-gc.d.ts +7 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +13 -0
- package/dist/types/harness-control-plane/gc-adapter.d.ts +3 -0
- package/dist/types/harness-control-plane/owner.d.ts +7 -0
- package/dist/types/harness-control-plane/storage.d.ts +20 -0
- package/dist/types/modes/components/hook-selector.d.ts +7 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/rpc/rpc-mode.d.ts +16 -1
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +13 -0
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +25 -0
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/blob-store.d.ts +39 -3
- package/dist/types/skill-state/workflow-hud.d.ts +14 -0
- package/dist/types/tools/ask.d.ts +15 -1
- package/dist/types/tools/subagent.d.ts +6 -0
- package/package.json +7 -7
- package/src/async/job-manager.ts +52 -0
- package/src/cli/args.ts +3 -0
- package/src/cli/auth-broker-cli.ts +1 -0
- package/src/cli/list-models.ts +13 -1
- package/src/cli.ts +1 -0
- package/src/commands/gc.ts +22 -0
- package/src/commands/harness.ts +7 -3
- package/src/config/file-lock-gc.ts +181 -0
- package/src/config/file-lock.ts +14 -0
- package/src/config/model-profiles.ts +24 -15
- package/src/coordinator/contract.ts +1 -0
- package/src/coordinator-mcp/server.ts +459 -3
- package/src/defaults/gjc/agent.models.grok-cli.yml +36 -0
- package/src/defaults/gjc/extensions/grok-build/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-build/package.json +7 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +39 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/package.json +8 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/index.ts +1 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +155 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/payload/sanitize.ts +361 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/billing.ts +57 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +99 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +50 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/usage.ts +56 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +36 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/errors.ts +44 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +131 -113
- package/src/defaults/gjc/skills/deep-interview/lateral-review-panel.md +49 -0
- package/src/defaults/gjc-defaults.ts +7 -0
- package/src/defaults/gjc-grok-cli.ts +22 -0
- package/src/extensibility/extensions/index.ts +1 -0
- package/src/extensibility/extensions/prefix-command-bridge.ts +128 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +417 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +18 -26
- package/src/gjc-runtime/deep-interview-state.ts +324 -0
- package/src/gjc-runtime/gc-render.ts +70 -0
- package/src/gjc-runtime/gc-runtime.ts +403 -0
- package/src/gjc-runtime/ledger-event-renderer.ts +164 -0
- package/src/gjc-runtime/ralplan-runtime.ts +58 -7
- package/src/gjc-runtime/state-renderer.ts +12 -3
- package/src/gjc-runtime/state-runtime.ts +46 -29
- package/src/gjc-runtime/team-gc.ts +49 -0
- package/src/gjc-runtime/team-runtime.ts +179 -2
- package/src/gjc-runtime/tmux-common.ts +14 -0
- package/src/gjc-runtime/tmux-gc.ts +176 -0
- package/src/gjc-runtime/tmux-sessions.ts +49 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +12 -0
- package/src/harness-control-plane/gc-adapter.ts +184 -0
- package/src/harness-control-plane/owner.ts +11 -0
- package/src/harness-control-plane/storage.ts +70 -0
- package/src/internal-urls/docs-index.generated.ts +14 -8
- package/src/main.ts +7 -2
- package/src/modes/components/hook-selector.ts +19 -0
- package/src/modes/components/model-selector.ts +25 -8
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/controllers/command-controller.ts +25 -6
- package/src/modes/controllers/extension-ui-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/rpc/rpc-mode.ts +151 -33
- package/src/modes/shared/agent-wire/command-dispatch.ts +278 -261
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +30 -1
- package/src/modes/shared/agent-wire/session-registry.ts +109 -0
- package/src/modes/shared/agent-wire/unattended-action-policy.ts +24 -0
- package/src/modes/shared/agent-wire/unattended-run-controller.ts +23 -3
- package/src/modes/shared/agent-wire/unattended-session.ts +16 -1
- package/src/sdk.ts +17 -3
- package/src/session/agent-session.ts +77 -8
- package/src/session/blob-store.ts +59 -3
- package/src/session/session-manager.ts +4 -4
- package/src/setup/hermes/templates/operator-instructions.v1.md +7 -1
- package/src/skill-state/workflow-hud.ts +106 -10
- package/src/slash-commands/builtin-registry.ts +3 -2
- package/src/task/executor.ts +9 -0
- package/src/tools/ask.ts +56 -1
- package/src/tools/job.ts +3 -2
- package/src/tools/monitor.ts +36 -1
- package/src/tools/subagent-render.ts +9 -0
- package/src/tools/subagent.ts +26 -2
|
@@ -21,6 +21,8 @@ interface RalplanHudState extends WorkflowGateHudState {
|
|
|
21
21
|
stage?: string;
|
|
22
22
|
waiting?: string;
|
|
23
23
|
iteration?: number;
|
|
24
|
+
iterationFromIndex?: number;
|
|
25
|
+
stages?: string;
|
|
24
26
|
verdict?: string;
|
|
25
27
|
latestSummary?: string;
|
|
26
28
|
pendingApproval?: boolean;
|
|
@@ -100,6 +102,95 @@ export function buildDeepInterviewHudSummary(state: DeepInterviewHudState): Work
|
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
104
|
|
|
105
|
+
export interface DeepInterviewHudDeriveOptions {
|
|
106
|
+
phase?: string;
|
|
107
|
+
specStatus?: string;
|
|
108
|
+
updatedAt?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function diIsPlainObject(value: unknown): value is Record<string, unknown> {
|
|
112
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function latestScoredAmbiguity(rounds: unknown): number | undefined {
|
|
116
|
+
if (!Array.isArray(rounds)) return undefined;
|
|
117
|
+
for (let index = rounds.length - 1; index >= 0; index--) {
|
|
118
|
+
const round = rounds[index];
|
|
119
|
+
if (diIsPlainObject(round) && round.lifecycle === "scored" && typeof round.ambiguity === "number") {
|
|
120
|
+
return round.ambiguity;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function weakestDimensionFromTopology(
|
|
127
|
+
topology: Record<string, unknown>,
|
|
128
|
+
targetComponent: string | undefined,
|
|
129
|
+
): string | undefined {
|
|
130
|
+
if (!Array.isArray(topology.components)) return undefined;
|
|
131
|
+
const components = topology.components.filter(diIsPlainObject);
|
|
132
|
+
const dimensionOf = (component: Record<string, unknown>): string | undefined =>
|
|
133
|
+
typeof component.weakest_dimension === "string" && component.weakest_dimension.trim()
|
|
134
|
+
? component.weakest_dimension
|
|
135
|
+
: undefined;
|
|
136
|
+
if (targetComponent) {
|
|
137
|
+
const targeted = components.find(component => component.id === targetComponent && dimensionOf(component));
|
|
138
|
+
if (targeted) return dimensionOf(targeted);
|
|
139
|
+
}
|
|
140
|
+
const active = components.find(component => component.status !== "deferred" && dimensionOf(component));
|
|
141
|
+
if (active) return dimensionOf(active);
|
|
142
|
+
const any = components.find(component => dimensionOf(component));
|
|
143
|
+
return any ? dimensionOf(any) : undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Single source of deep-interview HUD derivation. Reads a complete (normalized)
|
|
148
|
+
* mode-state envelope so recorder, `gjc state write`, reconcile, seed, and handoff
|
|
149
|
+
* all produce identical chips. Topology-aware `target`/`weakest` come from
|
|
150
|
+
* `state.topology`; `legacy_missing` topology omits those chips (no synthetic values).
|
|
151
|
+
*/
|
|
152
|
+
export function deriveDeepInterviewHud(
|
|
153
|
+
payload: Record<string, unknown>,
|
|
154
|
+
options: DeepInterviewHudDeriveOptions = {},
|
|
155
|
+
): WorkflowHudSummary {
|
|
156
|
+
const stateField = diIsPlainObject(payload.state) ? payload.state : {};
|
|
157
|
+
const isNumber = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value);
|
|
158
|
+
const isArray = (value: unknown): value is unknown[] => Array.isArray(value);
|
|
159
|
+
const pick = <T>(key: string, guard: (value: unknown) => value is T): T | undefined => {
|
|
160
|
+
const value = stateField[key] ?? payload[key];
|
|
161
|
+
return guard(value) ? value : undefined;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const phase = options.phase ?? (typeof payload.current_phase === "string" ? payload.current_phase : undefined);
|
|
165
|
+
const rounds = pick("rounds", isArray);
|
|
166
|
+
const ambiguity = pick("current_ambiguity", isNumber) ?? latestScoredAmbiguity(rounds);
|
|
167
|
+
const threshold = pick("threshold", isNumber);
|
|
168
|
+
const rawTopology = diIsPlainObject(stateField.topology)
|
|
169
|
+
? stateField.topology
|
|
170
|
+
: diIsPlainObject(payload.topology)
|
|
171
|
+
? payload.topology
|
|
172
|
+
: undefined;
|
|
173
|
+
// `legacy_missing` topology was never confirmed: omit target/weakest even if stale fields linger.
|
|
174
|
+
const topology = rawTopology && rawTopology.status !== "legacy_missing" ? rawTopology : undefined;
|
|
175
|
+
const targetComponent =
|
|
176
|
+
topology && typeof topology.last_targeted_component_id === "string"
|
|
177
|
+
? topology.last_targeted_component_id
|
|
178
|
+
: undefined;
|
|
179
|
+
const weakestDimension = topology ? weakestDimensionFromTopology(topology, targetComponent) : undefined;
|
|
180
|
+
const specStatus = options.specStatus ?? (typeof payload.spec_status === "string" ? payload.spec_status : undefined);
|
|
181
|
+
|
|
182
|
+
return buildDeepInterviewHudSummary({
|
|
183
|
+
phase,
|
|
184
|
+
ambiguity,
|
|
185
|
+
threshold,
|
|
186
|
+
roundCount: rounds?.length,
|
|
187
|
+
targetComponent,
|
|
188
|
+
weakestDimension,
|
|
189
|
+
specStatus,
|
|
190
|
+
updatedAt: options.updatedAt ?? new Date().toISOString(),
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
103
194
|
export function buildRalplanHudSummary(state: RalplanHudState): WorkflowHudSummary {
|
|
104
195
|
const verdict = state.verdict?.toUpperCase();
|
|
105
196
|
const verdictSeverity =
|
|
@@ -118,7 +209,14 @@ export function buildRalplanHudSummary(state: RalplanHudState): WorkflowHudSumma
|
|
|
118
209
|
...gateChips(state, 6),
|
|
119
210
|
chip("stage", state.stage, 10),
|
|
120
211
|
chip("waiting", state.waiting, 20),
|
|
121
|
-
chip(
|
|
212
|
+
chip(
|
|
213
|
+
"iter",
|
|
214
|
+
(state.iterationFromIndex ?? state.iteration) === undefined
|
|
215
|
+
? undefined
|
|
216
|
+
: String(state.iterationFromIndex ?? state.iteration),
|
|
217
|
+
30,
|
|
218
|
+
),
|
|
219
|
+
chip("stages", state.stages, 35),
|
|
122
220
|
chip("verdict", verdict, 40, verdictSeverity),
|
|
123
221
|
]),
|
|
124
222
|
...(state.updatedAt ? { updated_at: state.updatedAt } : {}),
|
|
@@ -136,17 +234,15 @@ export function buildUltragoalHudSummary(state: UltragoalHudState): WorkflowHudS
|
|
|
136
234
|
chip("goals", `${complete}/${total}`, 10),
|
|
137
235
|
chip("current", state.currentGoal ? `${state.currentGoal.id}:${state.currentGoal.title}` : state.status, 20),
|
|
138
236
|
chip("status", state.status, 30, state.status === "complete" ? "success" : undefined),
|
|
237
|
+
chip(
|
|
238
|
+
"ledger",
|
|
239
|
+
state.latestLedgerEvent?.event
|
|
240
|
+
? [state.latestLedgerEvent.event, state.latestLedgerEvent.goalId].filter(Boolean).join(":")
|
|
241
|
+
: undefined,
|
|
242
|
+
35,
|
|
243
|
+
),
|
|
139
244
|
...gateChips(state, 40),
|
|
140
245
|
]),
|
|
141
|
-
details: state.latestLedgerEvent
|
|
142
|
-
? compactChips([
|
|
143
|
-
chip(
|
|
144
|
-
"ledger",
|
|
145
|
-
[state.latestLedgerEvent.event, state.latestLedgerEvent.goalId].filter(Boolean).join(":"),
|
|
146
|
-
100,
|
|
147
|
-
),
|
|
148
|
-
])
|
|
149
|
-
: undefined,
|
|
150
246
|
...(state.updatedAt ? { updated_at: state.updatedAt } : {}),
|
|
151
247
|
};
|
|
152
248
|
}
|
|
@@ -4,6 +4,7 @@ import type { ThinkingLevel } from "@gajae-code/agent-core";
|
|
|
4
4
|
import { type Model, modelsAreEqual } from "@gajae-code/ai";
|
|
5
5
|
import { getOAuthProviders } from "@gajae-code/ai/utils/oauth";
|
|
6
6
|
import { setProjectDir } from "@gajae-code/utils";
|
|
7
|
+
import { jobElapsedMs } from "../async";
|
|
7
8
|
import {
|
|
8
9
|
GJC_MODEL_ASSIGNMENT_TARGET_IDS,
|
|
9
10
|
GJC_MODEL_ASSIGNMENT_TARGETS,
|
|
@@ -506,14 +507,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
506
507
|
if (snapshot.running.length > 0) {
|
|
507
508
|
lines.push("", "Running Jobs");
|
|
508
509
|
for (const job of snapshot.running) {
|
|
509
|
-
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now
|
|
510
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(jobElapsedMs(job, now))}`);
|
|
510
511
|
lines.push(` ${job.label}`);
|
|
511
512
|
}
|
|
512
513
|
}
|
|
513
514
|
if (snapshot.recent.length > 0) {
|
|
514
515
|
lines.push("", "Recent Jobs");
|
|
515
516
|
for (const job of snapshot.recent) {
|
|
516
|
-
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(now
|
|
517
|
+
lines.push(` [${job.id}] ${job.type} (${job.status}) — ${formatDuration(jobElapsedMs(job, now))}`);
|
|
517
518
|
lines.push(` ${job.label}`);
|
|
518
519
|
}
|
|
519
520
|
}
|
package/src/task/executor.ts
CHANGED
|
@@ -1154,6 +1154,15 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
1154
1154
|
resolvedModel: model.id,
|
|
1155
1155
|
});
|
|
1156
1156
|
}
|
|
1157
|
+
// Record which model the subagent actually runs on (and any auth fallback,
|
|
1158
|
+
// see #985) so the subagent panel can surface it to the user.
|
|
1159
|
+
if (model) {
|
|
1160
|
+
AsyncJobManager.instance()?.updateSubagentModel?.(options.subagentId ?? id, {
|
|
1161
|
+
requestedModel: modelSubstitutionWarning?.requested ?? resolvedModelString,
|
|
1162
|
+
effectiveModel: resolvedModelString,
|
|
1163
|
+
modelFellBack: authFallbackUsed === true,
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1157
1166
|
if (model?.contextWindow && model.contextWindow > 0) {
|
|
1158
1167
|
progress.contextWindow = model.contextWindow;
|
|
1159
1168
|
}
|
package/src/tools/ask.ts
CHANGED
|
@@ -34,6 +34,8 @@ import {
|
|
|
34
34
|
renderDeepInterviewAskQuestion,
|
|
35
35
|
} from "../deep-interview/render-middleware";
|
|
36
36
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
37
|
+
import { appendOrMergeDeepInterviewRound } from "../gjc-runtime/deep-interview-recorder";
|
|
38
|
+
import { deepInterviewStatePath } from "../gjc-runtime/deep-interview-runtime";
|
|
37
39
|
import { gateAnswerToResult, questionToGate } from "../modes/shared/agent-wire/deep-interview-gate";
|
|
38
40
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
39
41
|
import askDescription from "../prompts/tools/ask.md" with { type: "text" };
|
|
@@ -50,15 +52,25 @@ const OptionItem = z.object({
|
|
|
50
52
|
label: z.string().describe("display label"),
|
|
51
53
|
});
|
|
52
54
|
|
|
55
|
+
/** Optional structured deep-interview round metadata; when present the round is recorded automatically. */
|
|
56
|
+
const DeepInterviewMeta = z.object({
|
|
57
|
+
round_id: z.string().describe("stable optional round identity").optional(),
|
|
58
|
+
round: z.number().int().nonnegative().describe("round number"),
|
|
59
|
+
component: z.string().min(1).describe("targeted topology component"),
|
|
60
|
+
dimension: z.string().min(1).describe("targeted clarity dimension"),
|
|
61
|
+
ambiguity: z.number().min(0).max(1).describe("ambiguity at ask time (0..1)"),
|
|
62
|
+
});
|
|
63
|
+
|
|
53
64
|
const QuestionItem = z.object({
|
|
54
65
|
id: z.string().describe("question id"),
|
|
55
66
|
question: z.string().describe("question text"),
|
|
56
67
|
options: z.array(OptionItem).describe("available options"),
|
|
57
68
|
multi: z.boolean().describe("allow multiple selections").optional(),
|
|
58
69
|
recommended: z.number().describe("recommended option index").optional(),
|
|
70
|
+
deepInterview: DeepInterviewMeta.describe("optional deep-interview round metadata").optional(),
|
|
59
71
|
});
|
|
60
72
|
|
|
61
|
-
const askSchema = z.object({
|
|
73
|
+
export const askSchema = z.object({
|
|
62
74
|
questions: z.array(QuestionItem).min(1).describe("questions to ask"),
|
|
63
75
|
});
|
|
64
76
|
|
|
@@ -456,6 +468,45 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
456
468
|
TERMINAL.sendNotification("Waiting for input");
|
|
457
469
|
}
|
|
458
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Record a resolved deep-interview round when the question carries structured
|
|
473
|
+
* metadata. The runtime owns durable record/merge semantics; this tool is only the
|
|
474
|
+
* caller. Best-effort: a state-write hiccup must not break the user's answer flow.
|
|
475
|
+
*/
|
|
476
|
+
async #recordDeepInterviewRound(
|
|
477
|
+
q: AskParams["questions"][number],
|
|
478
|
+
selectedOptions: string[],
|
|
479
|
+
customInput: string | undefined,
|
|
480
|
+
): Promise<void> {
|
|
481
|
+
const meta = q.deepInterview;
|
|
482
|
+
if (!meta) return;
|
|
483
|
+
try {
|
|
484
|
+
const cwd = this.session.cwd;
|
|
485
|
+
const sessionId = this.session.getSessionId?.() ?? undefined;
|
|
486
|
+
const statePath = deepInterviewStatePath(cwd, sessionId);
|
|
487
|
+
await appendOrMergeDeepInterviewRound(
|
|
488
|
+
cwd,
|
|
489
|
+
statePath,
|
|
490
|
+
{
|
|
491
|
+
round: meta.round,
|
|
492
|
+
round_id: meta.round_id,
|
|
493
|
+
questionId: q.id,
|
|
494
|
+
questionText: q.question,
|
|
495
|
+
component: meta.component,
|
|
496
|
+
dimension: meta.dimension,
|
|
497
|
+
ambiguity: meta.ambiguity,
|
|
498
|
+
selectedOptions,
|
|
499
|
+
customInput,
|
|
500
|
+
},
|
|
501
|
+
{ sessionId },
|
|
502
|
+
);
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.warn(
|
|
505
|
+
`ask: deep-interview round recording failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
459
510
|
async execute(
|
|
460
511
|
_toolCallId: string,
|
|
461
512
|
params: AskParams,
|
|
@@ -515,6 +566,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
515
566
|
options: q.options,
|
|
516
567
|
multi: q.multi,
|
|
517
568
|
recommended: q.recommended,
|
|
569
|
+
deepInterview: q.deepInterview,
|
|
518
570
|
};
|
|
519
571
|
const answer = await gateEmitter.emitGate(questionToGate(gateQuestion));
|
|
520
572
|
const decoded = gateAnswerToResult(gateQuestion, answer);
|
|
@@ -582,6 +634,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
582
634
|
context?.abort();
|
|
583
635
|
throw new ToolAbortError("Ask tool was cancelled by the user");
|
|
584
636
|
}
|
|
637
|
+
await this.#recordDeepInterviewRound(q, selectedOptions, customInput);
|
|
585
638
|
const details: AskToolDetails = {
|
|
586
639
|
question: q.question,
|
|
587
640
|
options: optionLabels,
|
|
@@ -644,6 +697,8 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
644
697
|
customInput,
|
|
645
698
|
};
|
|
646
699
|
|
|
700
|
+
await this.#recordDeepInterviewRound(q, selectedOptions, customInput);
|
|
701
|
+
|
|
647
702
|
if (navAction === "back") {
|
|
648
703
|
questionIndex = Math.max(0, questionIndex - 1);
|
|
649
704
|
continue;
|
package/src/tools/job.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Component } from "@gajae-code/tui";
|
|
|
3
3
|
import { Text } from "@gajae-code/tui";
|
|
4
4
|
import { prompt } from "@gajae-code/utils";
|
|
5
5
|
import * as z from "zod/v4";
|
|
6
|
-
import { type AsyncJob, AsyncJobManager, isBackgroundJobSupportEnabled } from "../async";
|
|
6
|
+
import { type AsyncJob, AsyncJobManager, isBackgroundJobSupportEnabled, jobElapsedMs } from "../async";
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import type { Theme } from "../modes/theme/theme";
|
|
9
9
|
import jobDescription from "../prompts/tools/job.md" with { type: "text" };
|
|
@@ -257,6 +257,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
257
257
|
status: string;
|
|
258
258
|
label: string;
|
|
259
259
|
startTime: number;
|
|
260
|
+
endTime?: number;
|
|
260
261
|
resultText?: string;
|
|
261
262
|
errorText?: string;
|
|
262
263
|
}[],
|
|
@@ -270,7 +271,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
270
271
|
type: latest.type,
|
|
271
272
|
status: latest.status as JobSnapshot["status"],
|
|
272
273
|
label: latest.label,
|
|
273
|
-
durationMs:
|
|
274
|
+
durationMs: jobElapsedMs(latest, now),
|
|
274
275
|
...(latest.resultText ? { resultText: latest.resultText } : {}),
|
|
275
276
|
...(latest.errorText ? { errorText: latest.errorText } : {}),
|
|
276
277
|
};
|
package/src/tools/monitor.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { logger, prompt } from "@gajae-code/utils";
|
|
|
3
3
|
import * as z from "zod/v4";
|
|
4
4
|
import { AsyncJobManager, isBackgroundJobSupportEnabled } from "../async";
|
|
5
5
|
import monitorDescription from "../prompts/tools/monitor.md" with { type: "text" };
|
|
6
|
+
import { truncateTail } from "../session/streaming-output";
|
|
6
7
|
import { BashTool } from "./bash";
|
|
7
8
|
import type { ToolSession } from "./index";
|
|
8
9
|
import { ToolError } from "./tool-errors";
|
|
@@ -48,6 +49,8 @@ export interface MonitorToolDetails {
|
|
|
48
49
|
|
|
49
50
|
const MONITOR_LABEL_MAX = 120;
|
|
50
51
|
const MAX_PENDING_MONITOR_NOTIFICATIONS = 3;
|
|
52
|
+
const MONITOR_NOTIFICATION_LINE_MAX_BYTES = 16 * 1024;
|
|
53
|
+
const MONITOR_NOTIFICATION_LINE_MAX_LINES = 20;
|
|
51
54
|
|
|
52
55
|
function buildMonitorLabel(params: MonitorParams): string {
|
|
53
56
|
const base = `[monitor:${params.kind}] ${params.description}`;
|
|
@@ -55,6 +58,34 @@ function buildMonitorLabel(params: MonitorParams): string {
|
|
|
55
58
|
return `${base.slice(0, MONITOR_LABEL_MAX - 3)}...`;
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
function formatMonitorNotificationLine(line: string): {
|
|
62
|
+
content: string;
|
|
63
|
+
truncated: boolean;
|
|
64
|
+
totalBytes: number;
|
|
65
|
+
outputBytes: number;
|
|
66
|
+
} {
|
|
67
|
+
const truncation = truncateTail(line, {
|
|
68
|
+
maxBytes: MONITOR_NOTIFICATION_LINE_MAX_BYTES,
|
|
69
|
+
maxLines: MONITOR_NOTIFICATION_LINE_MAX_LINES,
|
|
70
|
+
});
|
|
71
|
+
const outputBytes = truncation.outputBytes ?? truncation.totalBytes;
|
|
72
|
+
if (!truncation.truncated) {
|
|
73
|
+
return {
|
|
74
|
+
content: truncation.content,
|
|
75
|
+
truncated: false,
|
|
76
|
+
totalBytes: truncation.totalBytes,
|
|
77
|
+
outputBytes,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const notice = `[Monitor output truncated: showing last ${outputBytes} of ${truncation.totalBytes} bytes]`;
|
|
81
|
+
return {
|
|
82
|
+
content: `${truncation.content}\n${notice}`,
|
|
83
|
+
truncated: true,
|
|
84
|
+
totalBytes: truncation.totalBytes,
|
|
85
|
+
outputBytes,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
58
89
|
export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolDetails> {
|
|
59
90
|
readonly name = "monitor";
|
|
60
91
|
readonly label = "Monitor";
|
|
@@ -130,7 +161,8 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
|
|
|
130
161
|
if (controller.closed) return;
|
|
131
162
|
const notificationId = `${jobId}:${sequence}`;
|
|
132
163
|
const suffix = count > 0 ? `\n(+${count} earlier lines)` : "";
|
|
133
|
-
const
|
|
164
|
+
const notificationLine = formatMonitorNotificationLine(line);
|
|
165
|
+
const content = `<task-notification>\nMonitor task ${jobId} (${params.kind}: ${params.description}) emitted latest state:\n${notificationLine.content}${suffix}\n</task-notification>`;
|
|
134
166
|
const details = {
|
|
135
167
|
taskId: jobId,
|
|
136
168
|
kind: params.kind,
|
|
@@ -139,6 +171,9 @@ export class MonitorTool implements AgentTool<typeof monitorSchema, MonitorToolD
|
|
|
139
171
|
notificationId,
|
|
140
172
|
sequence,
|
|
141
173
|
coalescedCount: count,
|
|
174
|
+
outputTruncated: notificationLine.truncated,
|
|
175
|
+
outputTotalBytes: notificationLine.totalBytes,
|
|
176
|
+
outputBytes: notificationLine.outputBytes,
|
|
142
177
|
};
|
|
143
178
|
pendingNotifications += 1;
|
|
144
179
|
if (pendingNotifications > MAX_PENDING_MONITOR_NOTIFICATIONS) {
|
|
@@ -66,6 +66,15 @@ function renderSubagentSnapshot(
|
|
|
66
66
|
if (snapshot.agent && snapshot.agent !== "unknown") {
|
|
67
67
|
lines.push(` ${theme.fg("dim", `Agent: ${snapshot.agent} (${snapshot.agentSource})`)}`);
|
|
68
68
|
}
|
|
69
|
+
if (snapshot.effectiveModel) {
|
|
70
|
+
if (snapshot.modelFellBack && snapshot.requestedModel) {
|
|
71
|
+
lines.push(
|
|
72
|
+
` ${theme.fg("warning", `Model: ${snapshot.effectiveModel} (requested ${snapshot.requestedModel}, fell back — no credentials)`)}`,
|
|
73
|
+
);
|
|
74
|
+
} else {
|
|
75
|
+
lines.push(` ${theme.fg("dim", `Model: ${snapshot.effectiveModel}`)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
69
78
|
if (snapshot.description) lines.push(` ${theme.fg("dim", `Description: ${snapshot.description}`)}`);
|
|
70
79
|
if (snapshot.outputRef) lines.push(` ${theme.fg("dim", `Output: ${snapshot.outputRef}`)}`);
|
|
71
80
|
if (snapshot.assignment) {
|
package/src/tools/subagent.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as path from "node:path";
|
|
|
2
2
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
3
3
|
import { prompt } from "@gajae-code/utils";
|
|
4
4
|
import * as z from "zod/v4";
|
|
5
|
-
import { type AsyncJob, AsyncJobManager, type SubagentRecord } from "../async";
|
|
5
|
+
import { type AsyncJob, AsyncJobManager, jobElapsedMs, type SubagentRecord } from "../async";
|
|
6
6
|
import subagentDescription from "../prompts/tools/subagent.md" with { type: "text" };
|
|
7
7
|
import type { AgentProgress, AgentSource } from "../task/types";
|
|
8
8
|
import { Ellipsis, truncateToWidth } from "../tui";
|
|
@@ -67,6 +67,12 @@ export interface SubagentSnapshot {
|
|
|
67
67
|
progress?: AgentProgress;
|
|
68
68
|
/** True when a live in-session progress producer exists for this subagent. */
|
|
69
69
|
liveProgressAvailable?: boolean;
|
|
70
|
+
/** Model the subagent actually runs on (after any auth fallback). */
|
|
71
|
+
effectiveModel?: string;
|
|
72
|
+
/** Model originally requested via role/preset mapping; differs from effective on fallback. */
|
|
73
|
+
requestedModel?: string;
|
|
74
|
+
/** True when the requested model lacked credentials and fell back to the parent model. */
|
|
75
|
+
modelFellBack?: boolean;
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
export interface SubagentToolDetails {
|
|
@@ -508,6 +514,13 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
508
514
|
lines.push(`### ${snapshot.id} — ${snapshot.status}`);
|
|
509
515
|
if (snapshot.jobId !== snapshot.id) lines.push(`Job: ${snapshot.jobId}`);
|
|
510
516
|
if (snapshot.agent) lines.push(`Agent: ${snapshot.agent} (${snapshot.agentSource})`);
|
|
517
|
+
if (snapshot.effectiveModel) {
|
|
518
|
+
lines.push(
|
|
519
|
+
snapshot.modelFellBack && snapshot.requestedModel
|
|
520
|
+
? `Model: ${snapshot.effectiveModel} (requested ${snapshot.requestedModel}, fell back — no credentials)`
|
|
521
|
+
: `Model: ${snapshot.effectiveModel}`,
|
|
522
|
+
);
|
|
523
|
+
}
|
|
511
524
|
if (snapshot.description) lines.push(`Description: ${snapshot.description}`);
|
|
512
525
|
if (snapshot.outputRef) lines.push(`Output: ${snapshot.outputRef}`);
|
|
513
526
|
if (snapshot.assignment) lines.push("Assignment:", "```", snapshot.assignment, "```");
|
|
@@ -584,9 +597,19 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
584
597
|
durationMs: 0,
|
|
585
598
|
...(verifiedOutputIds.has(record.subagentId) ? { outputRef: `agent://${record.subagentId}` } : {}),
|
|
586
599
|
...liveFields,
|
|
600
|
+
...this.#modelFields(record),
|
|
587
601
|
};
|
|
588
602
|
}
|
|
589
603
|
|
|
604
|
+
#modelFields(record?: SubagentRecord): Partial<SubagentSnapshot> {
|
|
605
|
+
if (!record) return {};
|
|
606
|
+
const fields: Partial<SubagentSnapshot> = {};
|
|
607
|
+
if (record.effectiveModel) fields.effectiveModel = record.effectiveModel;
|
|
608
|
+
if (record.requestedModel) fields.requestedModel = record.requestedModel;
|
|
609
|
+
if (record.modelFellBack) fields.modelFellBack = true;
|
|
610
|
+
return fields;
|
|
611
|
+
}
|
|
612
|
+
|
|
590
613
|
#snapshot(
|
|
591
614
|
job: AsyncJob,
|
|
592
615
|
timedOut = false,
|
|
@@ -608,7 +631,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
608
631
|
label: sanitizeText(job.label, RECEIPT_PREVIEW_WIDTH),
|
|
609
632
|
agent: subagent?.agent ?? "unknown",
|
|
610
633
|
agentSource: subagent?.agentSource ?? "bundled",
|
|
611
|
-
durationMs:
|
|
634
|
+
durationMs: jobElapsedMs(job),
|
|
612
635
|
...(subagent?.description ? { description: sanitizeText(subagent.description, RECEIPT_PREVIEW_WIDTH) } : {}),
|
|
613
636
|
...(verbosity === "full" && subagent?.assignment
|
|
614
637
|
? { assignment: sanitizeText(subagent.assignment, FULL_PREVIEW_WIDTH) }
|
|
@@ -622,6 +645,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
622
645
|
: {}),
|
|
623
646
|
...(outputRef ? { outputRef } : {}),
|
|
624
647
|
...(runningTimeoutGuidance ? { guidance: runningTimeoutGuidance } : {}),
|
|
648
|
+
...this.#modelFields(record),
|
|
625
649
|
};
|
|
626
650
|
}
|
|
627
651
|
|