bareloop 0.8.0 → 0.9.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 +421 -0
- package/README.md +10 -3
- package/bareloop.context.md +198 -17
- package/package.json +1 -1
- package/src/authorflow.js +1280 -0
- package/src/authoring.js +1296 -0
- package/src/authorjob.js +793 -0
- package/src/authorscout.js +706 -0
- package/src/bridges.js +2 -2
- package/src/declaredclose.js +519 -0
- package/src/index.js +72 -0
- package/src/job.js +48 -4
- package/src/kinds.js +1354 -0
- package/src/ledger.js +13 -0
- package/src/plan.js +26 -1
- package/src/planrun.js +239 -16
- package/src/ralph.js +19 -4
- package/src/reuse.js +68 -7
- package/src/root.js +8 -1
- package/src/run.js +10 -3
- package/src/text.js +120 -0
- package/src/workbranch.js +261 -0
- package/types/authorflow.d.ts +720 -0
- package/types/authoring.d.ts +447 -0
- package/types/authorjob.d.ts +241 -0
- package/types/authorscout.d.ts +307 -0
- package/types/declaredclose.d.ts +174 -0
- package/types/index.d.ts +9 -1
- package/types/job.d.ts +1 -0
- package/types/kinds.d.ts +343 -0
- package/types/plan.d.ts +18 -0
- package/types/planrun.d.ts +10 -3
- package/types/ralph.d.ts +1 -0
- package/types/run.d.ts +10 -2
- package/types/text.d.ts +84 -0
- package/types/workbranch.d.ts +102 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
/** The frozen question set for a picked class. Throws on a locked or unknown
|
|
2
|
+
* one: reaching here with either means admission did not refuse, which is a
|
|
3
|
+
* bareloop bug, and failing loudly beats interviewing for a class we cannot run.
|
|
4
|
+
* @param {string} verdictType */
|
|
5
|
+
export function questionsFor(verdictType: string): any;
|
|
6
|
+
/** The question numbers whose answer must be present for that class's interview
|
|
7
|
+
* to be finished. @param {string} verdictType @returns {number[]} */
|
|
8
|
+
export function requiredAnswersFor(verdictType: string): number[];
|
|
9
|
+
/**
|
|
10
|
+
* The mechanical seam between the catalogue and the schema, read BOTH ways: a
|
|
11
|
+
* catalogue parameter with no schema would be silently undeclarable, and a schema
|
|
12
|
+
* entry the catalogue does not have is a second catalogue.
|
|
13
|
+
* @param {Record<string, any>} [catalogue]
|
|
14
|
+
* @returns {{ok: boolean, missing: string[], orphan: string[]}}
|
|
15
|
+
*/
|
|
16
|
+
export function schemaCoverage(catalogue?: Record<string, any>): {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
missing: string[];
|
|
19
|
+
orphan: string[];
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* THE DECLARATION SCHEMA — the tool's input schema, and therefore the whole
|
|
23
|
+
* shape a model can express.
|
|
24
|
+
*
|
|
25
|
+
* One `oneOf` branch per LIVE kind, each pinning its own `kind` as a const so a
|
|
26
|
+
* parameter set cannot drift onto the wrong kind. A LOCKED kind gets no branch at
|
|
27
|
+
* all: declaring one is inexpressible here rather than rejected afterwards (see
|
|
28
|
+
* the header for what that costs).
|
|
29
|
+
*
|
|
30
|
+
* It enforces STRUCTURE, not LAW. The stage-name slug rule, the one-population
|
|
31
|
+
* law, the listing rule, F49's regex reject and the D5 guard equality are M2's,
|
|
32
|
+
* and they arrive as validation reds through the revise loop — a schema that
|
|
33
|
+
* tried to restate them would be a second validator.
|
|
34
|
+
*
|
|
35
|
+
* THROWS on a catalogue it cannot cover: a malformed catalogue is a bareloop bug,
|
|
36
|
+
* and failing loudly beats emitting a schema that quietly cannot say something
|
|
37
|
+
* the validator will then demand.
|
|
38
|
+
* @param {Record<string, any>} [catalogue]
|
|
39
|
+
*/
|
|
40
|
+
export function declarationSchema(catalogue?: Record<string, any>): {
|
|
41
|
+
type: string;
|
|
42
|
+
properties: {
|
|
43
|
+
stages: {
|
|
44
|
+
type: string;
|
|
45
|
+
minItems: number;
|
|
46
|
+
maxItems: number;
|
|
47
|
+
description: string;
|
|
48
|
+
items: {
|
|
49
|
+
oneOf: {
|
|
50
|
+
title: string;
|
|
51
|
+
description: any;
|
|
52
|
+
type: string;
|
|
53
|
+
properties: {
|
|
54
|
+
name: {
|
|
55
|
+
type: string;
|
|
56
|
+
description: string;
|
|
57
|
+
};
|
|
58
|
+
kind: {
|
|
59
|
+
const: string;
|
|
60
|
+
};
|
|
61
|
+
params: {
|
|
62
|
+
type: string;
|
|
63
|
+
properties: Record<string, any>;
|
|
64
|
+
required: any[];
|
|
65
|
+
additionalProperties: boolean;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
required: string[];
|
|
69
|
+
additionalProperties: boolean;
|
|
70
|
+
}[];
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
notes: {
|
|
74
|
+
type: string;
|
|
75
|
+
items: {
|
|
76
|
+
type: string;
|
|
77
|
+
minLength: number;
|
|
78
|
+
};
|
|
79
|
+
description: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
required: string[];
|
|
83
|
+
additionalProperties: boolean;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The output channel. It records and acknowledges; it takes no action, which is
|
|
87
|
+
* why handing it to a toolless authoring call does not reopen D3.
|
|
88
|
+
* @param {{calls: any[]}} box
|
|
89
|
+
* @param {{catalogue?: Record<string, any>}} [o]
|
|
90
|
+
*/
|
|
91
|
+
export function declarationTool(box: {
|
|
92
|
+
calls: any[];
|
|
93
|
+
}, { catalogue }?: {
|
|
94
|
+
catalogue?: Record<string, any>;
|
|
95
|
+
}): {
|
|
96
|
+
name: string;
|
|
97
|
+
description: string;
|
|
98
|
+
parameters: {
|
|
99
|
+
type: string;
|
|
100
|
+
properties: {
|
|
101
|
+
stages: {
|
|
102
|
+
type: string;
|
|
103
|
+
minItems: number;
|
|
104
|
+
maxItems: number;
|
|
105
|
+
description: string;
|
|
106
|
+
items: {
|
|
107
|
+
oneOf: {
|
|
108
|
+
title: string;
|
|
109
|
+
description: any;
|
|
110
|
+
type: string;
|
|
111
|
+
properties: {
|
|
112
|
+
name: {
|
|
113
|
+
type: string;
|
|
114
|
+
description: string;
|
|
115
|
+
};
|
|
116
|
+
kind: {
|
|
117
|
+
const: string;
|
|
118
|
+
};
|
|
119
|
+
params: {
|
|
120
|
+
type: string;
|
|
121
|
+
properties: Record<string, any>;
|
|
122
|
+
required: any[];
|
|
123
|
+
additionalProperties: boolean;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
required: string[];
|
|
127
|
+
additionalProperties: boolean;
|
|
128
|
+
}[];
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
notes: {
|
|
132
|
+
type: string;
|
|
133
|
+
items: {
|
|
134
|
+
type: string;
|
|
135
|
+
minLength: number;
|
|
136
|
+
};
|
|
137
|
+
description: string;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
required: string[];
|
|
141
|
+
additionalProperties: boolean;
|
|
142
|
+
};
|
|
143
|
+
execute: (args: any) => Promise<string>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* The catalogue, as the authoring call sees it — rendered FROM the catalogue, so
|
|
147
|
+
* the prompt and the validator judge one set of facts. `shape` and `asserts` are
|
|
148
|
+
* M2's own strings.
|
|
149
|
+
* @param {Record<string, any>} [catalogue]
|
|
150
|
+
*/
|
|
151
|
+
export function catalogueBlock(catalogue?: Record<string, any>): string;
|
|
152
|
+
/** The three laws. Stated to the author; enforced, where checkable, by M2. */
|
|
153
|
+
export function lawsBlock(): string;
|
|
154
|
+
/**
|
|
155
|
+
* The whole authoring prompt, as one string.
|
|
156
|
+
*
|
|
157
|
+
* Two people fed this brief and neither of them is the model: a person who is not
|
|
158
|
+
* a software engineer answered an interview, and a read-only survey produced the
|
|
159
|
+
* facts. The prompt says so, because round 1 measured what happens when a model
|
|
160
|
+
* treats a lay answer as a specification.
|
|
161
|
+
*
|
|
162
|
+
* `verdictType` is REQUIRED and has no default. It is the user's own answer and
|
|
163
|
+
* the one thing that tells the composer what DONE is allowed to mean here; a
|
|
164
|
+
* defaulted class would be the prompt answering a question the user was asked.
|
|
165
|
+
*
|
|
166
|
+
* @param {{answers: Record<string|number, string>, questions?: Record<string|number, string>,
|
|
167
|
+
* facts: any, listingBlock: string, lang: string, verdictType: string,
|
|
168
|
+
* guards: {name: string, kind: string, params: Record<string, any>, fill: string[]}[],
|
|
169
|
+
* ownedEnvNames?: string[], mode?: 'tool'|'text', catalogue?: Record<string, any>}} o
|
|
170
|
+
*/
|
|
171
|
+
export function authorPrompt({ answers, questions, facts, listingBlock, lang, verdictType, guards, ownedEnvNames, mode, catalogue }: {
|
|
172
|
+
answers: Record<string | number, string>;
|
|
173
|
+
questions?: Record<string | number, string>;
|
|
174
|
+
facts: any;
|
|
175
|
+
listingBlock: string;
|
|
176
|
+
lang: string;
|
|
177
|
+
verdictType: string;
|
|
178
|
+
guards: {
|
|
179
|
+
name: string;
|
|
180
|
+
kind: string;
|
|
181
|
+
params: Record<string, any>;
|
|
182
|
+
fill: string[];
|
|
183
|
+
}[];
|
|
184
|
+
ownedEnvNames?: string[];
|
|
185
|
+
mode?: "tool" | "text";
|
|
186
|
+
catalogue?: Record<string, any>;
|
|
187
|
+
}): string;
|
|
188
|
+
/**
|
|
189
|
+
* The seed read, as the model sees it: mechanical labels, no editorial. Per-stage
|
|
190
|
+
* gap capped with the trim ANNOUNCED (F28 — a bound that eats the failure names
|
|
191
|
+
* deletes exactly the mechanical detail that converts).
|
|
192
|
+
*
|
|
193
|
+
* SCRUBBED at the emission boundary. What this block renders is raw subprocess
|
|
194
|
+
* output (gap lines, an instrument stop carrying a command's stderr) and it
|
|
195
|
+
* becomes the next USER TURN sent to the provider — the surface most likely to
|
|
196
|
+
* carry a real credential, crossing the network. It goes through the ONE shared
|
|
197
|
+
* redactor, the same one `askDeclaration` puts the model's own text through and
|
|
198
|
+
* the same one `declaredclose`/`authorjob` put these very fields through; never
|
|
199
|
+
* a second inventory. Applied to the whole rendered block rather than one field,
|
|
200
|
+
* so a line added here is scrubbed by construction.
|
|
201
|
+
* @param {any[]} rows M1 `StageResult[]`
|
|
202
|
+
* @param {{gapCap?: number}} [o]
|
|
203
|
+
*/
|
|
204
|
+
export function renderSeedReadBlock(rows: any[], { gapCap }?: {
|
|
205
|
+
gapCap?: number;
|
|
206
|
+
}): string;
|
|
207
|
+
/**
|
|
208
|
+
* The measured block when the declaration never reached execution. Both cases are
|
|
209
|
+
* machine outputs of the pipeline, never editorial prose about the model's work.
|
|
210
|
+
*
|
|
211
|
+
* SCRUBBED at the emission boundary, for the SAME reason `renderSeedReadBlock`
|
|
212
|
+
* is and through the SAME one redactor. The channel here is different and just
|
|
213
|
+
* as real: a validation red QUOTES WHAT THE MODEL DECLARED, verbatim — the
|
|
214
|
+
* genre-env branch echoes the expected and the declared value, the deny floor
|
|
215
|
+
* echoes the whole `cmd`, the listing rule echoes the path — and this block
|
|
216
|
+
* becomes the next USER TURN sent to the provider. A declared value shaped like
|
|
217
|
+
* a credential crossed unmasked before this line existed. Applied to the whole
|
|
218
|
+
* rendered block rather than one field, so a red added to the validator is
|
|
219
|
+
* scrubbed by construction rather than by remembering.
|
|
220
|
+
* @param {{kind: 'validation'|'artifact', reds: (Red|string)[], cap?: number}} o
|
|
221
|
+
*/
|
|
222
|
+
export function renderRejectBlock({ kind, reds, cap }: {
|
|
223
|
+
kind: "validation" | "artifact";
|
|
224
|
+
reds: (Red | string)[];
|
|
225
|
+
cap?: number;
|
|
226
|
+
}): string;
|
|
227
|
+
/** A revise turn is EXACTLY the measured block, a blank line, and the instruction. */
|
|
228
|
+
export function buildReviseTurn(measuredBlock: string): string;
|
|
229
|
+
/**
|
|
230
|
+
* The structural guarantee that no other prose can enter a revise turn. Without
|
|
231
|
+
* it "the feedback is measurement only" is a promise; with it, it is a fact that
|
|
232
|
+
* fails the build the day someone appends a helpful sentence.
|
|
233
|
+
* @param {string} turn @param {string} measuredBlock
|
|
234
|
+
*/
|
|
235
|
+
export function assertReviseTurn(turn: string, measuredBlock: string): {
|
|
236
|
+
ok: boolean;
|
|
237
|
+
bytes: number;
|
|
238
|
+
};
|
|
239
|
+
/**
|
|
240
|
+
* Apply the genre's environment to a declaration, mechanically.
|
|
241
|
+
*
|
|
242
|
+
* Three rules, and each one is a finding:
|
|
243
|
+
* - it lands on every stage that SPAWNS something (env-capable by the
|
|
244
|
+
* catalogue) and nowhere else — a stage that runs no process has no
|
|
245
|
+
* environment to set;
|
|
246
|
+
* - a model-authored copy of a genre-owned name is DROPPED and ANNOUNCED. M2's
|
|
247
|
+
* validator already reds that, so this is defence in depth — but the failure
|
|
248
|
+
* direction matters: silently keeping the model's guess is how round 2's arm
|
|
249
|
+
* B graded an unedited checkout;
|
|
250
|
+
* - a genre variable this patient cannot supply is a RED, not an omission. It
|
|
251
|
+
* moves no seed number, so no seed-verdict read can ever see it missing, and
|
|
252
|
+
* the artefact it would be missing from is SIGNED.
|
|
253
|
+
*
|
|
254
|
+
* The input declaration is never mutated: the model's own copy has to stay what
|
|
255
|
+
* the model wrote, or the next turn's validator reds it for our injection.
|
|
256
|
+
* @param {any} declaration
|
|
257
|
+
* @param {string} lang
|
|
258
|
+
* @param {{sourcePrefixes?: string[], catalogue?: Record<string, any>}} [o]
|
|
259
|
+
* @returns {{declaration: any, applied: Record<string, string>, dropped: {stage: string, name: string}[],
|
|
260
|
+
* missing: string[], reds: Red[]}}
|
|
261
|
+
*/
|
|
262
|
+
export function applyGenreEnv(declaration: any, lang: string, { sourcePrefixes, catalogue }?: {
|
|
263
|
+
sourcePrefixes?: string[];
|
|
264
|
+
catalogue?: Record<string, any>;
|
|
265
|
+
}): {
|
|
266
|
+
declaration: any;
|
|
267
|
+
applied: Record<string, string>;
|
|
268
|
+
dropped: {
|
|
269
|
+
stage: string;
|
|
270
|
+
name: string;
|
|
271
|
+
}[];
|
|
272
|
+
missing: string[];
|
|
273
|
+
reds: Red[];
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* The prefixes `genreEnv` JOINS into the genre's variable, resolved against the
|
|
277
|
+
* real tree. Two filters, and the second one was paid for on a live patient.
|
|
278
|
+
*
|
|
279
|
+
* EXISTENCE (unchanged): a prefix that names nothing at the seed would build a
|
|
280
|
+
* variable pointing at nothing, and the grounded validator refuses it anyway
|
|
281
|
+
* (`ungroundedGenreEnv` checks every ELEMENT against the seed listing).
|
|
282
|
+
*
|
|
283
|
+
* IDENTITY (new): existence alone reads a SYMLINK and the directory behind it as
|
|
284
|
+
* two prefixes. On aurora, 11 entries under `src` resolve into the per-package
|
|
285
|
+
* `packages/<pkg>/src` trees, so the joined `MYPYPATH` named one tree twice
|
|
286
|
+
* under two spellings and mypy died FATALLY — exit 2, "shadows library module",
|
|
287
|
+
* zero output — which the close reads as a broken instrument on a perfectly
|
|
288
|
+
* healthy patient. The realpath-deduped control produced the identical clean
|
|
289
|
+
* reading as the known-good invocation.
|
|
290
|
+
*
|
|
291
|
+
* WHICH SPELLING SURVIVES is not cosmetic. The REAL path wins, because that is
|
|
292
|
+
* the one the checker itself resolves to; the symlink spelling is kept only when
|
|
293
|
+
* the real one cannot be used. And a spelling is only ever emitted when it
|
|
294
|
+
* SELECTS from the seed listing, because the arbiter records this value in a
|
|
295
|
+
* SIGNED envelope and re-validates it GROUNDED — a value the grounded validator
|
|
296
|
+
* cannot select is one the arbiter must never build. So a link out of the
|
|
297
|
+
* repository, or into a path git does not track, keeps its declared spelling
|
|
298
|
+
* rather than becoming an absolute path nothing can ground.
|
|
299
|
+
*
|
|
300
|
+
* An unreadable candidate (a dangling link, a race) is NOT dropped: it passed the
|
|
301
|
+
* existence filter against the listing, and silently losing a real prefix is the
|
|
302
|
+
* Result-5 hazard in the other direction.
|
|
303
|
+
* @param {{workdir: string, sourcePaths: any, seedFiles: string[]}} o
|
|
304
|
+
* @returns {string[]}
|
|
305
|
+
*/
|
|
306
|
+
export function resolveSourcePrefixes({ workdir, sourcePaths, seedFiles }: {
|
|
307
|
+
workdir: string;
|
|
308
|
+
sourcePaths: any;
|
|
309
|
+
seedFiles: string[];
|
|
310
|
+
}): string[];
|
|
311
|
+
/**
|
|
312
|
+
* F6's shape: an unpriced call or round makes the TOTAL unknown, and unknown is
|
|
313
|
+
* reported as null — never as the sum of the priced half wearing a complete coat.
|
|
314
|
+
* `knownUsd` carries the priced half explicitly so nothing is hidden either way.
|
|
315
|
+
*
|
|
316
|
+
* IT IS ALSO THE RUN'S AUDIT OF WHAT WAS SAID, not only of what was spent. Every
|
|
317
|
+
* `add` records the call's RAW output beside its price, through the one
|
|
318
|
+
* scrubbed-persist helper — one list of calls, read two ways. That pairing is
|
|
319
|
+
* structural rather than remembered: run mslhn707 metered a $0.053 scout call
|
|
320
|
+
* and kept not one byte of what it returned, so the malformation that killed the
|
|
321
|
+
* run could not be read afterwards. A cost entry with no raw beside it is that
|
|
322
|
+
* run again.
|
|
323
|
+
*/
|
|
324
|
+
export function makeCostBook(): {
|
|
325
|
+
/** @param {string} label @param {any} r a bare-agent run result @param {number} [attempt] */
|
|
326
|
+
add(label: string, r: any, attempt?: number): {
|
|
327
|
+
label: string;
|
|
328
|
+
costUsd: number | null;
|
|
329
|
+
unpricedRounds: number;
|
|
330
|
+
} | undefined;
|
|
331
|
+
/** @param {{label: string, costUsd: number|null, unpricedRounds: number}[]} calls
|
|
332
|
+
* @param {ReturnType<typeof scrubRaw>[]} [callRaws] */
|
|
333
|
+
absorb(calls: {
|
|
334
|
+
label: string;
|
|
335
|
+
costUsd: number | null;
|
|
336
|
+
unpricedRounds: number;
|
|
337
|
+
}[], callRaws?: ReturnType<typeof scrubRaw>[]): void;
|
|
338
|
+
/** the persisted raws, in call order — the same order and the same labels as
|
|
339
|
+
* `report().calls`, because both are readings of one list */
|
|
340
|
+
raws(): {
|
|
341
|
+
label: string;
|
|
342
|
+
attempt: number;
|
|
343
|
+
bytes: number;
|
|
344
|
+
trimmed: boolean;
|
|
345
|
+
text: string;
|
|
346
|
+
cause: string | null;
|
|
347
|
+
reason: string | null;
|
|
348
|
+
}[];
|
|
349
|
+
report(): {
|
|
350
|
+
costUsd: number | null;
|
|
351
|
+
knownUsd: number;
|
|
352
|
+
spendComplete: boolean;
|
|
353
|
+
nullCostCalls: number;
|
|
354
|
+
unpricedRounds: number;
|
|
355
|
+
calls: {
|
|
356
|
+
label: string;
|
|
357
|
+
costUsd: number | null;
|
|
358
|
+
unpricedRounds: number;
|
|
359
|
+
}[];
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* The default model boundary: one bare-agent Loop per call, with the declaration
|
|
364
|
+
* tool wired to END the call the moment it is used. The tool is an OUTPUT
|
|
365
|
+
* CHANNEL, not a step in a conversation, so the round that would acknowledge its
|
|
366
|
+
* result has nothing left to say.
|
|
367
|
+
*
|
|
368
|
+
* MEASURED against the real Loop with a counting provider, both arms: stop-wired
|
|
369
|
+
* = 1 provider round, $0.001; unwired = 2 rounds, $0.002. So the wiring halves
|
|
370
|
+
* the round count of every authoring and revise call — that is the number, not
|
|
371
|
+
* an estimate, and it is why tool mode does not cost double what text mode did.
|
|
372
|
+
*
|
|
373
|
+
* `cacheMessages` (F18) and `maxTokens` (F30) are not optional on this stack.
|
|
374
|
+
* @param {any} provider @param {{system?: string, maxTokens?: number}} [o]
|
|
375
|
+
* @returns {(messages: any[], tools: any[], opts?: any) => Promise<any>}
|
|
376
|
+
*/
|
|
377
|
+
export function makeLoopGenerate(provider: any, { system, maxTokens }?: {
|
|
378
|
+
system?: string;
|
|
379
|
+
maxTokens?: number;
|
|
380
|
+
}): (messages: any[], tools: any[], opts?: any) => Promise<any>;
|
|
381
|
+
/**
|
|
382
|
+
* THE AUTHORING FLOW.
|
|
383
|
+
*
|
|
384
|
+
* Everything cheap happens BEFORE any token is spent: an ABSENT survey, an
|
|
385
|
+
* unreadable listing, an unsupported genre and a genre variable that cannot be
|
|
386
|
+
* built are all $0 refusals, because a paid call that was always going to be
|
|
387
|
+
* thrown away is a paid call nobody needed.
|
|
388
|
+
*
|
|
389
|
+
* The return is deliberately complete rather than tidy:
|
|
390
|
+
* - `ok` is true iff a declaration passed validation and was measured;
|
|
391
|
+
* - `declaration` is the LAST ACCEPTED one, genre-env injected — the exact form
|
|
392
|
+
* that will run and that the signature will cover;
|
|
393
|
+
* - `reds` is what the LAST validated iteration said. `ok:true` with a non-empty
|
|
394
|
+
* `reds` means the final revision was rejected and the previous close stands:
|
|
395
|
+
* the rejection is reported, never hidden, and never silently shipped either;
|
|
396
|
+
* - `iterations` carries every intermediate declaration, validation, seed read
|
|
397
|
+
* and attempt count, because M4 emits them and a human signs against them;
|
|
398
|
+
* - `raws` carries what every model call actually SAID — the scout's attempts
|
|
399
|
+
* and the declaration's, scrubbed and bounded through the one persist
|
|
400
|
+
* helper, in the cost book's own order and under its own labels. It is
|
|
401
|
+
* present on EVERY path including the $0 preflight refusals, because the
|
|
402
|
+
* path that spends nothing more is exactly the one whose evidence used to
|
|
403
|
+
* vanish with the process.
|
|
404
|
+
*
|
|
405
|
+
* No spine emission here (M4 wires events).
|
|
406
|
+
*
|
|
407
|
+
* @param {{workdir: string, seedRef: string, lang: string, verdictType: string,
|
|
408
|
+
* answers: Record<string|number, string>, questions?: Record<string|number, string>,
|
|
409
|
+
* scout: {state: string, facts: any, reason?: string|null, calls?: any[], raws?: any[]},
|
|
410
|
+
* listing?: {stop: string|null, files: string[]|null, block: string|null, meta: any}|null,
|
|
411
|
+
* generate: Function, seedReadFn?: Function, closeCtx?: any,
|
|
412
|
+
* maxRevisions?: number, structureRetries?: number,
|
|
413
|
+
* structuredMode?: 'tool'|'text', catalogue?: Record<string, any>}} o
|
|
414
|
+
*/
|
|
415
|
+
export function authorClose({ workdir, seedRef, lang, verdictType, answers, questions, scout, listing, generate, seedReadFn, closeCtx, maxRevisions, structureRetries, structuredMode, catalogue, }: {
|
|
416
|
+
workdir: string;
|
|
417
|
+
seedRef: string;
|
|
418
|
+
lang: string;
|
|
419
|
+
verdictType: string;
|
|
420
|
+
answers: Record<string | number, string>;
|
|
421
|
+
questions?: Record<string | number, string>;
|
|
422
|
+
scout: {
|
|
423
|
+
state: string;
|
|
424
|
+
facts: any;
|
|
425
|
+
reason?: string | null;
|
|
426
|
+
calls?: any[];
|
|
427
|
+
raws?: any[];
|
|
428
|
+
};
|
|
429
|
+
listing?: {
|
|
430
|
+
stop: string | null;
|
|
431
|
+
files: string[] | null;
|
|
432
|
+
block: string | null;
|
|
433
|
+
meta: any;
|
|
434
|
+
} | null;
|
|
435
|
+
generate: Function;
|
|
436
|
+
seedReadFn?: Function;
|
|
437
|
+
closeCtx?: any;
|
|
438
|
+
maxRevisions?: number;
|
|
439
|
+
structureRetries?: number;
|
|
440
|
+
structuredMode?: "tool" | "text";
|
|
441
|
+
catalogue?: Record<string, any>;
|
|
442
|
+
}): Promise<any>;
|
|
443
|
+
/** @typedef {{code: string, path: string, detail: string, [k: string]: any}} Red */
|
|
444
|
+
/** the output channel's name. It takes no action; it carries the answer. */
|
|
445
|
+
export const DECLARATION_TOOL_NAME: "declare_close";
|
|
446
|
+
/** what the tool returns. The loop stops on delivery, so the model never reads it. */
|
|
447
|
+
export const DECLARATION_ACK: "declaration received";
|
|
448
|
+
/** addendum 5: unlimited revising launders thrash as adaptation (v1.12's rule). */
|
|
449
|
+
export const MAX_REVISIONS: 2;
|
|
450
|
+
/** a malformed EMISSION is a transport fault, bounded on its own axis */
|
|
451
|
+
export const MAX_STRUCTURE_RETRIES: 2;
|
|
452
|
+
/** per-stage gap lines carried into a revise turn, trim announced (F28) */
|
|
453
|
+
export const REVISE_GAP_CAP: 15;
|
|
454
|
+
/** validator reds carried into a revise turn, trim announced (F28) */
|
|
455
|
+
export const REVISE_RED_CAP: 20;
|
|
456
|
+
/** F18 / F30 — caching on, and the 4096 provider default cannot hold a declaration */
|
|
457
|
+
export const AUTHOR_MAX_TOKENS: 32000;
|
|
458
|
+
/** the one slot the model fills on an injected guard */
|
|
459
|
+
export const FILL_MARKER: "<FILL IN>";
|
|
460
|
+
/**
|
|
461
|
+
* THE FROZEN REVISE INSTRUCTION — gate-2 round 3's own text, byte for byte
|
|
462
|
+
* (`scratch-gate2/author.mjs:63`, the turn that PASSED both arms). It is the only
|
|
463
|
+
* prose a revise turn may carry, this constant is its only source, and the suite
|
|
464
|
+
* asserts its bytes: an edit reds the suite and forces a decision rather than
|
|
465
|
+
* quietly changing what every future close is corrected against.
|
|
466
|
+
*/
|
|
467
|
+
export const REVISE_INSTRUCTION: string;
|
|
468
|
+
/**
|
|
469
|
+
* The structure re-ask, per mode. DELIBERATELY NOT `REVISE_INSTRUCTION`: nothing
|
|
470
|
+
* has been measured, there is no declaration to judge, and reusing the revise
|
|
471
|
+
* text would tell a model to "judge its own declaration against them" when there
|
|
472
|
+
* is no them.
|
|
473
|
+
*/
|
|
474
|
+
export const STRUCTURE_INSTRUCTION_TOOL: string;
|
|
475
|
+
export const STRUCTURE_INSTRUCTION_TEXT: string;
|
|
476
|
+
/**
|
|
477
|
+
* THE INTERVIEW, KEYED BY VERDICT CLASS (hamr's ruling, PRD v1.57 §2).
|
|
478
|
+
*
|
|
479
|
+
* One question set per GENRE has no terminating shape — genres are a fat long
|
|
480
|
+
* tail that cannot be enumerated, and TYPES was the first specimen rather than
|
|
481
|
+
* the pattern. One set per verdict CLASS has exactly three. Genre understanding
|
|
482
|
+
* does not disappear: it moves into the COMPOSITION over the kind catalogue
|
|
483
|
+
* (`CLASS_STATEMENTS` below, rendered into the authoring prompt), which is the
|
|
484
|
+
* layer already built to hold it — the authoring call composes owned kinds and
|
|
485
|
+
* cannot emit anything else (D2/D3), so a genre it has never seen is a
|
|
486
|
+
* composition problem, not a catalogue problem.
|
|
487
|
+
*
|
|
488
|
+
* The GREEN set is questions 1–6 of the frozen TYPES set (prereg §5) BYTE FOR
|
|
489
|
+
* BYTE. What is gone is the seventh slot — D13's genre CONFIRM ("this looks like
|
|
490
|
+
* a type-fixing job, correct?") — superseded in full: the interview never asks
|
|
491
|
+
* about a genre again. The refusal it carried did not disappear; it MOVED to the
|
|
492
|
+
* composer, on the same counted `request-red` path (see `authorCloseForJob`).
|
|
493
|
+
* The remaining six are already genre-neutral, which is why generalising them
|
|
494
|
+
* required deleting a slot rather than rewording one.
|
|
495
|
+
*/
|
|
496
|
+
export const GREEN_QUESTIONS: Readonly<{
|
|
497
|
+
1: "What do you want done?";
|
|
498
|
+
2: "Which files or folders should change?";
|
|
499
|
+
3: "Is there anything that must not change?";
|
|
500
|
+
4: "How do you check today whether it's working?";
|
|
501
|
+
5: "What would make you say this came back worse than before?";
|
|
502
|
+
6: "Is there a code repo I can look at? Where?";
|
|
503
|
+
}>;
|
|
504
|
+
/**
|
|
505
|
+
* The three sets. `questions: null` on a locked class is ABSENT, never `{}`:
|
|
506
|
+
* an empty set would read as "this class needs no questions", which is a
|
|
507
|
+
* different and false statement (F59's distinction, at the interview layer).
|
|
508
|
+
* Nothing ever reads them — a locked class refuses at ADMISSION, before its
|
|
509
|
+
* questions run — and the two readers below throw rather than hand back nothing.
|
|
510
|
+
* @type {Record<string, {locked: boolean, questions: Record<string|number, string>|null, required: readonly number[]|null}>}
|
|
511
|
+
*/
|
|
512
|
+
export const QUESTION_SETS: Record<string, {
|
|
513
|
+
locked: boolean;
|
|
514
|
+
questions: Record<string | number, string> | null;
|
|
515
|
+
required: readonly number[] | null;
|
|
516
|
+
}>;
|
|
517
|
+
/**
|
|
518
|
+
* WHERE GENRE UNDERSTANDING NOW LIVES: the plain statement the composer is fed —
|
|
519
|
+
* *the user has this problem and declared it green/deterministic*. It is the
|
|
520
|
+
* user's own answer travelling into the composition call, and the composer is
|
|
521
|
+
* what turns it into stages over owned kinds.
|
|
522
|
+
*
|
|
523
|
+
* A locked class states NOTHING rather than something bland: a sentence here
|
|
524
|
+
* would be a class this build can compose for, and it cannot.
|
|
525
|
+
* @type {Record<string, string|null>}
|
|
526
|
+
*/
|
|
527
|
+
export const CLASS_STATEMENTS: Record<string, string | null>;
|
|
528
|
+
export const AUTHOR_SYSTEM: string;
|
|
529
|
+
/**
|
|
530
|
+
* ONE schema per PARAMETER NAME, composed into kinds by the catalogue's own
|
|
531
|
+
* `required`/`optional` lists. Keyed by name rather than by kind on purpose: the
|
|
532
|
+
* catalogue already decides which kind takes which parameter, and a per-kind
|
|
533
|
+
* table would be a second place for that fact to live.
|
|
534
|
+
*
|
|
535
|
+
* What is genuinely NEW here is only the JSON TYPE of each leaf — the catalogue
|
|
536
|
+
* states it as prose (`shape`) for the prompt, and nothing in the tree states it
|
|
537
|
+
* as a schema. Everything a schema could DRIFT on — which kinds exist, which are
|
|
538
|
+
* locked, which parameters each takes, which values an enum admits — is read
|
|
539
|
+
* from M2 and M1, and {@link schemaCoverage} makes the remaining seam mechanical:
|
|
540
|
+
* a catalogue that grows a parameter this table does not know about fails LOUDLY
|
|
541
|
+
* at schema-build time rather than silently omitting it from what the model can
|
|
542
|
+
* say.
|
|
543
|
+
*/
|
|
544
|
+
export const PARAM_SCHEMAS: Readonly<{
|
|
545
|
+
cmd: {
|
|
546
|
+
type: string;
|
|
547
|
+
description: string;
|
|
548
|
+
};
|
|
549
|
+
args: {
|
|
550
|
+
type: string;
|
|
551
|
+
items: {
|
|
552
|
+
type: string;
|
|
553
|
+
};
|
|
554
|
+
};
|
|
555
|
+
expectExit: {
|
|
556
|
+
type: string;
|
|
557
|
+
};
|
|
558
|
+
timeoutMs: {
|
|
559
|
+
type: string;
|
|
560
|
+
minimum: number;
|
|
561
|
+
};
|
|
562
|
+
env: {
|
|
563
|
+
type: string;
|
|
564
|
+
additionalProperties: {
|
|
565
|
+
type: string;
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
parser: Readonly<{
|
|
569
|
+
description: string;
|
|
570
|
+
oneOf: ({
|
|
571
|
+
title: string;
|
|
572
|
+
type: string;
|
|
573
|
+
properties: {
|
|
574
|
+
terms: {
|
|
575
|
+
type: string;
|
|
576
|
+
minItems: number;
|
|
577
|
+
maxItems: number;
|
|
578
|
+
items: {
|
|
579
|
+
type: string;
|
|
580
|
+
properties: {
|
|
581
|
+
lineMatch: {
|
|
582
|
+
type: string;
|
|
583
|
+
description: string;
|
|
584
|
+
};
|
|
585
|
+
capture: {
|
|
586
|
+
type: string;
|
|
587
|
+
minimum: number;
|
|
588
|
+
description: string;
|
|
589
|
+
};
|
|
590
|
+
sign: {
|
|
591
|
+
enum: number[];
|
|
592
|
+
description: string;
|
|
593
|
+
};
|
|
594
|
+
aggregate: {
|
|
595
|
+
enum: string[];
|
|
596
|
+
description: string;
|
|
597
|
+
};
|
|
598
|
+
region: {
|
|
599
|
+
oneOf: ({
|
|
600
|
+
const: string;
|
|
601
|
+
type?: undefined;
|
|
602
|
+
properties?: undefined;
|
|
603
|
+
required?: undefined;
|
|
604
|
+
additionalProperties?: undefined;
|
|
605
|
+
} | {
|
|
606
|
+
type: string;
|
|
607
|
+
properties: {
|
|
608
|
+
anchor: {
|
|
609
|
+
type: string;
|
|
610
|
+
};
|
|
611
|
+
capture: {
|
|
612
|
+
type: string;
|
|
613
|
+
minimum: number;
|
|
614
|
+
description: string;
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
required: string[];
|
|
618
|
+
additionalProperties: boolean;
|
|
619
|
+
const?: undefined;
|
|
620
|
+
})[];
|
|
621
|
+
};
|
|
622
|
+
};
|
|
623
|
+
required: string[];
|
|
624
|
+
additionalProperties: boolean;
|
|
625
|
+
};
|
|
626
|
+
};
|
|
627
|
+
lineMatch?: undefined;
|
|
628
|
+
capture?: undefined;
|
|
629
|
+
};
|
|
630
|
+
required: string[];
|
|
631
|
+
additionalProperties: boolean;
|
|
632
|
+
description?: undefined;
|
|
633
|
+
} | {
|
|
634
|
+
title: string;
|
|
635
|
+
type: string;
|
|
636
|
+
description: string;
|
|
637
|
+
properties: {
|
|
638
|
+
lineMatch: {
|
|
639
|
+
type: string;
|
|
640
|
+
};
|
|
641
|
+
capture: {
|
|
642
|
+
type: string;
|
|
643
|
+
minimum: number;
|
|
644
|
+
};
|
|
645
|
+
terms?: undefined;
|
|
646
|
+
};
|
|
647
|
+
required: string[];
|
|
648
|
+
additionalProperties: boolean;
|
|
649
|
+
})[];
|
|
650
|
+
}>;
|
|
651
|
+
scope: Readonly<{
|
|
652
|
+
type: "object";
|
|
653
|
+
description: "which population this stage counts. A path prefix, read off the seed listing.";
|
|
654
|
+
properties: {
|
|
655
|
+
includePrefixes: {
|
|
656
|
+
type: string;
|
|
657
|
+
minItems: number;
|
|
658
|
+
items: {
|
|
659
|
+
type: string;
|
|
660
|
+
};
|
|
661
|
+
};
|
|
662
|
+
excludePrefixes: {
|
|
663
|
+
type: string;
|
|
664
|
+
minItems: number;
|
|
665
|
+
items: {
|
|
666
|
+
type: string;
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
};
|
|
670
|
+
additionalProperties: false;
|
|
671
|
+
}>;
|
|
672
|
+
direction: {
|
|
673
|
+
enum: string[];
|
|
674
|
+
};
|
|
675
|
+
baseline: {
|
|
676
|
+
enum: (string | number)[];
|
|
677
|
+
description: string;
|
|
678
|
+
};
|
|
679
|
+
patterns: {
|
|
680
|
+
type: string;
|
|
681
|
+
minItems: number;
|
|
682
|
+
items: {
|
|
683
|
+
type: string;
|
|
684
|
+
properties: {
|
|
685
|
+
id: {
|
|
686
|
+
type: string;
|
|
687
|
+
};
|
|
688
|
+
regex: {
|
|
689
|
+
type: string;
|
|
690
|
+
};
|
|
691
|
+
};
|
|
692
|
+
required: string[];
|
|
693
|
+
additionalProperties: boolean;
|
|
694
|
+
};
|
|
695
|
+
};
|
|
696
|
+
extensions: {
|
|
697
|
+
type: string;
|
|
698
|
+
minItems: number;
|
|
699
|
+
items: {
|
|
700
|
+
type: string;
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
allowPrefixes: {
|
|
704
|
+
type: string;
|
|
705
|
+
minItems: number;
|
|
706
|
+
items: {
|
|
707
|
+
type: string;
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
requireNonEmpty: {
|
|
711
|
+
const: boolean;
|
|
712
|
+
};
|
|
713
|
+
}>;
|
|
714
|
+
export type Red = {
|
|
715
|
+
code: string;
|
|
716
|
+
path: string;
|
|
717
|
+
detail: string;
|
|
718
|
+
[k: string]: any;
|
|
719
|
+
};
|
|
720
|
+
import { scrubRaw } from './text.js';
|