@yagni-app/code 0.3.2 → 0.3.3
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/dist/cli.js +13 -0
- package/dist/extension/footer.d.ts +1 -1
- package/dist/extension/hooks.d.ts +111 -0
- package/dist/extension/hooks.js +666 -0
- package/dist/extension/index.d.ts +13 -6
- package/dist/extension/index.js +57 -7
- package/dist/extension/{approvedPrefixes.js → permission/approvedPrefixes.js} +1 -1
- package/dist/extension/permission/dbReadPolicy.d.ts +90 -0
- package/dist/extension/permission/dbReadPolicy.js +227 -0
- package/dist/extension/{execPolicy.js → permission/execPolicy.js} +41 -13
- package/dist/extension/{permission.d.ts → permission/gate.d.ts} +9 -2
- package/dist/extension/{permission.js → permission/gate.js} +103 -4
- package/dist/extension/{guardian.d.ts → permission/guardian.d.ts} +2 -2
- package/dist/extension/{guardian.js → permission/guardian.js} +1 -1
- package/dist/extension/permission/index.d.ts +14 -0
- package/dist/extension/permission/index.js +14 -0
- package/dist/extension/permission/packageManagerPolicy.d.ts +55 -0
- package/dist/extension/permission/packageManagerPolicy.js +170 -0
- package/dist/extension/pipeline/activityFeed.js +19 -5
- package/dist/extension/pipeline/checker.d.ts +99 -0
- package/dist/extension/pipeline/checker.js +238 -0
- package/dist/extension/pipeline/fanout.d.ts +116 -0
- package/dist/extension/pipeline/fanout.js +248 -0
- package/dist/extension/pipeline/fanoutBeats.d.ts +31 -0
- package/dist/extension/pipeline/fanoutBeats.js +86 -0
- package/dist/extension/pipeline/goCommand.d.ts +14 -0
- package/dist/extension/pipeline/goCommand.js +38 -1
- package/dist/extension/pipeline/headlessGo.d.ts +163 -0
- package/dist/extension/pipeline/headlessGo.js +333 -0
- package/dist/extension/pipeline/invocation.d.ts +7 -1
- package/dist/extension/pipeline/invocation.js +7 -1
- package/dist/extension/pipeline/mission.d.ts +55 -0
- package/dist/extension/pipeline/mission.js +70 -0
- package/dist/extension/pipeline/orchestrator.d.ts +48 -3
- package/dist/extension/pipeline/orchestrator.js +450 -9
- package/dist/extension/pipeline/personas.d.ts +16 -1
- package/dist/extension/pipeline/personas.js +117 -6
- package/dist/extension/pipeline/runSession.d.ts +45 -1
- package/dist/extension/pipeline/runState.d.ts +57 -12
- package/dist/extension/pipeline/runState.js +60 -18
- package/dist/extension/pipeline/runner.js +10 -1
- package/dist/extension/pipeline/stages.d.ts +84 -7
- package/dist/extension/pipeline/stages.js +166 -0
- package/dist/extension/pipeline/tierCap.d.ts +32 -0
- package/dist/extension/pipeline/tierCap.js +57 -0
- package/dist/extension/pipeline/types.d.ts +130 -1
- package/dist/extension/pipeline/types.js +17 -0
- package/dist/extension/pipeline/verify.d.ts +86 -3
- package/dist/extension/pipeline/verify.js +175 -6
- package/dist/extension/turnLog.d.ts +38 -0
- package/dist/extension/turnLog.js +93 -0
- package/dist/goHeadless.d.ts +75 -0
- package/dist/goHeadless.js +132 -0
- package/dist/paths.d.ts +9 -0
- package/dist/paths.js +12 -0
- package/package.json +2 -2
- /package/dist/extension/{approvedPrefixes.d.ts → permission/approvedPrefixes.d.ts} +0 -0
- /package/dist/extension/{execPolicy.d.ts → permission/execPolicy.d.ts} +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The implement diamond's RECORDING thread: pipeline progress in, run-session
|
|
3
|
+
* stage beats out (spec "Recording and the run surface").
|
|
4
|
+
*
|
|
5
|
+
* The pipeline already says everything the run surface needs — the partition
|
|
6
|
+
* verdict, each builder starting and finishing, each bounded fix turn — on the
|
|
7
|
+
* `onProgress` channel. This folds that stream into the additive
|
|
8
|
+
* `fanout` / `children` / `fixTurns` payloads on `POST /runs/:id/stages`, so the
|
|
9
|
+
* web run surface can render per-workstream rows without the pipeline growing a
|
|
10
|
+
* second reporting path.
|
|
11
|
+
*
|
|
12
|
+
* PURE and I/O-free, like `findings.ts` and `fanout.ts`: `apply` returns the beat
|
|
13
|
+
* to send (or null when the signal is not the diamond's), and the caller decides
|
|
14
|
+
* whether to POST it. Two properties are load-bearing:
|
|
15
|
+
*
|
|
16
|
+
* 1. **Recorded states only.** A child row exists only once its `stage_start`
|
|
17
|
+
* arrives, and it is `failed` only when the pipeline said the child died.
|
|
18
|
+
* There is no invented "pending", and no tick nobody earned.
|
|
19
|
+
* 2. **Every beat is a copy.** The roster keeps mutating as children finish, so
|
|
20
|
+
* a beat already handed to the (async, fail-soft) session must never change
|
|
21
|
+
* underneath it.
|
|
22
|
+
*/
|
|
23
|
+
/** The stage every fan-out beat belongs to: the diamond lives INSIDE implement. */
|
|
24
|
+
const IMPLEMENT = "implement";
|
|
25
|
+
export function makeFanoutBeats() {
|
|
26
|
+
// Name -> tier + claim count from the partition verdict, so a child row can
|
|
27
|
+
// carry them the moment that child starts.
|
|
28
|
+
const shape = new Map();
|
|
29
|
+
const children = [];
|
|
30
|
+
const fixTurns = [];
|
|
31
|
+
const roster = () => children.map((c) => ({ ...c }));
|
|
32
|
+
const upsert = (name, state) => {
|
|
33
|
+
const existing = children.find((c) => c.name === name);
|
|
34
|
+
if (existing) {
|
|
35
|
+
existing.state = state;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const meta = shape.get(name) ?? {};
|
|
39
|
+
children.push({
|
|
40
|
+
name,
|
|
41
|
+
...(meta.tier ? { tier: meta.tier } : {}),
|
|
42
|
+
...(meta.files !== undefined ? { files: meta.files } : {}),
|
|
43
|
+
state,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return { stage: IMPLEMENT, phase: "start", children: roster() };
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
apply(p) {
|
|
50
|
+
switch (p.kind) {
|
|
51
|
+
case "fanout": {
|
|
52
|
+
for (const w of p.workstreams ?? [])
|
|
53
|
+
shape.set(w.name, { tier: w.tier, files: w.files });
|
|
54
|
+
return {
|
|
55
|
+
stage: IMPLEMENT,
|
|
56
|
+
phase: "start",
|
|
57
|
+
fanout: {
|
|
58
|
+
mode: p.mode,
|
|
59
|
+
...(p.width ? { width: p.width } : {}),
|
|
60
|
+
reason: p.reason,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
case "stage_start":
|
|
65
|
+
if (p.stageId !== IMPLEMENT || !p.workstream)
|
|
66
|
+
return null;
|
|
67
|
+
return upsert(p.workstream, "running");
|
|
68
|
+
case "stage_done":
|
|
69
|
+
if (p.stageId !== IMPLEMENT || !p.workstream)
|
|
70
|
+
return null;
|
|
71
|
+
return upsert(p.workstream, p.degraded === "failed" ? "failed" : "done");
|
|
72
|
+
case "fix_turn": {
|
|
73
|
+
fixTurns.push({ turn: p.turn, findings: p.findings, reengaged: [...p.reengaged] });
|
|
74
|
+
return {
|
|
75
|
+
stage: IMPLEMENT,
|
|
76
|
+
phase: "start",
|
|
77
|
+
fixTurns: fixTurns.map((t) => ({ ...t, reengaged: [...t.reengaged] })),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=fanoutBeats.js.map
|
|
@@ -114,6 +114,20 @@ export interface RegisterGoDeps {
|
|
|
114
114
|
exists?: (path: string) => boolean;
|
|
115
115
|
/** Clock seam for registry rows + staleness. */
|
|
116
116
|
now?: () => number;
|
|
117
|
+
/**
|
|
118
|
+
* Environment the run-wide /go settings are read from (today: `go.fanout` via
|
|
119
|
+
* YAGNI_GO_FANOUT). Defaults to `process.env`; injected so a test can pin a
|
|
120
|
+
* benchmark lane without mutating the process.
|
|
121
|
+
*/
|
|
122
|
+
env?: NodeJS.ProcessEnv;
|
|
123
|
+
/**
|
|
124
|
+
* Is /ultra on right now? The same holder the subagent tool reads (wired in
|
|
125
|
+
* index.ts), consulted per call because /ultra can flip mid-session. It is the
|
|
126
|
+
* implement diamond's parallel ceiling: ultra runs the fan (and its fix turns)
|
|
127
|
+
* up to 8 wide, everything else stays at 4. Absent (an unwired embedder or a
|
|
128
|
+
* test) simply means "not ultra", which is today's behavior.
|
|
129
|
+
*/
|
|
130
|
+
isUltra?: () => boolean;
|
|
117
131
|
baseUrl?: string;
|
|
118
132
|
getToken?: () => string | undefined;
|
|
119
133
|
/**
|
|
@@ -63,6 +63,8 @@ import { existsSync } from "node:fs";
|
|
|
63
63
|
import { join } from "node:path";
|
|
64
64
|
import { eventToLine } from "./activity.js";
|
|
65
65
|
import { ActivityFeed, SPINNER_FRAMES } from "./activityFeed.js";
|
|
66
|
+
import { resolveFanoutMode } from "./fanout.js";
|
|
67
|
+
import { makeFanoutBeats } from "./fanoutBeats.js";
|
|
66
68
|
import { RunState } from "./runState.js";
|
|
67
69
|
import { aggregateRunUsage } from "./budget.js";
|
|
68
70
|
import { formatRunCostTable } from "./runCostTable.js";
|
|
@@ -78,7 +80,7 @@ import { runPipeline as defaultRunPipeline } from "./orchestrator.js";
|
|
|
78
80
|
import { composeAbortSignal } from "./resilience.js";
|
|
79
81
|
import { planResume } from "./resume.js";
|
|
80
82
|
import { activeRunCount, beginRun, classifyRunLiveness, findActiveRunByTicket, isRunInFlight, isTerminalStatus, lastJournalTs, loadRegistryRows, resolveMaxConcurrentRuns, settleRun, trackRunAbort, trackRunPromise, worktreesDir, } from "./runRegistry.js";
|
|
81
|
-
import { makeRunSession as defaultMakeRunSession } from "./runSession.js";
|
|
83
|
+
import { makeRunSession as defaultMakeRunSession, } from "./runSession.js";
|
|
82
84
|
import { recordSessionRun } from "../sessionRuns.js";
|
|
83
85
|
import { resolveTicketBrief as defaultResolveTicketBrief } from "./ticketResolution.js";
|
|
84
86
|
import { snapshotWorkspace as defaultSnapshotWorkspace } from "./workspace.js";
|
|
@@ -422,6 +424,13 @@ export function registerGoCommand(pi, deps = {}) {
|
|
|
422
424
|
const loadJournal = deps.loadJournal ?? ((sessionKey) => makeFileCheckpointStore(sessionKey).load());
|
|
423
425
|
const exists = deps.exists ?? existsSync;
|
|
424
426
|
const now = deps.now ?? Date.now;
|
|
427
|
+
// `go.fanout` (spec decision 8): `always` pins the implement diamond for
|
|
428
|
+
// benchmark / eval lanes; unset leaves the partitioner's conservative verdict
|
|
429
|
+
// as the only thing that decides.
|
|
430
|
+
const fanoutMode = resolveFanoutMode(deps.env ?? process.env);
|
|
431
|
+
// The dial is held, never its value: /ultra can be toggled between runs, so
|
|
432
|
+
// the pipeline calls it when it needs the ceiling rather than reading it here.
|
|
433
|
+
const isUltra = deps.isUltra;
|
|
425
434
|
// Task 8: no default — see the RegisterGoDeps doc comment for why (only
|
|
426
435
|
// index.ts's real wiring makes sense here; absent, `resolveCostText` falls
|
|
427
436
|
// back to the local client estimate).
|
|
@@ -559,6 +568,10 @@ export function registerGoCommand(pi, deps = {}) {
|
|
|
559
568
|
const feed = ctx.hasUI && !desktop ? new ActivityFeed(ticket) : undefined;
|
|
560
569
|
const startedAt = Date.now();
|
|
561
570
|
const run = ctx.hasUI && desktop ? new RunState({ runId: runShortId, ticket, startedAt }) : undefined;
|
|
571
|
+
// The implement diamond's recording thread. Per RUN (never module-scoped):
|
|
572
|
+
// it holds this run's child roster and fix turns. Independent of `feed` /
|
|
573
|
+
// `run`, because the Work page's run surface is fed on headless runs too.
|
|
574
|
+
const fanoutBeats = makeFanoutBeats();
|
|
562
575
|
const stateKey = `${STATE_KEY_PREFIX}${runShortId}`;
|
|
563
576
|
let paintTimer;
|
|
564
577
|
let animationTimer;
|
|
@@ -639,6 +652,21 @@ export function registerGoCommand(pi, deps = {}) {
|
|
|
639
652
|
baseUrl: deps.baseUrl ?? resolveBaseUrl(),
|
|
640
653
|
getToken: deps.getToken ?? defaultGetToken,
|
|
641
654
|
});
|
|
655
|
+
// The diamond's own recording channel is STRICTLY ORDERED, and it is the
|
|
656
|
+
// only one that has to be. Every roster beat carries the WHOLE roster and
|
|
657
|
+
// the run surface derives it last-one-wins (FanoutSection's `readFanout`),
|
|
658
|
+
// so two beats in flight at once can land in either order and a "running"
|
|
659
|
+
// that overtakes its own "done" paints a finished builder as running for
|
|
660
|
+
// the rest of the run. Chaining them through one promise makes the order
|
|
661
|
+
// the surface reads the order the pipeline produced. Still fire-and-forget
|
|
662
|
+
// (nothing on the run's critical path waits on it) and still fail-soft: a
|
|
663
|
+
// rejected beat never breaks the chain. The other stage beats stay
|
|
664
|
+
// unserialized on purpose — each names its own boundary, so their arrival
|
|
665
|
+
// order carries no state a later beat can undo.
|
|
666
|
+
let fanBeatChain = Promise.resolve();
|
|
667
|
+
const recordFanBeat = (b) => {
|
|
668
|
+
fanBeatChain = fanBeatChain.then(() => session.stage(b)).catch(() => { });
|
|
669
|
+
};
|
|
642
670
|
const repoCtx = await resolveRepoContext(runCwd, ctx.signal);
|
|
643
671
|
// Durable resilience journal, keyed by the RUN tree (the worktree path for
|
|
644
672
|
// a default run; ctx.cwd for --here). Detect an interrupted prior /go for
|
|
@@ -880,10 +908,19 @@ export function registerGoCommand(pi, deps = {}) {
|
|
|
880
908
|
let result = await runPipeline(ticket, {
|
|
881
909
|
cwd: runCwd,
|
|
882
910
|
signal: runSignal,
|
|
911
|
+
fanout: fanoutMode,
|
|
912
|
+
...(isUltra ? { isUltra } : {}),
|
|
883
913
|
...(ticketBrief ? { ticketBrief } : {}),
|
|
884
914
|
onProgress: (p) => {
|
|
885
915
|
feed?.applyProgress(p);
|
|
886
916
|
run?.applyProgress(p, Date.now());
|
|
917
|
+
// The implement diamond's own beats (the partition verdict, each
|
|
918
|
+
// child's state, each fix turn) ride the SAME fail-soft stage
|
|
919
|
+
// channel as every other boundary, additively: a signal that is not
|
|
920
|
+
// the diamond's produces no beat at all.
|
|
921
|
+
const fanBeat = fanoutBeats.apply(p);
|
|
922
|
+
if (fanBeat)
|
|
923
|
+
recordFanBeat(fanBeat);
|
|
887
924
|
// The ribbon replaces the status chip on the desktop, so the chip is
|
|
888
925
|
// only set for the terminal's status-line fallback.
|
|
889
926
|
if (ctx.hasUI && !desktop) {
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The headless front door to the /go pipeline (spec: sandbox harness parity).
|
|
3
|
+
*
|
|
4
|
+
* `/go` is an interactive pi slash command and pi's print mode ignores slash
|
|
5
|
+
* commands, so until now the ONLY way to drive the pipeline from a script was
|
|
6
|
+
* to import `runPipeline` directly (`scripts/eval-go-harness.ts` did exactly
|
|
7
|
+
* that). That gap is why the cloud mission sandbox ran a single flat model
|
|
8
|
+
* session instead of the product's staged pipeline. This module closes it:
|
|
9
|
+
*
|
|
10
|
+
* yagni go --headless --ticket-file <p> [--plan-file <p>] [--memo-file <p>]
|
|
11
|
+
* [--run-id <id>] [--cwd <p>] [--json]
|
|
12
|
+
*
|
|
13
|
+
* It is the SAME `runPipeline` path the interactive command drives (no forked
|
|
14
|
+
* driver) with the interactive-only machinery left out: no worktree, no run
|
|
15
|
+
* registry, no run session mint (the caller already owns the run row and passes
|
|
16
|
+
* its id with `--run-id`), no checkpoint journal, no terminal UI.
|
|
17
|
+
*
|
|
18
|
+
* MISSION MODE (`--plan-file`, optionally `--memo-file`): the factory already
|
|
19
|
+
* produced the plan and a human approved it at the mission's plan gate, so the
|
|
20
|
+
* pipeline skips its own map/plan stages and enters at implement on that plan,
|
|
21
|
+
* with the scoping memo as the repo context the map brief would have given (see
|
|
22
|
+
* mission.ts for the pure rules). Delivery is NOT ours in mission mode: the
|
|
23
|
+
* FINISH stage is a `/go`-session step and is never driven here, so a mission
|
|
24
|
+
* run ends at the reviewed candidate and never commits, pushes, opens a PR, or
|
|
25
|
+
* reports one. The mission's own prHandoff owns all of that.
|
|
26
|
+
*
|
|
27
|
+
* Output contract:
|
|
28
|
+
* - `--json`: one NDJSON line per event on stdout. Child events are the pi
|
|
29
|
+
* `--mode json` objects VERBATIM plus an additive `yagni` attribution key
|
|
30
|
+
* (stage / lens / round), so the event vocabulary is unchanged; pipeline-level
|
|
31
|
+
* lines carry their own `pipeline_*` types.
|
|
32
|
+
* - `{type:"pipeline_fanout", stage, phase, fanout?, children?, fixTurns?}`:
|
|
33
|
+
* the implement diamond's recording beats. Interactive `/go` POSTs these to
|
|
34
|
+
* its run session; headlessly there IS no session, so the SAME
|
|
35
|
+
* `makeFanoutBeats` thread rides the stream instead and the mission
|
|
36
|
+
* collector folds them back onto the Run. Without this a cloud mission would
|
|
37
|
+
* record no partition decision, no per-workstream row, and no fix turn.
|
|
38
|
+
* - Always, as the last line: the result object
|
|
39
|
+
* `{type:"pipeline_result", ok, stopReason, rounds, findings, stages:[{stage,
|
|
40
|
+
* tier, usage}], …}` — or `{type:"pipeline_error", …}` when a build stage
|
|
41
|
+
* threw.
|
|
42
|
+
* - Exit codes: 0 ONLY on a verified candidate (a `clean` stop), 1 for any
|
|
43
|
+
* other outcome or a thrown pipeline error, 2 for a usage/input problem.
|
|
44
|
+
*
|
|
45
|
+
* `YAGNI_GO_TIER_CAP` (eval + template-smoke lanes) is resolved here for the
|
|
46
|
+
* result object and forwarded on the child env; the clamp itself is applied
|
|
47
|
+
* centrally in `runStage` (see tierCap.ts). `YAGNI_GO_FANOUT` is read the same
|
|
48
|
+
* way and pins the implement diamond for a benchmark lane (see fanout.ts).
|
|
49
|
+
*/
|
|
50
|
+
import { runPipeline as defaultRunPipeline } from "./orchestrator.js";
|
|
51
|
+
import type { RunBudget } from "./budget.js";
|
|
52
|
+
import type { JsonEvent, ModelTier, PipelineStage, StageId, StageTag, StageUsage, StopReason } from "./types.js";
|
|
53
|
+
/** One-line usage copy, shared by every argument error. */
|
|
54
|
+
export declare const HEADLESS_GO_USAGE = "Usage: yagni go --headless --ticket-file <path> [--plan-file <path>] [--memo-file <path>] [--run-id <id>] [--cwd <path>] [--json]";
|
|
55
|
+
/**
|
|
56
|
+
* Exit codes. `verified` is deliberately narrow: only a `clean` stop means the
|
|
57
|
+
* run produced a reviewed candidate, so a round_cap / no_changes / failed run
|
|
58
|
+
* can never read as success to a script, a smoke gate, or CI.
|
|
59
|
+
*/
|
|
60
|
+
export declare const HEADLESS_GO_EXIT: {
|
|
61
|
+
readonly verified: 0;
|
|
62
|
+
readonly unverified: 1;
|
|
63
|
+
readonly usage: 2;
|
|
64
|
+
};
|
|
65
|
+
/** The parsed flag surface (pure; no fs, no env). */
|
|
66
|
+
export interface HeadlessGoArgs {
|
|
67
|
+
headless: boolean;
|
|
68
|
+
json: boolean;
|
|
69
|
+
ticketFile?: string;
|
|
70
|
+
planFile?: string;
|
|
71
|
+
memoFile?: string;
|
|
72
|
+
runId?: string;
|
|
73
|
+
cwd?: string;
|
|
74
|
+
/** Any `--flag`-shaped token we do not know: refused, never folded in. */
|
|
75
|
+
unknownFlags: string[];
|
|
76
|
+
/** Bare tokens: the ticket is a FILE here, so a stray positional is refused. */
|
|
77
|
+
positionals: string[];
|
|
78
|
+
/**
|
|
79
|
+
* Value flags whose value was missing, empty, or another flag: refused, so
|
|
80
|
+
* `--ticket-file --json` fails naming the real problem instead of trying to
|
|
81
|
+
* read a file called `--json` with JSON mode silently off.
|
|
82
|
+
*/
|
|
83
|
+
missingValues: string[];
|
|
84
|
+
}
|
|
85
|
+
/** One row of the result's per-stage usage table. */
|
|
86
|
+
export interface HeadlessStageRow {
|
|
87
|
+
stage: StageId;
|
|
88
|
+
/** The tier the stage actually ran on (capped when `YAGNI_GO_TIER_CAP` is set). */
|
|
89
|
+
tier?: ModelTier;
|
|
90
|
+
/** Present on review rows: which review round produced them. */
|
|
91
|
+
round?: number;
|
|
92
|
+
usage: StageUsage;
|
|
93
|
+
}
|
|
94
|
+
/** The final result object written to stdout and returned to the caller. */
|
|
95
|
+
export interface HeadlessGoResult {
|
|
96
|
+
/** True ONLY for a verified candidate (a `clean` stop) — mirrors exit 0. */
|
|
97
|
+
ok: boolean;
|
|
98
|
+
stopReason: StopReason;
|
|
99
|
+
rounds: number;
|
|
100
|
+
findings: number;
|
|
101
|
+
blocking: number;
|
|
102
|
+
stages: HeadlessStageRow[];
|
|
103
|
+
runId?: string;
|
|
104
|
+
/** The resolved `YAGNI_GO_TIER_CAP` ceiling; absent when the run is uncapped. */
|
|
105
|
+
tierCap?: ModelTier;
|
|
106
|
+
/**
|
|
107
|
+
* True when a plan or memo was injected: the pipeline entered at implement on
|
|
108
|
+
* the approved plan and FINISH did not run, so `commitSha` / `prUrl` are never
|
|
109
|
+
* reported (the mission's prHandoff delivers the candidate). Absent otherwise.
|
|
110
|
+
*/
|
|
111
|
+
missionMode?: true;
|
|
112
|
+
commitSha?: string;
|
|
113
|
+
prUrl?: string;
|
|
114
|
+
/** Honest note when the deterministic verify gate gave no verdict. */
|
|
115
|
+
verifyNote?: string;
|
|
116
|
+
verifyCommand?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface HeadlessGoOutcome {
|
|
119
|
+
exitCode: number;
|
|
120
|
+
result?: HeadlessGoResult;
|
|
121
|
+
/** Set when the run ended on a thrown error or an argument problem. */
|
|
122
|
+
error?: string;
|
|
123
|
+
}
|
|
124
|
+
/** Injectable seams: no fs, no stdout, no pipeline in a unit test. */
|
|
125
|
+
export interface HeadlessGoDeps {
|
|
126
|
+
/** Repo to run in. `--cwd` wins; defaults to `process.cwd()`. */
|
|
127
|
+
cwd?: string;
|
|
128
|
+
/** Environment the tier cap is read from. Defaults to `process.env`. */
|
|
129
|
+
env?: NodeJS.ProcessEnv;
|
|
130
|
+
/** Environment for spawned stage children. Defaults to `env`. */
|
|
131
|
+
childEnv?: NodeJS.ProcessEnv;
|
|
132
|
+
runPipeline?: typeof defaultRunPipeline;
|
|
133
|
+
readFile?: (path: string) => string;
|
|
134
|
+
/** stdout sink; one call per NDJSON line, newline added by the caller. */
|
|
135
|
+
write?: (line: string) => void;
|
|
136
|
+
/** stderr sink for human-readable problems. */
|
|
137
|
+
writeErr?: (line: string) => void;
|
|
138
|
+
signal?: AbortSignal;
|
|
139
|
+
budget?: RunBudget;
|
|
140
|
+
/** Stage-list override (the blind eval lane's grounding-stripped copy). */
|
|
141
|
+
stages?: PipelineStage[];
|
|
142
|
+
/** False runs the blind eval lane; defaults to grounded like every real run. */
|
|
143
|
+
grounded?: boolean;
|
|
144
|
+
/** Additive tap on every child event (the eval lane's retrieval telemetry). */
|
|
145
|
+
onEvent?: (ev: JsonEvent, tag: StageTag) => void;
|
|
146
|
+
/** Additive tap on the pipeline's own log events. */
|
|
147
|
+
logger?: (event: string, data?: unknown) => void;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Parse the argv remainder after `go`. Accepts both `--flag value` and
|
|
151
|
+
* `--flag=value`; unknown flags and stray positionals are collected rather than
|
|
152
|
+
* silently absorbed, so a typo fails loudly instead of running the wrong thing.
|
|
153
|
+
*/
|
|
154
|
+
export declare function parseHeadlessGoArgs(argv: string[]): HeadlessGoArgs;
|
|
155
|
+
/** The argument problem, or undefined when the invocation is usable. */
|
|
156
|
+
export declare function validateHeadlessGoArgs(args: HeadlessGoArgs): string | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* Run the pipeline headlessly. Never throws: every failure resolves to an exit
|
|
159
|
+
* code and an honest final line, so the sandbox harness and the smoke gate can
|
|
160
|
+
* read one contract.
|
|
161
|
+
*/
|
|
162
|
+
export declare function runHeadlessGo(argv: string[], deps?: HeadlessGoDeps): Promise<HeadlessGoOutcome>;
|
|
163
|
+
//# sourceMappingURL=headlessGo.d.ts.map
|