@yagni-app/code-staging 1.1.5-staging.1430.1 → 1.1.5-staging.1434.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.
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
* TUI sees is a complete picture of all tasks.
|
|
9
9
|
*
|
|
10
10
|
* The renderer half implements pi's per-tool rendering seam (`renderCall` /
|
|
11
|
-
* `renderResult`).
|
|
11
|
+
* `renderResult`). pi paints the call component above the result component
|
|
12
|
+
* for the life of the row, so the two split the work: the call header lists
|
|
13
|
+
* the tasks only while the arguments stream in, and hands off to the result
|
|
14
|
+
* rows the moment execution starts (the tool's first partial update fires at
|
|
15
|
+
* spawn). Collapsed-while-running is the two-line pattern: a stable
|
|
12
16
|
* `agent — task` title over a churning `↳ current tool` line. Completion is a
|
|
13
|
-
*
|
|
14
|
-
* the curated action log and the full report as markdown.
|
|
17
|
+
* receipt (`✓ agent · N tool uses · Xk tokens · Ys`) over a dim task line;
|
|
18
|
+
* expanded shows the curated action log and the full report as markdown.
|
|
19
|
+
* String assembly is
|
|
15
20
|
* kept in pure helpers over a minimal {@link RenderTheme} so tests run against
|
|
16
21
|
* plain text — renderer exceptions are swallowed by pi (silently degrading to
|
|
17
22
|
* the bare title bar), so everything here must stay boringly total.
|
|
@@ -116,8 +121,28 @@ interface RenderContextSlice {
|
|
|
116
121
|
state?: LiveRenderState;
|
|
117
122
|
invalidate: () => void;
|
|
118
123
|
}
|
|
119
|
-
/**
|
|
120
|
-
|
|
124
|
+
/** The slice of pi's ToolRenderContext the call renderer reads. */
|
|
125
|
+
interface CallContextSlice {
|
|
126
|
+
/** True from the moment pi invokes `execute` (tool-execution.ts re-renders the call on the flip). */
|
|
127
|
+
executionStarted?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* True from construction until a FINAL result lands. A row rebuilt from
|
|
130
|
+
* session history on resume never has `executionStarted` flipped (pi only
|
|
131
|
+
* marks it on the live tool_execution_start event) but does carry its final
|
|
132
|
+
* result, so this is the signal that keeps a replayed row from listing twice.
|
|
133
|
+
*/
|
|
134
|
+
isPartial?: boolean;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Title painted the moment the call streams in. While the model is still
|
|
138
|
+
* writing the arguments this is the only preview of what is being delegated,
|
|
139
|
+
* so it lists every task. Once execution starts (or, on a replayed row, once
|
|
140
|
+
* the final result is present) the result renderer paints a row per task
|
|
141
|
+
* (live line, then receipt) directly beneath, so the header collapses to its
|
|
142
|
+
* title line — otherwise every agent was listed twice, once here and once in
|
|
143
|
+
* the result, for the whole life of the call.
|
|
144
|
+
*/
|
|
145
|
+
export declare function renderSubagentCall(args: SubagentCallArgs | undefined, theme: RenderTheme, context: CallContextSlice | undefined): Component;
|
|
121
146
|
/**
|
|
122
147
|
* A prose body as markdown when the TUI's markdown theme is available.
|
|
123
148
|
* `getMarkdownTheme()` hands back a lazy proxy that only throws when a style is
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
* TUI sees is a complete picture of all tasks.
|
|
9
9
|
*
|
|
10
10
|
* The renderer half implements pi's per-tool rendering seam (`renderCall` /
|
|
11
|
-
* `renderResult`).
|
|
11
|
+
* `renderResult`). pi paints the call component above the result component
|
|
12
|
+
* for the life of the row, so the two split the work: the call header lists
|
|
13
|
+
* the tasks only while the arguments stream in, and hands off to the result
|
|
14
|
+
* rows the moment execution starts (the tool's first partial update fires at
|
|
15
|
+
* spawn). Collapsed-while-running is the two-line pattern: a stable
|
|
12
16
|
* `agent — task` title over a churning `↳ current tool` line. Completion is a
|
|
13
|
-
*
|
|
14
|
-
* the curated action log and the full report as markdown.
|
|
17
|
+
* receipt (`✓ agent · N tool uses · Xk tokens · Ys`) over a dim task line;
|
|
18
|
+
* expanded shows the curated action log and the full report as markdown.
|
|
19
|
+
* String assembly is
|
|
15
20
|
* kept in pure helpers over a minimal {@link RenderTheme} so tests run against
|
|
16
21
|
* plain text — renderer exceptions are swallowed by pi (silently degrading to
|
|
17
22
|
* the bare title bar), so everything here must stay boringly total.
|
|
@@ -226,6 +231,14 @@ export function receiptLine(p, theme) {
|
|
|
226
231
|
}
|
|
227
232
|
return `${theme.fg("success", "✓")} ${name} ${theme.fg("dim", `· ${parts.join(" · ")}`)}`;
|
|
228
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* The dim task line under a receipt. Once execution starts the call header
|
|
236
|
+
* no longer lists the tasks (see {@link renderSubagentCall}), so the receipt
|
|
237
|
+
* is the only place the reader can tell two same-named agents apart.
|
|
238
|
+
*/
|
|
239
|
+
function taskLine(p, theme) {
|
|
240
|
+
return theme.fg("dim", ` ${clip(p.task, 2 * TASK_PREVIEW_MAX)}`);
|
|
241
|
+
}
|
|
229
242
|
/** Aggregate receipt across parallel tasks. */
|
|
230
243
|
function totalLine(tasks, theme) {
|
|
231
244
|
const toolCalls = tasks.reduce((n, p) => n + p.toolCalls, 0);
|
|
@@ -262,19 +275,31 @@ export function formatWorkingMessage(tasks, now) {
|
|
|
262
275
|
const label = running > 0 && running < tasks.length ? `${running}/${tasks.length} subagents` : `${tasks.length} subagents`;
|
|
263
276
|
return `${label} · ${countToolUses(toolCalls)} · ${elapsed}`;
|
|
264
277
|
}
|
|
265
|
-
/**
|
|
266
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Title painted the moment the call streams in. While the model is still
|
|
280
|
+
* writing the arguments this is the only preview of what is being delegated,
|
|
281
|
+
* so it lists every task. Once execution starts (or, on a replayed row, once
|
|
282
|
+
* the final result is present) the result renderer paints a row per task
|
|
283
|
+
* (live line, then receipt) directly beneath, so the header collapses to its
|
|
284
|
+
* title line — otherwise every agent was listed twice, once here and once in
|
|
285
|
+
* the result, for the whole life of the call.
|
|
286
|
+
*/
|
|
287
|
+
export function renderSubagentCall(args, theme, context) {
|
|
267
288
|
const title = theme.fg("toolTitle", theme.bold("subagent"));
|
|
268
289
|
const a = args ?? {};
|
|
290
|
+
const resultOwnsRows = context?.executionStarted === true || context?.isPartial === false;
|
|
291
|
+
const listTasks = !resultOwnsRows;
|
|
269
292
|
if (a.tasks && a.tasks.length > 0) {
|
|
270
293
|
let text = `${title} ${theme.fg("accent", `${a.tasks.length} parallel tasks`)}`;
|
|
271
|
-
|
|
272
|
-
|
|
294
|
+
if (listTasks) {
|
|
295
|
+
for (const t of a.tasks) {
|
|
296
|
+
text += `\n ${theme.fg("accent", t.agent ?? "general")} ${theme.fg("dim", clip(t.task ?? "…", TASK_PREVIEW_MAX))}`;
|
|
297
|
+
}
|
|
273
298
|
}
|
|
274
299
|
return new Text(text, 0, 0);
|
|
275
300
|
}
|
|
276
301
|
let text = `${title} ${theme.fg("accent", a.agent ?? "general")}`;
|
|
277
|
-
if (a.task)
|
|
302
|
+
if (listTasks && a.task)
|
|
278
303
|
text += `\n ${theme.fg("dim", clip(a.task, 2 * TASK_PREVIEW_MAX))}`;
|
|
279
304
|
return new Text(text, 0, 0);
|
|
280
305
|
}
|
|
@@ -393,7 +418,7 @@ export function renderSubagentResult(result, options, theme, context) {
|
|
|
393
418
|
if (p.status === "running")
|
|
394
419
|
lines.push(...runningLines(p, theme, now, frame));
|
|
395
420
|
else
|
|
396
|
-
lines.push(receiptLine(p, theme));
|
|
421
|
+
lines.push(receiptLine(p, theme), taskLine(p, theme));
|
|
397
422
|
if (options.expanded)
|
|
398
423
|
lines.push(...actionLogLines(p, theme, EXPANDED_ACTIONS_SHOWN));
|
|
399
424
|
}
|
|
@@ -405,7 +430,7 @@ export function renderSubagentResult(result, options, theme, context) {
|
|
|
405
430
|
if (i > 0)
|
|
406
431
|
container.addChild(new Spacer(1));
|
|
407
432
|
container.addChild(new Text(receiptLine(p, theme), 0, 0));
|
|
408
|
-
container.addChild(new Text(
|
|
433
|
+
container.addChild(new Text(taskLine(p, theme), 0, 0));
|
|
409
434
|
if (p.errorMessage)
|
|
410
435
|
container.addChild(new Text(theme.fg("error", ` ${clip(p.errorMessage, 200)}`), 0, 0));
|
|
411
436
|
const log = actionLogLines(p, theme, EXPANDED_ACTIONS_SHOWN);
|
|
@@ -423,13 +448,13 @@ export function renderSubagentResult(result, options, theme, context) {
|
|
|
423
448
|
}
|
|
424
449
|
return container;
|
|
425
450
|
}
|
|
426
|
-
// Collapsed, final:
|
|
451
|
+
// Collapsed, final: receipt + task + a short report preview per task.
|
|
427
452
|
const lines = [];
|
|
428
453
|
let truncated = false;
|
|
429
454
|
for (const p of tasks) {
|
|
430
455
|
if (lines.length > 0)
|
|
431
456
|
lines.push("");
|
|
432
|
-
lines.push(receiptLine(p, theme));
|
|
457
|
+
lines.push(receiptLine(p, theme), taskLine(p, theme));
|
|
433
458
|
if (p.errorMessage)
|
|
434
459
|
lines.push(theme.fg("error", ` ${clip(p.errorMessage, 160)}`));
|
|
435
460
|
const report = (p.report ?? "").trim();
|
|
@@ -132,11 +132,26 @@ export interface MakeSubagentToolDeps {
|
|
|
132
132
|
childUsage?: ChildUsageHandle;
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
135
|
-
* The subagent lane's
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
135
|
+
* The subagent lane's wall-clock ceiling: two hours per child.
|
|
136
|
+
*
|
|
137
|
+
* The /go stage default (20 minutes) is sized for one pipeline step; a chat
|
|
138
|
+
* subagent is routinely handed a whole review, and at 20 minutes the runner
|
|
139
|
+
* killed three parallel reviewers mid-report and threw their findings away
|
|
140
|
+
* as "PARTIAL output". The wall clock is not the stall guard — the idle timer
|
|
141
|
+
* (4 minutes, deferred while a tool is in flight) is what catches a hung
|
|
142
|
+
* child — it only bounds a runaway that keeps emitting events, so it can be
|
|
143
|
+
* generous. Two hours is above what a child's context window sustains in
|
|
144
|
+
* practice, so it is effectively "let the task finish" while still bounding
|
|
145
|
+
* spend at roughly $20 per child on the mix that burned $2 in 20 minutes.
|
|
146
|
+
*/
|
|
147
|
+
export declare const SUBAGENT_WALL_TIMEOUT_MS: number;
|
|
148
|
+
/**
|
|
149
|
+
* The subagent lane's resilience policy: the /go defaults, except the wall
|
|
150
|
+
* clock is the lane's own ({@link SUBAGENT_WALL_TIMEOUT_MS}) and a wall-clock
|
|
151
|
+
* exhaustion is NOT retried (a two-hour burn means the task is too big — fail
|
|
152
|
+
* honestly instead of re-burning it per attempt; idle stalls stay retryable).
|
|
153
|
+
* Exported so a test can pin the composed policy the production path actually
|
|
154
|
+
* runs (every unit test injects runStageImpl instead).
|
|
140
155
|
*/
|
|
141
156
|
export declare function subagentRunPolicy(): ResiliencePolicy;
|
|
142
157
|
/**
|
|
@@ -463,14 +463,29 @@ const parameters = Type.Object({
|
|
|
463
463
|
})),
|
|
464
464
|
});
|
|
465
465
|
/**
|
|
466
|
-
* The subagent lane's
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
466
|
+
* The subagent lane's wall-clock ceiling: two hours per child.
|
|
467
|
+
*
|
|
468
|
+
* The /go stage default (20 minutes) is sized for one pipeline step; a chat
|
|
469
|
+
* subagent is routinely handed a whole review, and at 20 minutes the runner
|
|
470
|
+
* killed three parallel reviewers mid-report and threw their findings away
|
|
471
|
+
* as "PARTIAL output". The wall clock is not the stall guard — the idle timer
|
|
472
|
+
* (4 minutes, deferred while a tool is in flight) is what catches a hung
|
|
473
|
+
* child — it only bounds a runaway that keeps emitting events, so it can be
|
|
474
|
+
* generous. Two hours is above what a child's context window sustains in
|
|
475
|
+
* practice, so it is effectively "let the task finish" while still bounding
|
|
476
|
+
* spend at roughly $20 per child on the mix that burned $2 in 20 minutes.
|
|
477
|
+
*/
|
|
478
|
+
export const SUBAGENT_WALL_TIMEOUT_MS = 2 * 60 * 60_000;
|
|
479
|
+
/**
|
|
480
|
+
* The subagent lane's resilience policy: the /go defaults, except the wall
|
|
481
|
+
* clock is the lane's own ({@link SUBAGENT_WALL_TIMEOUT_MS}) and a wall-clock
|
|
482
|
+
* exhaustion is NOT retried (a two-hour burn means the task is too big — fail
|
|
483
|
+
* honestly instead of re-burning it per attempt; idle stalls stay retryable).
|
|
484
|
+
* Exported so a test can pin the composed policy the production path actually
|
|
485
|
+
* runs (every unit test injects runStageImpl instead).
|
|
471
486
|
*/
|
|
472
487
|
export function subagentRunPolicy() {
|
|
473
|
-
return { ...DEFAULT_RESILIENCE_POLICY, retryWallTimeout: false };
|
|
488
|
+
return { ...DEFAULT_RESILIENCE_POLICY, wallTimeoutMs: SUBAGENT_WALL_TIMEOUT_MS, retryWallTimeout: false };
|
|
474
489
|
}
|
|
475
490
|
/**
|
|
476
491
|
* The per-attempt telemetry adapter: one content-free error-sink row per
|
|
@@ -499,13 +514,14 @@ export function subagentTelemetry(env) {
|
|
|
499
514
|
}
|
|
500
515
|
export function makeSubagentTool(deps = {}) {
|
|
501
516
|
// The default runner rides the /go pipeline's resilience wrapper, so a chat
|
|
502
|
-
// subagent gets the same idle
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
//
|
|
508
|
-
//
|
|
517
|
+
// subagent gets the same idle ceiling and transient-only retry as a /go
|
|
518
|
+
// stage child (previously a hung subagent hung the tool call until the user
|
|
519
|
+
// pressed Esc). Three subagent-specific knobs ride the wrapper: the lane's
|
|
520
|
+
// own two-hour wall clock (SUBAGENT_WALL_TIMEOUT_MS), retryWallTimeout=false
|
|
521
|
+
// (a wall burn means the task is too big — fail honestly instead of
|
|
522
|
+
// re-burning it per attempt) and per-attempt telemetry into the error sink
|
|
523
|
+
// (a subagent failure previously left NO trace outside the tool result
|
|
524
|
+
// text). The write-gate keys on the resolved agent's tools,
|
|
509
525
|
// passed per task below — the synthetic stage id is "implement" and would
|
|
510
526
|
// otherwise block retry of a read-only verifier forever.
|
|
511
527
|
const run = deps.runStageImpl ??
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.1.5-staging.
|
|
3
|
+
"version": "1.1.5-staging.1434.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"turndown": "^7.2.4",
|
|
59
59
|
"typebox": "^1.3.15"
|
|
60
60
|
},
|
|
61
|
-
"yagniSourceSha": "
|
|
61
|
+
"yagniSourceSha": "04f866e8c724b482892ee608ca484ae2779c55ba"
|
|
62
62
|
}
|