@gajae-code/coding-agent 0.11.0 → 0.11.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 +9 -1
- package/dist/types/async/job-manager.d.ts +46 -7
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -2
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +1 -0
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/sdk/broker/ensure.d.ts +9 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +23 -0
- package/dist/types/tools/subagent-render.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +6 -1
- package/package.json +8 -8
- package/scripts/build-sdk-package-smoke.ts +5 -0
- package/src/async/job-manager.ts +406 -63
- package/src/gjc-runtime/ultragoal-guard.ts +55 -8
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +72 -8
- package/src/internal-urls/docs-index.generated.ts +2 -2
- package/src/modes/controllers/command-controller.ts +9 -15
- package/src/modes/controllers/extension-ui-controller.ts +48 -24
- package/src/modes/interactive-mode.ts +28 -16
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +7 -3
- package/src/modes/types.ts +2 -2
- package/src/sdk/broker/discovery.ts +20 -1
- package/src/sdk/broker/ensure.ts +82 -24
- package/src/sdk/bus/chat-effect-journal.ts +6 -4
- package/src/sdk/bus/discord-daemon.ts +25 -8
- package/src/sdk/bus/index.ts +57 -21
- package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
- package/src/sdk/bus/telegram-daemon.ts +102 -10
- package/src/session/agent-session.ts +176 -29
- package/src/task/index.ts +26 -16
- package/src/tools/subagent-render.ts +19 -8
- package/src/tools/subagent.ts +84 -32
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
} from "./render-utils";
|
|
24
24
|
import { type SubagentSnapshot, type SubagentToolDetails, subagentAwaitRenderedStateSignature } from "./subagent";
|
|
25
25
|
|
|
26
|
+
export { subagentAwaitRenderedStateSignature } from "./subagent";
|
|
27
|
+
|
|
26
28
|
const PREVIEW_LINES_COLLAPSED = 1;
|
|
27
29
|
const PREVIEW_LINES_EXPANDED = 4;
|
|
28
30
|
const PREVIEW_LINE_WIDTH = 80;
|
|
@@ -128,7 +130,7 @@ function renderSubagentStatusLine(snapshot: SubagentSnapshot, theme: Theme, spin
|
|
|
128
130
|
theme,
|
|
129
131
|
snapshot.status === "running" ? spinnerFrame : undefined,
|
|
130
132
|
);
|
|
131
|
-
const id = theme.fg("muted", snapshot.id);
|
|
133
|
+
const id = theme.fg("muted", truncateToWidth(replaceTabs(snapshot.id), PREVIEW_LINE_WIDTH, Ellipsis.Unicode));
|
|
132
134
|
const status = theme.fg("dim", snapshot.status);
|
|
133
135
|
const duration = theme.fg("dim", formatDuration(snapshot.durationMs));
|
|
134
136
|
return `${icon} ${id} ${status} ${duration}`;
|
|
@@ -195,7 +197,12 @@ function renderSubagentSnapshotBody(snapshot: SubagentSnapshot, expanded: boolea
|
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
199
|
|
|
198
|
-
if (snapshot.guidance)
|
|
200
|
+
if (snapshot.guidance) {
|
|
201
|
+
for (const line of getPreviewLines(snapshot.guidance, 1, PREVIEW_LINE_WIDTH, Ellipsis.Unicode)) {
|
|
202
|
+
lines.push(` ${theme.fg("dim", replaceTabs(line))}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
199
206
|
return lines;
|
|
200
207
|
}
|
|
201
208
|
|
|
@@ -218,6 +225,7 @@ export const subagentToolRenderer = {
|
|
|
218
225
|
}
|
|
219
226
|
|
|
220
227
|
const runningCount = subagents.filter(s => s.status === "running").length;
|
|
228
|
+
const interrupted = result.details?.interrupted === true;
|
|
221
229
|
|
|
222
230
|
// Each snapshot's rendered-state signature is constant for this component
|
|
223
231
|
// instance, so compute them at most once; the heavy per-subagent bodies are
|
|
@@ -232,11 +240,12 @@ export const subagentToolRenderer = {
|
|
|
232
240
|
// it is never gated by the heavy body cache.
|
|
233
241
|
const header = renderStatusLine(
|
|
234
242
|
{
|
|
235
|
-
icon: runningCount > 0 ? "info" : "success",
|
|
236
|
-
spinnerFrame: runningCount > 0 ? options.spinnerFrame : undefined,
|
|
237
|
-
title: "Subagent",
|
|
238
|
-
description:
|
|
239
|
-
|
|
243
|
+
icon: interrupted ? "warning" : runningCount > 0 ? "info" : "success",
|
|
244
|
+
spinnerFrame: !interrupted && runningCount > 0 ? options.spinnerFrame : undefined,
|
|
245
|
+
title: interrupted ? "Subagent await interrupted" : "Subagent",
|
|
246
|
+
description: interrupted
|
|
247
|
+
? "child subagents continue"
|
|
248
|
+
: runningCount > 0
|
|
240
249
|
? `awaiting ${runningCount} of ${subagents.length}`
|
|
241
250
|
: `${subagents.length} ${subagents.length === 1 ? "subagent" : "subagents"}`,
|
|
242
251
|
},
|
|
@@ -249,7 +258,9 @@ export const subagentToolRenderer = {
|
|
|
249
258
|
out.push(truncateToWidth(` ${theme.fg("dim", "(ctrl+s to observe sessions)")}`, width, Ellipsis.Omit));
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
snapshotSignatures ??= subagents.map(snapshot =>
|
|
261
|
+
snapshotSignatures ??= subagents.map(snapshot =>
|
|
262
|
+
subagentAwaitRenderedStateSignature([snapshot], result.details),
|
|
263
|
+
);
|
|
253
264
|
subagents.forEach((snapshot, index) => {
|
|
254
265
|
// Fresh per-subagent status line (cheap), then the cached heavy body.
|
|
255
266
|
out.push(
|
package/src/tools/subagent.ts
CHANGED
|
@@ -80,8 +80,17 @@ export interface SubagentSnapshot {
|
|
|
80
80
|
modelFellBack?: boolean;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export type SubagentAwaitOutcome = "completed" | "timed_out" | "interrupted";
|
|
84
|
+
|
|
85
|
+
const AWAIT_INTERRUPTED_GUIDANCE =
|
|
86
|
+
"Await interrupted; this subagent continues. Inspect its current state or cancel it only when necessary.";
|
|
87
|
+
|
|
83
88
|
export interface SubagentToolDetails {
|
|
84
89
|
subagents: SubagentSnapshot[];
|
|
90
|
+
/** Await outcome for a live await receipt; omitted when no wait was started. */
|
|
91
|
+
awaitOutcome?: SubagentAwaitOutcome;
|
|
92
|
+
/** True only when the parent await was interrupted; the child was not cancelled. */
|
|
93
|
+
interrupted?: true;
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentToolDetails> {
|
|
@@ -370,35 +379,50 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
370
379
|
// the viewport (the source of the await-panel repaint storms).
|
|
371
380
|
emitIfChanged(true);
|
|
372
381
|
|
|
373
|
-
let
|
|
382
|
+
let awaitOutcome: SubagentAwaitOutcome;
|
|
383
|
+
const { promise: timeoutPromise, resolve: resolveTimeout } = Promise.withResolvers<SubagentAwaitOutcome>();
|
|
384
|
+
const timeoutTimer = setTimeout(() => resolveTimeout("timed_out"), timeoutMs);
|
|
385
|
+
const completionPromise = Promise.all(runningJobs.map(job => job.promise)).then(() => "completed" as const);
|
|
386
|
+
let onAbort: (() => void) | undefined;
|
|
374
387
|
try {
|
|
375
|
-
const completionPromise = Promise.all(runningJobs.map(job => job.promise));
|
|
376
|
-
const timeoutPromise = Bun.sleep(timeoutMs).then(() => {
|
|
377
|
-
timedOut = true;
|
|
378
|
-
});
|
|
379
388
|
if (signal) {
|
|
380
|
-
const { promise: abortPromise, resolve:
|
|
381
|
-
|
|
382
|
-
signal.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
} finally {
|
|
386
|
-
signal.removeEventListener("abort", onAbort);
|
|
387
|
-
}
|
|
389
|
+
const { promise: abortPromise, resolve: resolveAbort } = Promise.withResolvers<SubagentAwaitOutcome>();
|
|
390
|
+
onAbort = () => resolveAbort("interrupted");
|
|
391
|
+
if (signal.aborted) onAbort();
|
|
392
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
393
|
+
awaitOutcome = await Promise.race([completionPromise, timeoutPromise, abortPromise]);
|
|
388
394
|
} else {
|
|
389
|
-
await Promise.race([completionPromise, timeoutPromise]);
|
|
395
|
+
awaitOutcome = await Promise.race([completionPromise, timeoutPromise]);
|
|
390
396
|
}
|
|
391
397
|
} finally {
|
|
392
|
-
|
|
398
|
+
clearTimeout(timeoutTimer);
|
|
399
|
+
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
393
400
|
if (progressTimer) clearInterval(progressTimer);
|
|
394
401
|
}
|
|
402
|
+
if (awaitOutcome !== "interrupted") {
|
|
403
|
+
const terminalWatchedJobIds = watchedJobIds.filter(jobId => {
|
|
404
|
+
const status = manager.getJob(jobId)?.status;
|
|
405
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
406
|
+
});
|
|
407
|
+
manager.acknowledgeDeliveries(terminalWatchedJobIds);
|
|
408
|
+
}
|
|
409
|
+
manager.unwatchJobs(watchedJobIds);
|
|
395
410
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
411
|
+
const finalRecords = this.#visibleRecordsByIds(
|
|
412
|
+
manager,
|
|
413
|
+
records.map(record => record.subagentId),
|
|
414
|
+
ownerFilter,
|
|
415
|
+
);
|
|
416
|
+
const finalNotFoundIds = [...new Set([...notFoundIds, ...records.map(record => record.subagentId)])].filter(
|
|
417
|
+
id => !finalRecords.some(record => record.subagentId === id),
|
|
418
|
+
);
|
|
419
|
+
return await this.#buildRecordResult(manager, finalRecords, {
|
|
420
|
+
title: awaitOutcome === "interrupted" ? "Subagent await interrupted" : "Subagent await",
|
|
421
|
+
notFoundIds: finalNotFoundIds,
|
|
422
|
+
timedOut: awaitOutcome === "timed_out",
|
|
400
423
|
verbosity: params.verbosity ?? "receipt",
|
|
401
424
|
attachLiveProgress: true,
|
|
425
|
+
awaitOutcome,
|
|
402
426
|
});
|
|
403
427
|
}
|
|
404
428
|
|
|
@@ -516,6 +540,7 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
516
540
|
timedOut?: boolean;
|
|
517
541
|
verbosity?: SubagentParams["verbosity"];
|
|
518
542
|
attachLiveProgress?: boolean;
|
|
543
|
+
awaitOutcome?: SubagentAwaitOutcome;
|
|
519
544
|
},
|
|
520
545
|
): Promise<AgentToolResult<SubagentToolDetails>> {
|
|
521
546
|
const verifiedOutputIds = await this.#verifiedOutputIds(records);
|
|
@@ -530,18 +555,34 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
530
555
|
for (const id of options.notFoundIds ?? []) {
|
|
531
556
|
snapshots.push(this.#missingSnapshot(id, "not_found", "No visible detached subagent matches this id."));
|
|
532
557
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
s
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
558
|
+
if (options.awaitOutcome !== "interrupted") {
|
|
559
|
+
manager.acknowledgeDeliveries(
|
|
560
|
+
snapshots
|
|
561
|
+
.filter(
|
|
562
|
+
s =>
|
|
563
|
+
s.status !== "running" &&
|
|
564
|
+
s.status !== "paused" &&
|
|
565
|
+
s.status !== "queued" &&
|
|
566
|
+
s.status !== "not_found",
|
|
567
|
+
)
|
|
568
|
+
.map(s => s.jobId),
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
if (options.awaitOutcome === "interrupted") {
|
|
572
|
+
for (const snapshot of snapshots) {
|
|
573
|
+
if (snapshot.status === "running" || snapshot.status === "paused" || snapshot.status === "queued") {
|
|
574
|
+
snapshot.guidance = AWAIT_INTERRUPTED_GUIDANCE;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return this.#buildSnapshotResult(snapshots, options.title, options.awaitOutcome);
|
|
542
579
|
}
|
|
543
580
|
|
|
544
|
-
#buildSnapshotResult(
|
|
581
|
+
#buildSnapshotResult(
|
|
582
|
+
snapshots: SubagentSnapshot[],
|
|
583
|
+
title: string,
|
|
584
|
+
awaitOutcome?: SubagentAwaitOutcome,
|
|
585
|
+
): AgentToolResult<SubagentToolDetails> {
|
|
545
586
|
const lines = [`## ${title} (${snapshots.length})`, ""];
|
|
546
587
|
for (const snapshot of snapshots) {
|
|
547
588
|
lines.push(`### ${snapshot.id} — ${snapshot.status}`);
|
|
@@ -571,7 +612,11 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
571
612
|
}
|
|
572
613
|
return {
|
|
573
614
|
content: [{ type: "text", text: lines.join("\n").trimEnd() }],
|
|
574
|
-
details: {
|
|
615
|
+
details: {
|
|
616
|
+
subagents: snapshots,
|
|
617
|
+
...(awaitOutcome ? { awaitOutcome } : {}),
|
|
618
|
+
...(awaitOutcome === "interrupted" ? { interrupted: true } : {}),
|
|
619
|
+
},
|
|
575
620
|
};
|
|
576
621
|
}
|
|
577
622
|
|
|
@@ -778,8 +823,15 @@ function previewJobOutput(
|
|
|
778
823
|
* countdown ticking is sacrificed by design; every real transition still changes
|
|
779
824
|
* the signature.
|
|
780
825
|
*/
|
|
781
|
-
export function subagentAwaitRenderedStateSignature(
|
|
782
|
-
|
|
826
|
+
export function subagentAwaitRenderedStateSignature(
|
|
827
|
+
subagents: readonly SubagentSnapshot[],
|
|
828
|
+
receipt?: Pick<SubagentToolDetails, "awaitOutcome" | "interrupted">,
|
|
829
|
+
): string {
|
|
830
|
+
return JSON.stringify({
|
|
831
|
+
awaitOutcome: receipt?.awaitOutcome ?? null,
|
|
832
|
+
interrupted: receipt?.interrupted === true,
|
|
833
|
+
subagents: subagents.map(canonicalizeSnapshotForSignature),
|
|
834
|
+
});
|
|
783
835
|
}
|
|
784
836
|
|
|
785
837
|
function canonicalizeSnapshotForSignature(snapshot: SubagentSnapshot): unknown {
|