bareloop 0.39.0 → 0.40.0
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 +27 -0
- package/bareloop.context.md +63 -11
- package/package.json +2 -2
- package/src/authorflow.js +333 -58
- package/src/authoring.js +65 -2
- package/src/authorjob.js +57 -9
- package/src/authorreadout.js +50 -2
- package/src/authorrun.js +5 -2
- package/src/authorscout.js +9 -6
- package/src/bridges.js +1 -1
- package/src/calibrate.js +1 -1
- package/src/cardauthor.js +4 -1
- package/src/declaredclose.js +4 -3
- package/src/draftspend.js +6 -1
- package/src/interviewrun.js +56 -27
- package/src/job.js +38 -5
- package/src/jobblock.js +140 -0
- package/src/judged.js +1 -1
- package/src/ledger.js +1 -1
- package/src/panel/authorroutes.js +15 -5
- package/src/panel/authorsession.js +158 -47
- package/src/panel/index.html +179 -70
- package/src/panel/jobplan.js +107 -0
- package/src/panel/server.js +67 -13
- package/src/planrun.js +4 -4
- package/src/promptregisters.js +1 -1
- package/src/providers.js +101 -19
- package/src/reuse.js +1 -1
- package/src/root.js +1 -1
- package/src/run.js +3 -3
- package/src/selection.js +1 -1
- package/src/source.js +64 -1
- package/src/text.js +13 -0
- package/src/transport.js +1 -1
- package/src/userrun.js +3 -3
- package/types/authorflow.d.ts +258 -39
- package/types/authoring.d.ts +7 -1
- package/types/authorjob.d.ts +35 -3
- package/types/authorreadout.d.ts +17 -1
- package/types/authorscout.d.ts +5 -3
- package/types/declaredclose.d.ts +4 -1
- package/types/draftspend.d.ts +8 -2
- package/types/job.d.ts +1 -1
- package/types/jobblock.d.ts +102 -0
- package/types/panel/authorsession.d.ts +50 -14
- package/types/panel/jobplan.d.ts +58 -0
- package/types/panel/server.d.ts +20 -0
- package/types/planrun.d.ts +1 -1
- package/types/providers.d.ts +47 -3
- package/types/run.d.ts +1 -1
- package/types/source.d.ts +26 -0
package/types/authorjob.d.ts
CHANGED
|
@@ -79,26 +79,38 @@ export function refuseLockedKind(kind: string): Refusal;
|
|
|
79
79
|
* one-genre-at-a-time law is untouched.
|
|
80
80
|
*
|
|
81
81
|
* @param {{answers: Record<string|number, any>, repoPath?: string|null, lang: string,
|
|
82
|
+
* jobLines?: {n: number, text: string}[]|null, inputs?: {n: number, label: string, value: string}[]|null,
|
|
82
83
|
* verdictType?: string|null,
|
|
83
84
|
* questions?: Record<string|number, string>|null, generate?: Function, provider?: any,
|
|
84
85
|
* seedRef?: string|null, scout?: any, listing?: any, ceilingUsd?: number|null, judgeModel?: string|null,
|
|
85
|
-
* rates?: {in: number, out: number}|null,
|
|
86
|
+
* rates?: {in: number, out: number}|null, draftThinking?: 'on'|'off',
|
|
86
87
|
* onPhase?: (phase: string, data?: any) => void,
|
|
87
|
-
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
88
|
+
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number, thinkingApplied?: boolean|null}) => void,
|
|
88
89
|
* seedFn?: Function, scoutFn?: Function, listingFn?: Function,
|
|
89
90
|
* authorFn?: Function, authorOpts?: object, writeScope?: string[]|null,
|
|
90
91
|
* signerFix?: Function|any, proposeFn?: Function, compileOpts?: object,
|
|
91
92
|
* ask?: ((step: {kind: string, [k: string]: any}) => Promise<string|null>)|null,
|
|
93
|
+
* answerQuestions?: ((o: {questions: {line: number, question: string}[], dropped: {reason: string, raw: any}[], confirmed: any}) =>
|
|
94
|
+
* Promise<{jobLines: {n: number, text: string, rule?: string}[], answers: Record<string|number, any>}|null>)|null,
|
|
92
95
|
* confirmGenerate?: Function|null, isRepo?: boolean,
|
|
93
96
|
* langResult?: {kind: string, candidates?: string[], [k: string]: any}|null}} o
|
|
94
97
|
* @returns {Promise<{ok: boolean, refusal: Refusal|null, verdictType: string|null,
|
|
95
98
|
* closeDecl: any, seedRef: string|null, authoring: any, judged: any, reds: Red[],
|
|
96
99
|
* stop: string|null, cost: any, interview: any, confirmed: any}>}
|
|
97
100
|
*/
|
|
98
|
-
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, rates, writeScope, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, ask, confirmGenerate, isRepo, langResult, }: {
|
|
101
|
+
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, jobLines, inputs, generate, provider, seedRef, scout, listing, ceilingUsd, rates, draftThinking, writeScope, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, ask, confirmGenerate, isRepo, langResult, answerQuestions, }: {
|
|
99
102
|
answers: Record<string | number, any>;
|
|
100
103
|
repoPath?: string | null;
|
|
101
104
|
lang: string;
|
|
105
|
+
jobLines?: {
|
|
106
|
+
n: number;
|
|
107
|
+
text: string;
|
|
108
|
+
}[] | null;
|
|
109
|
+
inputs?: {
|
|
110
|
+
n: number;
|
|
111
|
+
label: string;
|
|
112
|
+
value: string;
|
|
113
|
+
}[] | null;
|
|
102
114
|
verdictType?: string | null;
|
|
103
115
|
questions?: Record<string | number, string> | null;
|
|
104
116
|
generate?: Function;
|
|
@@ -112,11 +124,13 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
|
|
|
112
124
|
in: number;
|
|
113
125
|
out: number;
|
|
114
126
|
} | null;
|
|
127
|
+
draftThinking?: "on" | "off";
|
|
115
128
|
onPhase?: (phase: string, data?: any) => void;
|
|
116
129
|
onCall?: (call: {
|
|
117
130
|
label: string;
|
|
118
131
|
costUsd: number | null;
|
|
119
132
|
unpricedRounds: number;
|
|
133
|
+
thinkingApplied?: boolean | null;
|
|
120
134
|
}) => void;
|
|
121
135
|
seedFn?: Function;
|
|
122
136
|
scoutFn?: Function;
|
|
@@ -131,6 +145,24 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
|
|
|
131
145
|
kind: string;
|
|
132
146
|
[k: string]: any;
|
|
133
147
|
}) => Promise<string | null>) | null;
|
|
148
|
+
answerQuestions?: ((o: {
|
|
149
|
+
questions: {
|
|
150
|
+
line: number;
|
|
151
|
+
question: string;
|
|
152
|
+
}[];
|
|
153
|
+
dropped: {
|
|
154
|
+
reason: string;
|
|
155
|
+
raw: any;
|
|
156
|
+
}[];
|
|
157
|
+
confirmed: any;
|
|
158
|
+
}) => Promise<{
|
|
159
|
+
jobLines: {
|
|
160
|
+
n: number;
|
|
161
|
+
text: string;
|
|
162
|
+
rule?: string;
|
|
163
|
+
}[];
|
|
164
|
+
answers: Record<string | number, any>;
|
|
165
|
+
} | null>) | null;
|
|
134
166
|
confirmGenerate?: Function | null;
|
|
135
167
|
isRepo?: boolean;
|
|
136
168
|
langResult?: {
|
package/types/authorreadout.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export function declarationLines({ goal, closeDecl }: {
|
|
|
12
12
|
* F176'S FALLBACK, SAID OUT LOUD — the revise ladder kept the NEWEST SOUND
|
|
13
13
|
* declaration rather than the last one it happened to try, because "kept the
|
|
14
14
|
* last accepted revision" is not the same claim as "kept the best revision"
|
|
15
|
-
* (docs/
|
|
15
|
+
* (docs/wiki/FINDINGS.md F176). Absent on every run where the fallback never
|
|
16
16
|
* fired (the overwhelming majority), which is why this returns an EMPTY list
|
|
17
17
|
* rather than a "no fallback" line — a green close has nothing to say here and
|
|
18
18
|
* printing it every run would be noise, not information.
|
|
@@ -216,6 +216,22 @@ export function proposalStopText({ stop, reds, cases, spend, source }: {
|
|
|
216
216
|
} | null;
|
|
217
217
|
source?: "proposal" | "signer";
|
|
218
218
|
}): string;
|
|
219
|
+
/**
|
|
220
|
+
* @param {{stop: string, reds?: PlainRed[]|null, refusal?: {detail?: string}|null,
|
|
221
|
+
* spend?: {knownUsd?: number|null, spendComplete?: boolean|null}|null}} o
|
|
222
|
+
* @returns {string}
|
|
223
|
+
*/
|
|
224
|
+
export function draftStopText({ stop, reds, refusal, spend }: {
|
|
225
|
+
stop: string;
|
|
226
|
+
reds?: PlainRed[] | null;
|
|
227
|
+
refusal?: {
|
|
228
|
+
detail?: string;
|
|
229
|
+
} | null;
|
|
230
|
+
spend?: {
|
|
231
|
+
knownUsd?: number | null;
|
|
232
|
+
spendComplete?: boolean | null;
|
|
233
|
+
} | null;
|
|
234
|
+
}): string;
|
|
219
235
|
/**
|
|
220
236
|
* Plain sentences for the reds of a refused signing, one or more per red. Card and practice-case reds reuse the
|
|
221
237
|
* proposal table; an unknown code is a generic sentence that quotes the code.
|
package/types/authorscout.d.ts
CHANGED
|
@@ -92,15 +92,15 @@ export function defaultSurveyor({ workdir, granted, auditPath, ctx }: {
|
|
|
92
92
|
* @param {{workdir: string, provider?: any,
|
|
93
93
|
* rounds?: number, minBytes?: number, blobMax?: number, maxTokens?: number, ctx?: boolean,
|
|
94
94
|
* attempts?: number, ceilingUsd?: number|null, callTimeoutMs?: number,
|
|
95
|
-
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
95
|
+
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number, thinkingApplied?: boolean|null}) => void,
|
|
96
96
|
* createLoop?: (o: {system: string, policy: any, onLlmResult: any, provider: any, rates?: {in: number, out: number}}) => any,
|
|
97
|
-
* rates?: {in: number, out: number}|null,
|
|
97
|
+
* rates?: {in: number, out: number}|null, callOptions?: Record<string, any>,
|
|
98
98
|
* createSurveyor?: (o: {workdir: string, granted: readonly string[], ctx: boolean}) => Promise<any>}} o
|
|
99
99
|
* @returns {Promise<Survey & {raw: string, raws: ReturnType<typeof scrubRaw>[],
|
|
100
100
|
* calls: {label: string, costUsd: number|null, unpricedRounds: number}[],
|
|
101
101
|
* budgetStop: 'cap-halt'|'pricing-red'|null}>}
|
|
102
102
|
*/
|
|
103
|
-
export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, maxTokens, ctx, attempts, ceilingUsd, callTimeoutMs, onCall, createLoop, rates, createSurveyor, }: {
|
|
103
|
+
export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, maxTokens, ctx, attempts, ceilingUsd, callTimeoutMs, onCall, createLoop, rates, callOptions, createSurveyor, }: {
|
|
104
104
|
workdir: string;
|
|
105
105
|
provider?: any;
|
|
106
106
|
rounds?: number;
|
|
@@ -115,6 +115,7 @@ export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, m
|
|
|
115
115
|
label: string;
|
|
116
116
|
costUsd: number | null;
|
|
117
117
|
unpricedRounds: number;
|
|
118
|
+
thinkingApplied?: boolean | null;
|
|
118
119
|
}) => void;
|
|
119
120
|
createLoop?: (o: {
|
|
120
121
|
system: string;
|
|
@@ -130,6 +131,7 @@ export function runAuthorScout({ workdir, provider, rounds, minBytes, blobMax, m
|
|
|
130
131
|
in: number;
|
|
131
132
|
out: number;
|
|
132
133
|
} | null;
|
|
134
|
+
callOptions?: Record<string, any>;
|
|
133
135
|
createSurveyor?: (o: {
|
|
134
136
|
workdir: string;
|
|
135
137
|
granted: readonly string[];
|
package/types/declaredclose.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export function declaredStages(closeDecl: any): any[] | null;
|
|
|
53
53
|
*
|
|
54
54
|
* @param {any} closeDecl
|
|
55
55
|
* @param {{at?: string, listing?: string[]|null, deferListing?: boolean,
|
|
56
|
-
* catalogue?: Record<string, any>, verdictType?: string|null, writeScope?: string[]|null}} [opts]
|
|
56
|
+
* catalogue?: Record<string, any>, verdictType?: string|null, writeScope?: string[]|null, jobLines?: {n: number}[]|null}} [opts]
|
|
57
57
|
* @returns {{ok: boolean, reds: Red[], closeDecl: any, grounded: boolean,
|
|
58
58
|
* scoped: {scoped: boolean, via: string|null},
|
|
59
59
|
* ceiling: {class: string, kind: string, stage: string|null, locked: boolean}|null}}
|
|
@@ -65,6 +65,9 @@ export function validateCloseDecl(closeDecl: any, opts?: {
|
|
|
65
65
|
catalogue?: Record<string, any>;
|
|
66
66
|
verdictType?: string | null;
|
|
67
67
|
writeScope?: string[] | null;
|
|
68
|
+
jobLines?: {
|
|
69
|
+
n: number;
|
|
70
|
+
}[] | null;
|
|
68
71
|
}): {
|
|
69
72
|
ok: boolean;
|
|
70
73
|
reds: Red[];
|
package/types/draftspend.d.ts
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
* One line of the drafting log. `no` = the step's position in the session's progress list (a step id can repeat).
|
|
3
3
|
* - `step-start` {no, id, label, at}
|
|
4
4
|
* - `step-end` {no, status: 'done'|'failed', label, at} (a re-opened step writes `step-reopen` {no, at} first)
|
|
5
|
-
* - `
|
|
5
|
+
* - `step-label` {no, label, at} (the step reads `label` from now on, status and times untouched: the thinking row's "the model thought anyway")
|
|
6
|
+
* - `call` {no, step, label, model, thinking, thinkingApplied?, costUsd (null stays null), unpricedRounds, at}: one METERED model call
|
|
7
|
+
* (`thinking` = what was asked and sent: off | on | not-switchable | unverified; `thinkingApplied` = bare-agent's own verdict for the
|
|
8
|
+
* call's run (true | false | null), present only when thinkingMode was sent)
|
|
6
9
|
* (no token counts: the call path does not carry them without reworking the library, so none are recorded)
|
|
7
10
|
* @typedef {{kind: string, no: number, at: string, [k: string]: any}} DraftEvent
|
|
8
11
|
*/
|
|
@@ -105,7 +108,10 @@ export const DRAFT_LOG_FILE: "draft-log.jsonl";
|
|
|
105
108
|
* One line of the drafting log. `no` = the step's position in the session's progress list (a step id can repeat).
|
|
106
109
|
* - `step-start` {no, id, label, at}
|
|
107
110
|
* - `step-end` {no, status: 'done'|'failed', label, at} (a re-opened step writes `step-reopen` {no, at} first)
|
|
108
|
-
* - `
|
|
111
|
+
* - `step-label` {no, label, at} (the step reads `label` from now on, status and times untouched: the thinking row's "the model thought anyway")
|
|
112
|
+
* - `call` {no, step, label, model, thinking, thinkingApplied?, costUsd (null stays null), unpricedRounds, at}: one METERED model call
|
|
113
|
+
* (`thinking` = what was asked and sent: off | on | not-switchable | unverified; `thinkingApplied` = bare-agent's own verdict for the
|
|
114
|
+
* call's run (true | false | null), present only when thinkingMode was sent)
|
|
109
115
|
* (no token counts: the call path does not carry them without reworking the library, so none are recorded)
|
|
110
116
|
*/
|
|
111
117
|
export type DraftEvent = {
|
package/types/job.d.ts
CHANGED
|
@@ -272,7 +272,7 @@ export const PROVIDERS: readonly string[];
|
|
|
272
272
|
export const CONDITION_KEYS: readonly string[];
|
|
273
273
|
/**
|
|
274
274
|
* The fields a "Reuse workflow" run may change: where the work lands (the write fence, `writeScope`, and a
|
|
275
|
-
* `destination`/`source` where a spec ever carries them) and the two ceilings. Everything else in a signed spec is
|
|
275
|
+
* `destination`/`source` where a spec ever carries them), the `inputs` the person pointed at (P7: Inputs is open on a reuse) and the two ceilings. Everything else in a signed spec is
|
|
276
276
|
* the WORKFLOW — goal, checks, guardrails, close (the worker model is the third kind, {@link WORKFLOW_KEY_IDENTITY_FIELDS}). ONE list, shared by {@link workflowKey} and by the panel's
|
|
277
277
|
* reuse (so what the key ignores is exactly what the person may edit, never two spellings).
|
|
278
278
|
*/
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse "The job" box. A plain line is a numbered job line (1, 2, 3...). A line starting `~` has no number and belongs
|
|
3
|
+
* to the numbered line above; consecutive `~` lines become ONE rule joined with `'; '`. A `~` line starting `PASS:` or
|
|
4
|
+
* `FAIL:` is also a judge example (kept with its line number); any other `~` line is a guardrail. Empty lines are
|
|
5
|
+
* ignored. A `~` before line 1 is a red.
|
|
6
|
+
* @param {unknown} text the box's raw text
|
|
7
|
+
* @returns {{ok: true, lines: JobLine[]}|{ok: false, error: string}}
|
|
8
|
+
*/
|
|
9
|
+
export function parseJobBlock(text: unknown): {
|
|
10
|
+
ok: true;
|
|
11
|
+
lines: JobLine[];
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
error: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parse the "Inputs" box: one `label: value` per line, numbered 1, 2, 3 in the order written. Empty lines are ignored.
|
|
18
|
+
* This only checks the SHAPE — what a value must point at (line 1 an absolute repo, lines 2+ tracked files inside it)
|
|
19
|
+
* is proven by the panel at $0.
|
|
20
|
+
* @param {unknown} text
|
|
21
|
+
* @returns {{ok: true, inputs: JobInput[]}|{ok: false, error: string}}
|
|
22
|
+
*/
|
|
23
|
+
export function parseInputs(text: unknown): {
|
|
24
|
+
ok: true;
|
|
25
|
+
inputs: JobInput[];
|
|
26
|
+
} | {
|
|
27
|
+
ok: false;
|
|
28
|
+
error: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* The person's job lines as the drafter reads them (answer 1 of the interview): each numbered line, then its rule
|
|
32
|
+
* (the `~` lines joined) indented under it. One spelling, so the number the person typed beside a line is the number a
|
|
33
|
+
* stage's `fromLine` names.
|
|
34
|
+
* @param {JobLine[]} lines
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export function renderJobLines(lines: JobLine[]): string;
|
|
38
|
+
/**
|
|
39
|
+
* The PASS:/FAIL: examples the person wrote, each kept with its line number (answer 2 of a rubric interview — what the
|
|
40
|
+
* calibration compile reads). Empty string when there are none.
|
|
41
|
+
* @param {JobLine[]} lines
|
|
42
|
+
* @returns {string}
|
|
43
|
+
*/
|
|
44
|
+
export function renderExamples(lines: JobLine[]): string;
|
|
45
|
+
/**
|
|
46
|
+
* The inputs as the drafter is told about them: "the person pointed at these inputs: N label path".
|
|
47
|
+
* @param {JobInput[]} inputs
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
export function renderInputs(inputs: JobInput[]): string;
|
|
51
|
+
/**
|
|
52
|
+
* The two cross-checks between a parsed job block and the Check type: a RUBRIC job needs at least one `PASS:` and one
|
|
53
|
+
* `FAIL:` example (the calibration compile reads them); a DETERMINISTIC job has no judge, so an example line would be
|
|
54
|
+
* silently dropped — a red instead.
|
|
55
|
+
* @param {JobLine[]} lines
|
|
56
|
+
* @param {boolean} rubric
|
|
57
|
+
* @returns {string|null} the plain red, or null when the block fits the Check type
|
|
58
|
+
*/
|
|
59
|
+
export function jobBlockFitsCheck(lines: JobLine[], rubric: boolean): string | null;
|
|
60
|
+
/**
|
|
61
|
+
* The signed `jobLines` field from parsed lines: the person's own words, `{n, text, rule, rules}` (examples stay inside the
|
|
62
|
+
* rule text, where the person wrote them). `rules` is the same `~` lines as an array, BESIDE the joined `rule` (never
|
|
63
|
+
* replacing it); it is omitted on a line with no `~` line, and an older spec lacks it entirely.
|
|
64
|
+
* @param {JobLine[]} lines
|
|
65
|
+
* @returns {{n: number, text: string, rule: string, rules?: string[]}[]}
|
|
66
|
+
*/
|
|
67
|
+
export function signedJobLines(lines: JobLine[]): {
|
|
68
|
+
n: number;
|
|
69
|
+
text: string;
|
|
70
|
+
rule: string;
|
|
71
|
+
rules?: string[];
|
|
72
|
+
}[];
|
|
73
|
+
/** @typedef {{verdict: 'PASS'|'FAIL', text: string}} JobExample a `~ PASS:` / `~ FAIL:` line (rubric only), kept with its job line */
|
|
74
|
+
/** @typedef {{n: number, text: string, rule: string, rules: string[], examples: JobExample[]}} JobLine one numbered job line; `rule` = its `~` lines joined with '; ' ('' when none), `rules` = those same `~` lines one by one (so an example's own '; ' survives) */
|
|
75
|
+
/** @typedef {{n: number, label: string, value: string}} JobInput one numbered `label: value` input line */
|
|
76
|
+
/** the joiner for consecutive `~` lines (fwd's joinGuardrails) */
|
|
77
|
+
export const RULE_JOINER: "; ";
|
|
78
|
+
/**
|
|
79
|
+
* a `~ PASS:` / `~ FAIL:` line (rubric only), kept with its job line
|
|
80
|
+
*/
|
|
81
|
+
export type JobExample = {
|
|
82
|
+
verdict: "PASS" | "FAIL";
|
|
83
|
+
text: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* one numbered job line; `rule` = its `~` lines joined with '; ' ('' when none), `rules` = those same `~` lines one by one (so an example's own '; ' survives)
|
|
87
|
+
*/
|
|
88
|
+
export type JobLine = {
|
|
89
|
+
n: number;
|
|
90
|
+
text: string;
|
|
91
|
+
rule: string;
|
|
92
|
+
rules: string[];
|
|
93
|
+
examples: JobExample[];
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* one numbered `label: value` input line
|
|
97
|
+
*/
|
|
98
|
+
export type JobInput = {
|
|
99
|
+
n: number;
|
|
100
|
+
label: string;
|
|
101
|
+
value: string;
|
|
102
|
+
};
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first drafting row's words (hamr 2026-10-11) — code-owned, one owner, ALWAYS starting "thinking on" or "thinking off".
|
|
3
|
+
* `sent` is what the drafting calls carry (`draftThinkingFor`); `thoughtAnyway` = a call reported `thinkingApplied === false`
|
|
4
|
+
* after 'off' was sent (bare-agent saw reasoning in the reply).
|
|
5
|
+
* @param {'on'|'off'|'not-switchable'|'unverified'} sent @param {boolean} [thoughtAnyway]
|
|
6
|
+
* @returns {string}
|
|
7
|
+
*/
|
|
8
|
+
export function thinkingRowText(sent: "on" | "off" | "not-switchable" | "unverified", thoughtAnyway?: boolean): string;
|
|
1
9
|
/** @typedef {{id: string, label: string, status: 'running'|'done'|'failed', detail: string}} StepLine */
|
|
2
10
|
/** @param {StepLine[]} steps @param {string} id the LATEST line carrying this id (the list is a time-ordered log; an id can repeat) */
|
|
3
11
|
export function latestStep(steps: StepLine[], id: string): StepLine | undefined;
|
|
@@ -10,19 +18,36 @@ export function latestStep(steps: StepLine[], id: string): StepLine | undefined;
|
|
|
10
18
|
*/
|
|
11
19
|
export function advanceSteps(steps: StepLine[], id: string, detail?: string): void;
|
|
12
20
|
/**
|
|
13
|
-
* The confirm turn's plan as
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* @param {{
|
|
18
|
-
* @returns {string}
|
|
21
|
+
* The confirm turn's plan as a chat message (P7): a STRUCTURED `{role:'bot', kind:'plan', plan}` the page draws with the
|
|
22
|
+
* same renderer the Job tab uses — each numbered job line, its `~` rule, its drafted checks folded; the AI's own "not
|
|
23
|
+
* checked"; the always-on guards. `questions` (what the plan still asks) ride inside the plan. One owner of the shape.
|
|
24
|
+
* @param {{goal?: string, checks?: string[], checkItems?: {text: string, fromLine: number[]|null}[], notChecked?: string[], questions?: string[], protections?: string[]}} p the confirm turn's plan
|
|
25
|
+
* @param {{n: number, text: string, rule: string}[]|null} jobLines
|
|
26
|
+
* @returns {{role: 'bot', kind: 'plan', text: string, plan: ReturnType<typeof planFromConfirm> & {goal: string, questions: string[]}}}
|
|
19
27
|
*/
|
|
20
|
-
export function
|
|
28
|
+
export function planMessage(p: {
|
|
21
29
|
goal?: string;
|
|
22
30
|
checks?: string[];
|
|
31
|
+
checkItems?: {
|
|
32
|
+
text: string;
|
|
33
|
+
fromLine: number[] | null;
|
|
34
|
+
}[];
|
|
23
35
|
notChecked?: string[];
|
|
24
36
|
questions?: string[];
|
|
25
|
-
|
|
37
|
+
protections?: string[];
|
|
38
|
+
}, jobLines: {
|
|
39
|
+
n: number;
|
|
40
|
+
text: string;
|
|
41
|
+
rule: string;
|
|
42
|
+
}[] | null): {
|
|
43
|
+
role: "bot";
|
|
44
|
+
kind: "plan";
|
|
45
|
+
text: string;
|
|
46
|
+
plan: ReturnType<typeof planFromConfirm> & {
|
|
47
|
+
goal: string;
|
|
48
|
+
questions: string[];
|
|
49
|
+
};
|
|
50
|
+
};
|
|
26
51
|
/**
|
|
27
52
|
* `true` when `jobs/<name>.json` already exists in the repo's own jobs/ dir
|
|
28
53
|
* (resolved from THIS module's own on-disk location, never `process.cwd()`,
|
|
@@ -36,6 +61,13 @@ export function planText(p: {
|
|
|
36
61
|
export function jobNameTaken(name: string, opts?: {
|
|
37
62
|
jobsDir?: string;
|
|
38
63
|
}): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* The repo a card names: Inputs line 1 (P7 — the Source box is gone; the first input IS the repo). `''` when the Inputs
|
|
66
|
+
* box does not parse (the card validator refuses that before anything uses this).
|
|
67
|
+
* @param {any} card
|
|
68
|
+
* @returns {string}
|
|
69
|
+
*/
|
|
70
|
+
export function cardSource(card: any): string;
|
|
39
71
|
/**
|
|
40
72
|
* Only the form's own fields, nothing else a request body might carry (`startFrom`, stray keys).
|
|
41
73
|
* @param {any} card
|
|
@@ -52,12 +84,12 @@ export function cardFields(card: any): Record<string, any>;
|
|
|
52
84
|
export function lockedFieldChanged(card: Record<string, any>, origin: Record<string, any>): string | null;
|
|
53
85
|
/**
|
|
54
86
|
* The spec a reuse signs: a COPY of the origin's signed spec with ONLY the open fields set from the card —
|
|
55
|
-
* `writeScope` from Destination, `budgetUsd` from the $ cap, `maxWallMs` from the Time cap (absent when blank), and the
|
|
87
|
+
* `writeScope` from Destination, `inputs` from the Inputs box (P7), `budgetUsd` from the $ cap, `maxWallMs` from the Time cap (absent when blank), and the
|
|
56
88
|
* worker (`provider`/`baseUrl`/`model`) from the Model Name chosen in Settings > Providers, spelled exactly as normal
|
|
57
89
|
* authoring spells it (`baseUrl` only when the row has one; `model` only when the Name is not the provider's default
|
|
58
90
|
* tier). A RUBRIC (soft-green) origin with no explicit `judge` first has its judge pinned to the ORIGIN's resolved judge
|
|
59
91
|
* identity — the judge must not change because the worker did (`resolveJobJudge`, the one spelling); an explicit
|
|
60
|
-
* `judge` is kept; a deterministic job has no judge to move.
|
|
92
|
+
* `judge` is kept; a deterministic job has no judge to move. The repo (Inputs line 1) is in a spec only as `inputs`. The copy
|
|
61
93
|
* hashes to a NEW `jobSpecHash` and the SAME `workflowKey` — the caller checks the second after building.
|
|
62
94
|
* @param {any} originSpec
|
|
63
95
|
* @param {Record<string, any>} card
|
|
@@ -67,7 +99,7 @@ export function lockedFieldChanged(card: Record<string, any>, origin: Record<str
|
|
|
67
99
|
export function buildReuseSpec(originSpec: any, card: Record<string, any>, rows?: readonly import("../providerrows.js").KeyRow[]): any;
|
|
68
100
|
/**
|
|
69
101
|
* `$0` validation for a REUSE start: the signed job carries the goal, checks and guardrails, so those boxes are not
|
|
70
|
-
* required here — only what the new session itself needs:
|
|
102
|
+
* required here — only what the new session itself needs: Inputs, a Destination, a Model that is a Settings Name,
|
|
71
103
|
* and the cap. (The spec built from the open boxes is validated by the route with `validateJob`.)
|
|
72
104
|
* @param {any} card
|
|
73
105
|
* @param {{rows?: readonly import('../providerrows.js').KeyRow[]}} [opts]
|
|
@@ -86,7 +118,7 @@ export function validateReuseCard(card: any, opts?: {
|
|
|
86
118
|
* relative to the repo, so an absolute path is refused — at $0, before any copy or model call. A FOLDER source (or a
|
|
87
119
|
* source that does not peek as a repo) keeps today's behaviour: its Destination is an absolute output directory.
|
|
88
120
|
* @param {string} destination
|
|
89
|
-
* @param {boolean} isRepoLike `looksLikeRepoSource(card
|
|
121
|
+
* @param {boolean} isRepoLike `looksLikeRepoSource(cardSource(card))`
|
|
90
122
|
* @returns {string|null} the plain refusal, or null when the Destination is fine
|
|
91
123
|
*/
|
|
92
124
|
export function repoDestinationProblem(destination: string, isRepoLike: boolean): string | null;
|
|
@@ -115,7 +147,7 @@ export function validateJobCard(card: any, opts?: {
|
|
|
115
147
|
* @param {any} card the validated job card (see {@link validateJobCard})
|
|
116
148
|
* @param {{env?: Record<string,string|undefined>, sessionsRoot?: string, home?: string, timeoutMs?: number,
|
|
117
149
|
* scout?: any, generate?: Function, confirmGenerate?: Function, authorFn?: Function,
|
|
118
|
-
* prepareSigningFn?: Function, npmCiFn?: Function, reuse?: {spec: any, workflowKey: string}}} [deps] `scout`..`prepareSigningFn` are
|
|
150
|
+
* prepareSigningFn?: Function, npmCiFn?: Function, draftThinking?: "on"|"off", reuse?: {spec: any, workflowKey: string}}} [deps] `scout`..`prepareSigningFn` are
|
|
119
151
|
* TEST SEAMS ONLY — see the note just above where each is read, below. `reuse` (Reuse workflow) is NOT a seam: the
|
|
120
152
|
* caller (`/api/author/start`) sets it only when the server's own same-job rule held.
|
|
121
153
|
* @returns {any} the session object
|
|
@@ -131,6 +163,7 @@ export function createSession(card: any, deps?: {
|
|
|
131
163
|
authorFn?: Function;
|
|
132
164
|
prepareSigningFn?: Function;
|
|
133
165
|
npmCiFn?: Function;
|
|
166
|
+
draftThinking?: "on" | "off";
|
|
134
167
|
reuse?: {
|
|
135
168
|
spec: any;
|
|
136
169
|
workflowKey: string;
|
|
@@ -149,11 +182,13 @@ export const STEP_LABELS: Readonly<Record<string, string>>;
|
|
|
149
182
|
* @type {Readonly<Record<string, keyof typeof STEP_LABELS>>}
|
|
150
183
|
*/
|
|
151
184
|
export const PHASE_STEP: Readonly<Record<string, keyof typeof STEP_LABELS>>;
|
|
185
|
+
/** the line under the plan bubble, in plain words (the bubble itself is drawn by the page's one `renderJobPlan`) */
|
|
186
|
+
export const PLAN_FOOTER: "Sign & run to confirm this plan, or Revise to edit the boxes and draft again.";
|
|
152
187
|
/** the form's fields, in card order — what `card.json` stores, verbatim (P5 item 3) */
|
|
153
188
|
export const CARD_FIELDS: readonly string[];
|
|
154
189
|
/**
|
|
155
190
|
* REUSE WORKFLOW (hamr 2026-10-03, replaces P5 item 3's same-job rule; Model opened 2026-10-04): a reuse card has exactly
|
|
156
|
-
* FIVE open boxes —
|
|
191
|
+
* FIVE open boxes — Inputs, Destination, Model, $ cap, Time cap — and every other box is the signed workflow, shown greyed and never
|
|
157
192
|
* editable. The page greys them; THIS is what refuses (the server never trusts the page). Changing any locked box
|
|
158
193
|
* is a different job: Clear the card, which drafts.
|
|
159
194
|
*/
|
|
@@ -167,4 +202,5 @@ export type StepLine = {
|
|
|
167
202
|
status: "running" | "done" | "failed";
|
|
168
203
|
detail: string;
|
|
169
204
|
};
|
|
205
|
+
import { planFromConfirm } from './jobplan.js';
|
|
170
206
|
import { nonRepoSourceMessage } from '../source.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** @typedef {{name: string, cls: 'machine check'|'judge'|null}} PlanCheck */
|
|
2
|
+
/** @typedef {{n: number, text: string, rule: string, checks: PlanCheck[], refused: string[]}} PlanLine */
|
|
3
|
+
/** @typedef {{lines: PlanLine[], loose: PlanCheck[], notChecked: string[], alwaysOn: string[], older: boolean, goal?: string}} JobPlan */
|
|
4
|
+
/**
|
|
5
|
+
* `machine check` for a kind a command decides, `judge` for one that needs judgement; `null` for a stage whose kind is
|
|
6
|
+
* unknown (a hand-written command close has no kind: it is a command, so a machine check).
|
|
7
|
+
* @param {unknown} kind
|
|
8
|
+
* @returns {'machine check'|'judge'|null}
|
|
9
|
+
*/
|
|
10
|
+
export function checkClassOfKind(kind: unknown): "machine check" | "judge" | null;
|
|
11
|
+
/**
|
|
12
|
+
* The plan of a SIGNED spec. With `jobLines`, each stage lands under EACH line its `fromLine` array names (a stage with none is
|
|
13
|
+
* a mandatory guard: "Always on"), `closeDecl.refused` reasons land under their line, and `closeDecl.notes` are the
|
|
14
|
+
* drafter's own "not checked" words. Without `jobLines` (a spec that predates the job block) the signed goal sentence is
|
|
15
|
+
* line 1 and every stage name is a check under it — `older: true`.
|
|
16
|
+
* @param {any} spec
|
|
17
|
+
* @returns {JobPlan}
|
|
18
|
+
*/
|
|
19
|
+
export function planFromSpec(spec: any): JobPlan;
|
|
20
|
+
/**
|
|
21
|
+
* The plan of the confirm turn, before anything is drafted: the person's own job lines, the model's checks under the
|
|
22
|
+
* line each names, its own "not checked" words, and the real protections (code-owned) as "Always on".
|
|
23
|
+
* @param {{n: number, text: string, rule: string}[]|null|undefined} jobLines
|
|
24
|
+
* @param {{checkItems?: {text: string, fromLine: number[]|null}[], checks?: string[], notChecked?: string[], protections?: string[]}} plan
|
|
25
|
+
* @returns {JobPlan}
|
|
26
|
+
*/
|
|
27
|
+
export function planFromConfirm(jobLines: {
|
|
28
|
+
n: number;
|
|
29
|
+
text: string;
|
|
30
|
+
rule: string;
|
|
31
|
+
}[] | null | undefined, plan: {
|
|
32
|
+
checkItems?: {
|
|
33
|
+
text: string;
|
|
34
|
+
fromLine: number[] | null;
|
|
35
|
+
}[];
|
|
36
|
+
checks?: string[];
|
|
37
|
+
notChecked?: string[];
|
|
38
|
+
protections?: string[];
|
|
39
|
+
}): JobPlan;
|
|
40
|
+
export type PlanCheck = {
|
|
41
|
+
name: string;
|
|
42
|
+
cls: "machine check" | "judge" | null;
|
|
43
|
+
};
|
|
44
|
+
export type PlanLine = {
|
|
45
|
+
n: number;
|
|
46
|
+
text: string;
|
|
47
|
+
rule: string;
|
|
48
|
+
checks: PlanCheck[];
|
|
49
|
+
refused: string[];
|
|
50
|
+
};
|
|
51
|
+
export type JobPlan = {
|
|
52
|
+
lines: PlanLine[];
|
|
53
|
+
loose: PlanCheck[];
|
|
54
|
+
notChecked: string[];
|
|
55
|
+
alwaysOn: string[];
|
|
56
|
+
older: boolean;
|
|
57
|
+
goal?: string;
|
|
58
|
+
};
|
package/types/panel/server.d.ts
CHANGED
|
@@ -298,6 +298,26 @@ export function getRunRounds(runid: string, query?: {
|
|
|
298
298
|
}, opts?: {
|
|
299
299
|
home?: string;
|
|
300
300
|
}): any | null;
|
|
301
|
+
/**
|
|
302
|
+
* The Signed row of the Job tab: `YYYY-MM-DD HH:MM · <first 6 of specHash>`, the time being the run's FIRST `job-start`
|
|
303
|
+
* in local time (hamr, P7). `null` when either part is not on record — never a made-up stamp.
|
|
304
|
+
* @param {string|null|undefined} ts an ISO stamp @param {string|null|undefined} hash
|
|
305
|
+
* @returns {string|null}
|
|
306
|
+
*/
|
|
307
|
+
export function signedLine(ts: string | null | undefined, hash: string | null | undefined): string | null;
|
|
308
|
+
/**
|
|
309
|
+
* The Job box's text for a signed spec: the person's own numbered lines (a `~` line per entry of `rules`, or — for a spec
|
|
310
|
+
* signed before `rules` existed — per '; '-split piece of `rule`), or — for
|
|
311
|
+
* an older job that saved no job lines — the signed goal sentence as the one line.
|
|
312
|
+
* @param {any} spec @returns {string}
|
|
313
|
+
*/
|
|
314
|
+
export function jobTextFromSpec(spec: any): string;
|
|
315
|
+
/**
|
|
316
|
+
* The Inputs box's text for a signed spec: its numbered inputs, or — for an older job — `repo: <source>` from the
|
|
317
|
+
* caller's best knowledge of the source (or empty).
|
|
318
|
+
* @param {any} spec @param {string} source @returns {string}
|
|
319
|
+
*/
|
|
320
|
+
export function inputsTextFromSpec(spec: any, source: string): string;
|
|
301
321
|
/**
|
|
302
322
|
* `GET /api/runs/:runid/job` — the Job tab, filled from the first source
|
|
303
323
|
* that actually resolves (item 2, 2026-09-25), in order:
|
package/types/planrun.d.ts
CHANGED
|
@@ -221,7 +221,7 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
221
221
|
* item 27/M3 Part B), forwarded to every close invocation as
|
|
222
222
|
* `BARELOOP_CLOSE_DIR`
|
|
223
223
|
* @param {number} [opts.maxStepRounds] the shell's per-step rounds ceiling (validatePlan's bound)
|
|
224
|
-
* @param {boolean} [opts.scout=true] operator-only switch (docs/
|
|
224
|
+
* @param {boolean} [opts.scout=true] operator-only switch (docs/logs/SCOUT-CONTRAST.md):
|
|
225
225
|
* `false` on a FRESH run (no `resumeSeed`) skips the survey entirely — `scoutBlob`
|
|
226
226
|
* stays `''`, no `scout-start`/`scout-result`/`scout-empty` is emitted, and a single
|
|
227
227
|
* `scout-skipped {reason:'operator-off'}` records it (a RECORD, never silence — the
|
package/types/providers.d.ts
CHANGED
|
@@ -1,10 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The per-call options the rubric JUDGE's model calls carry for this provider
|
|
3
|
-
*
|
|
2
|
+
* The per-call options the rubric JUDGE's model calls carry for this provider instance: `{ thinkingMode: 'off' }` when
|
|
3
|
+
* bare-agent measures the model switchable, else `{}` (F192: judge runs thinking off; a judge on a model that cannot turn it
|
|
4
|
+
* off must not throw).
|
|
4
5
|
* @param {any} provider a constructed bare-agent provider
|
|
5
6
|
* @returns {Record<string, any>}
|
|
6
7
|
*/
|
|
7
8
|
export function judgeCallOptions(provider: any): Record<string, any>;
|
|
9
|
+
/**
|
|
10
|
+
* Throw unless `mode` is one of {@link DRAFT_THINKING_MODES} — an operator-param guard, so a typo never silently means a mode.
|
|
11
|
+
* @param {unknown} mode
|
|
12
|
+
* @returns {'on'|'off'}
|
|
13
|
+
*/
|
|
14
|
+
export function assertDraftThinking(mode: unknown): "on" | "off";
|
|
15
|
+
/**
|
|
16
|
+
* What the drafting calls on this provider ACTUALLY carry for thinking, for the draft log and the panel row: `'on'` (mode on,
|
|
17
|
+
* nothing sent), `'off'` (mode off and the switch is sent), `'not-switchable'` (mode off, model measured not switchable —
|
|
18
|
+
* nothing sent), `'unverified'` (mode off, model not measured by bare-agent — nothing sent). Never re-derives a model list.
|
|
19
|
+
* @param {any} provider a constructed bare-agent provider
|
|
20
|
+
* @param {unknown} [mode] 'on' | 'off' (default 'off')
|
|
21
|
+
* @returns {'on'|'off'|'not-switchable'|'unverified'}
|
|
22
|
+
*/
|
|
23
|
+
export function draftThinkingSent(provider: any, mode?: unknown): "on" | "off" | "not-switchable" | "unverified";
|
|
24
|
+
/**
|
|
25
|
+
* {@link draftThinkingSent} for a model the caller has only NAMED (provider shape, model id, baseUrl) — no key, no network. For
|
|
26
|
+
* the panel's first drafting row, which is shown before the key is read. Constructing the provider only makes the class and
|
|
27
|
+
* the model id available to `thinkingSupport`; it sends nothing.
|
|
28
|
+
* @param {{providerName: string, model: string, baseUrl?: string|null}} o
|
|
29
|
+
* @param {unknown} [mode] 'on' | 'off' (default 'off')
|
|
30
|
+
* @returns {'on'|'off'|'not-switchable'|'unverified'}
|
|
31
|
+
*/
|
|
32
|
+
export function draftThinkingFor({ providerName, model, baseUrl }: {
|
|
33
|
+
providerName: string;
|
|
34
|
+
model: string;
|
|
35
|
+
baseUrl?: string | null;
|
|
36
|
+
}, mode?: unknown): "on" | "off" | "not-switchable" | "unverified";
|
|
37
|
+
/**
|
|
38
|
+
* The ONE owner of the per-call options every DRAFTING model call carries (scout, confirm, author, revise, structure retries).
|
|
39
|
+
* `{ thinkingMode: 'off' }` exactly when {@link draftThinkingSent} says `'off'`, else `{}`.
|
|
40
|
+
* @param {any} provider a constructed bare-agent provider
|
|
41
|
+
* @param {unknown} [mode] 'on' | 'off' (default 'off')
|
|
42
|
+
* @returns {Record<string, any>}
|
|
43
|
+
*/
|
|
44
|
+
export function draftCallOptions(provider: any, mode?: unknown): Record<string, any>;
|
|
8
45
|
/**
|
|
9
46
|
* Look up a provider's table entry. Throws a NAMED error on an unrecognized
|
|
10
47
|
* provider — never a silent default (the `resolveRates`-style-substring-
|
|
@@ -18,7 +55,7 @@ export function resolveProvider(providerName: string): ProviderTableEntry;
|
|
|
18
55
|
/**
|
|
19
56
|
* Report whether a resolved API key VALUE (not its presence — every call
|
|
20
57
|
* site already presence-checks separately) carries a shape an HTTP header
|
|
21
|
-
* value cannot: F181 (`docs/
|
|
58
|
+
* value cannot: F181 (`docs/wiki/FINDINGS.md`) — a two-line secret-store
|
|
22
59
|
* entry put the key plus a trailing metadata line into `OPENAI_API_KEY`
|
|
23
60
|
* (`"<key>\nmeta"`), the presence-only check at every door passed, and Node
|
|
24
61
|
* crashed `TypeError [ERR_INVALID_CHAR]` inside its own header-encode at the
|
|
@@ -217,6 +254,13 @@ export const PROBE_STATUS: Readonly<Record<string, {
|
|
|
217
254
|
probed: boolean;
|
|
218
255
|
evidence: string;
|
|
219
256
|
}>>;
|
|
257
|
+
/**
|
|
258
|
+
* The drafter's THINKING knob — an enumerated OPERATOR param, never model output and never read from env inside the library.
|
|
259
|
+
* `'on'` sends nothing (the model's own thinking, byte-identical to a no-knob call); `'off'` sends `thinkingMode: 'off'` where
|
|
260
|
+
* bare-agent measures the model switchable. The DEFAULT is `'off'` (hamr 2026-10-11, decided by the frozen ON/OFF contrast,
|
|
261
|
+
* docs/logs/2026-10-10-drafter-thinking-contrast-prereg.md); `'on'` stays selectable for a later Settings checkbox.
|
|
262
|
+
*/
|
|
263
|
+
export const DRAFT_THINKING_MODES: readonly string[];
|
|
220
264
|
export type ProviderTableEntry = {
|
|
221
265
|
/**
|
|
222
266
|
* the bare-agent provider class
|
package/types/run.d.ts
CHANGED
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
* frozen A0 baseline arm has to be exactly today, so a guard firing under a disabled
|
|
159
159
|
* shim would make the baseline a treatment arm. The default-flip belongs to a
|
|
160
160
|
* paid contrast nobody has approved yet (the `layerRoot` precedent, F41).
|
|
161
|
-
* @param {boolean} [opts.scout=true] operator-only switch (docs/
|
|
161
|
+
* @param {boolean} [opts.scout=true] operator-only switch (docs/logs/SCOUT-CONTRAST.md),
|
|
162
162
|
* forwarded verbatim to the plan flow: `false` on a fresh run (no `resumeSeed`) skips the
|
|
163
163
|
* read-only survey and records `scout-skipped {reason:'operator-off'}` instead — the
|
|
164
164
|
* planner drafts from `(no scout notes)`. Runner territory exactly like `readShim`: the
|