@yagni-app/code-staging 1.2.14-staging.2204.1 → 1.2.14-staging.2213.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.
|
@@ -238,6 +238,18 @@ export declare class PipelineStageError extends Error {
|
|
|
238
238
|
readonly partial: StageResult[];
|
|
239
239
|
constructor(stageId: string, message: string, partial: StageResult[]);
|
|
240
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* The silent-402 landmine: when the workspace runs out of credits mid-run, the
|
|
243
|
+
* proxy 402s every model request, the child pi exits 0 with an EMPTY transcript,
|
|
244
|
+
* and the run otherwise dies as a confusing `no_changes` / empty-handoff / false
|
|
245
|
+
* "clean" stop with the real cause buried in stderr. Detect it and stop with an
|
|
246
|
+
* actionable message instead. Gated on the stage having produced no real output
|
|
247
|
+
* (or having failed outright) so a healthy stage whose stderr happens to contain
|
|
248
|
+
* a matching token can never trip it. A Run that spent its own budget stops the
|
|
249
|
+
* same way, with its own message. The session lane's turn reads it too
|
|
250
|
+
* (`sessionLane.ts` `turnStop`).
|
|
251
|
+
*/
|
|
252
|
+
export declare function creditExhaustionReason(r: StageResult): string | undefined;
|
|
241
253
|
/**
|
|
242
254
|
* The resilience policy for a run's stages. With a deadline, a stage that
|
|
243
255
|
* spent its wall clock spent the build's time: a retry would only get the
|
|
@@ -155,9 +155,10 @@ const RUN_REQUEST_LIMIT_RE = /run_request_limit_exhausted|This Run has used its
|
|
|
155
155
|
* actionable message instead. Gated on the stage having produced no real output
|
|
156
156
|
* (or having failed outright) so a healthy stage whose stderr happens to contain
|
|
157
157
|
* a matching token can never trip it. A Run that spent its own budget stops the
|
|
158
|
-
* same way, with its own message.
|
|
158
|
+
* same way, with its own message. The session lane's turn reads it too
|
|
159
|
+
* (`sessionLane.ts` `turnStop`).
|
|
159
160
|
*/
|
|
160
|
-
function creditExhaustionReason(r) {
|
|
161
|
+
export function creditExhaustionReason(r) {
|
|
161
162
|
if (r.finalOutput.trim().length > 0 && !isFailed(r))
|
|
162
163
|
return undefined;
|
|
163
164
|
const hay = `${r.errorMessage ?? ""}\n${r.stderr}`;
|
|
@@ -118,9 +118,16 @@ export declare function parseSessionReport(text: string | null, known?: Readonly
|
|
|
118
118
|
/** The report's checks and its words on `result.json`: the model's own account, never the host's proof. */
|
|
119
119
|
export declare function withReport(attempt: AttemptResult, report: SessionReport | null): AttemptResult;
|
|
120
120
|
/**
|
|
121
|
-
* The turn's
|
|
122
|
-
*
|
|
123
|
-
*
|
|
121
|
+
* The end of a turn's child's stderr as a note carries it: its last lines
|
|
122
|
+
* without the CLI's own `Skills:` line, secrets scrubbed, clipped; empty when
|
|
123
|
+
* nothing else is left. The child's stderr is the parent's alone, so without
|
|
124
|
+
* this a turn that died said nothing on the CLI's stderr either.
|
|
125
|
+
*/
|
|
126
|
+
export declare function childStderrTail(stderr: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* The turn's stop as the result names it: aborted, its clock, a model proxy
|
|
129
|
+
* that refused it for what the run may spend, a child that failed, a writing
|
|
130
|
+
* turn whose tools mostly errored (the cloud lane's health gate), else clean.
|
|
124
131
|
*/
|
|
125
132
|
export declare function turnStop(run: StageResult, writes: boolean, aborted: boolean): {
|
|
126
133
|
stop: StopReason;
|
|
@@ -44,9 +44,11 @@ import { scanChange } from "./changeScan.js";
|
|
|
44
44
|
import { completeCloudSession, RESULT_FILE_NAME, } from "./cloud.js";
|
|
45
45
|
import { finishExec } from "./finish.js";
|
|
46
46
|
import { MISSION_CHECKS_FILE_NAME, readMissionChecksFile } from "./missionChecks.js";
|
|
47
|
+
import { creditExhaustionReason } from "./orchestrator.js";
|
|
47
48
|
import { WORK_A_TICKET_SKILL } from "./personas.js";
|
|
48
49
|
import { extractPlanJson } from "./planOutput.js";
|
|
49
50
|
import { runStage as defaultRunStage } from "./runner.js";
|
|
51
|
+
import { scrubSecrets } from "./scrubSecrets.js";
|
|
50
52
|
import { DECISIONS_READ_TOOL, PLAN_JSON_CONTRACT } from "./stages.js";
|
|
51
53
|
import { STAGE_WRAP_UP_GRACE_MS } from "./stageClock.js";
|
|
52
54
|
import { MIN_TOOL_CALLS_FOR_HEALTH, TOOL_ERROR_FAIL_RATE, } from "./types.js";
|
|
@@ -340,10 +342,28 @@ export function withReport(attempt, report) {
|
|
|
340
342
|
notes,
|
|
341
343
|
};
|
|
342
344
|
}
|
|
345
|
+
/** The most of a child's stderr a failed turn's note carries. */
|
|
346
|
+
const STDERR_NOTE_LINES = 8;
|
|
347
|
+
const STDERR_NOTE_CHARS = 600;
|
|
343
348
|
/**
|
|
344
|
-
* The turn's
|
|
345
|
-
*
|
|
346
|
-
*
|
|
349
|
+
* The end of a turn's child's stderr as a note carries it: its last lines
|
|
350
|
+
* without the CLI's own `Skills:` line, secrets scrubbed, clipped; empty when
|
|
351
|
+
* nothing else is left. The child's stderr is the parent's alone, so without
|
|
352
|
+
* this a turn that died said nothing on the CLI's stderr either.
|
|
353
|
+
*/
|
|
354
|
+
export function childStderrTail(stderr) {
|
|
355
|
+
const lines = scrubSecrets(stderr)
|
|
356
|
+
.split(/\r?\n/)
|
|
357
|
+
.map((line) => line.trim())
|
|
358
|
+
.filter((line) => line && !line.startsWith("Skills: "))
|
|
359
|
+
.slice(-STDERR_NOTE_LINES)
|
|
360
|
+
.join(" / ");
|
|
361
|
+
return lines.length > STDERR_NOTE_CHARS ? `...${lines.slice(-(STDERR_NOTE_CHARS - 3))}` : lines;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* The turn's stop as the result names it: aborted, its clock, a model proxy
|
|
365
|
+
* that refused it for what the run may spend, a child that failed, a writing
|
|
366
|
+
* turn whose tools mostly errored (the cloud lane's health gate), else clean.
|
|
347
367
|
*/
|
|
348
368
|
export function turnStop(run, writes, aborted) {
|
|
349
369
|
if (aborted || run.stopReason === "aborted")
|
|
@@ -351,8 +371,19 @@ export function turnStop(run, writes, aborted) {
|
|
|
351
371
|
if (run.timedOut) {
|
|
352
372
|
return { stop: "budget_exceeded", note: "The turn ran out of the run's time and was stopped." };
|
|
353
373
|
}
|
|
374
|
+
// The proxy's 402 or 429 for the workspace's credits or the run's own
|
|
375
|
+
// budget, in the pipeline's words, which the host reads as a spend stop
|
|
376
|
+
// (`spendStop.ts`) and hands to a person at once. Named only as an error,
|
|
377
|
+
// the host retried it twice into the same refusal and published the half
|
|
378
|
+
// build for review (staging NEARM-627, 2026-10-30).
|
|
379
|
+
const spent = creditExhaustionReason(run);
|
|
380
|
+
if (spent)
|
|
381
|
+
return { stop: "failed", note: `The session stopped: ${spent}` };
|
|
354
382
|
if (run.exitCode !== 0 || run.stopReason === "error") {
|
|
355
|
-
const
|
|
383
|
+
const said = scrubSecrets(run.errorMessage?.trim() ?? "") || childStderrTail(run.stderr);
|
|
384
|
+
const why = said
|
|
385
|
+
? `: ${said.slice(0, STDERR_NOTE_CHARS)}`
|
|
386
|
+
: ` (exit ${run.exitCode}, nothing on its stderr)`;
|
|
356
387
|
return { stop: "failed", note: `The session's turn ended on an error${why}.` };
|
|
357
388
|
}
|
|
358
389
|
if (writes &&
|
|
@@ -467,6 +498,10 @@ export async function runSessionTurn(input, deps = {}) {
|
|
|
467
498
|
const stop = turnStop(run, writes, input.signal?.aborted === true);
|
|
468
499
|
if (stop.note)
|
|
469
500
|
notes.push(stop.note);
|
|
501
|
+
// On the CLI's own stderr too, whose tail the host logs with a session
|
|
502
|
+
// that ended without its work.
|
|
503
|
+
if (stop.stop === "failed" && stop.note)
|
|
504
|
+
input.writeErr(stop.note);
|
|
470
505
|
const planned = input.stage !== "plan" || planText.trim().length > 0;
|
|
471
506
|
ran = {
|
|
472
507
|
result: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "1.2.14-staging.
|
|
3
|
+
"version": "1.2.14-staging.2213.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": "4cc9f6388171d4967fbf78689819f238baa1f5f8"
|
|
62
62
|
}
|