bareloop 0.42.0 → 0.44.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 +88 -0
- package/bareloop.context.md +103 -41
- package/package.json +2 -2
- package/src/authorflow.js +26 -11
- package/src/authoring.js +1 -1
- package/src/authorjob.js +33 -15
- package/src/authorreadout.js +7 -6
- package/src/declaredclose.js +6 -3
- package/src/draftspend.js +20 -2
- package/src/facts.js +54 -9
- package/src/factwords.js +58 -6
- package/src/inputlook.js +38 -3
- package/src/interviewrun.js +0 -1
- package/src/job.js +39 -12
- package/src/kinds.js +5 -0
- package/src/pagedropwords.js +64 -0
- package/src/pagemap.js +264 -0
- package/src/pagerules.js +319 -0
- package/src/panel/authorroutes.js +138 -60
- package/src/panel/authorsession.js +88 -32
- package/src/panel/index.html +1251 -522
- package/src/panel/jobplan.js +4 -2
- package/src/panel/runroutes.js +188 -6
- package/src/panel/scheduler.js +156 -0
- package/src/panel/schedules.js +667 -0
- package/src/panel/schedulestart.js +243 -0
- package/src/panel/server.js +1234 -68
- package/src/panel/status.js +33 -0
- package/src/panelmoney.js +29 -0
- package/src/planrun.js +92 -11
- package/src/promptregisters.js +4 -0
- package/src/providerrows.js +3 -3
- package/src/replay.js +118 -7
- package/src/reviewdoor.js +37 -0
- package/src/rubricstep.js +123 -11
- package/src/runlist.js +37 -1
- package/src/schedulewords.js +299 -0
- package/src/source.js +18 -0
- package/src/thinking.js +63 -2
- package/src/trigger.js +48 -0
- package/src/triggerclock.js +127 -0
- package/src/triggerfiles.js +215 -0
- package/src/triggerledger.js +79 -0
- package/src/userrun.js +34 -21
- package/types/authorflow.d.ts +42 -6
- package/types/authorjob.d.ts +10 -2
- package/types/declaredclose.d.ts +3 -1
- package/types/draftspend.d.ts +27 -1
- package/types/facts.d.ts +15 -0
- package/types/factwords.d.ts +26 -3
- package/types/inputlook.d.ts +15 -0
- package/types/job.d.ts +8 -1
- package/types/kinds.d.ts +5 -0
- package/types/pagedropwords.d.ts +26 -0
- package/types/pagemap.d.ts +154 -0
- package/types/pagerules.d.ts +86 -0
- package/types/panel/authorroutes.d.ts +50 -14
- package/types/panel/authorsession.d.ts +37 -12
- package/types/panel/runroutes.d.ts +32 -1
- package/types/panel/scheduler.d.ts +17 -0
- package/types/panel/schedules.d.ts +412 -0
- package/types/panel/schedulestart.d.ts +106 -0
- package/types/panel/server.d.ts +297 -15
- package/types/panel/status.d.ts +51 -0
- package/types/panelmoney.d.ts +5 -0
- package/types/planrun.d.ts +2 -0
- package/types/providerrows.d.ts +2 -2
- package/types/replay.d.ts +21 -1
- package/types/reviewdoor.d.ts +27 -0
- package/types/rubricstep.d.ts +57 -1
- package/types/runlist.d.ts +24 -1
- package/types/schedulewords.d.ts +137 -0
- package/types/source.d.ts +11 -0
- package/types/thinking.d.ts +39 -0
- package/types/trigger.d.ts +37 -0
- package/types/triggerclock.d.ts +52 -0
- package/types/triggerfiles.d.ts +93 -0
- package/types/triggerledger.d.ts +35 -0
- package/types/userrun.d.ts +4 -0
package/types/panel/server.d.ts
CHANGED
|
@@ -116,7 +116,7 @@ export function legDividersFor(spineRecords: any[], parts: any[]): {
|
|
|
116
116
|
*
|
|
117
117
|
* The table (outcome → reason / next / buttons) is PANEL-BUILD.md P5 item 1. The
|
|
118
118
|
* Resume button appears only when `o.resume` says the engine would accept it; "Reuse workflow"
|
|
119
|
-
* (replaces P5 item 3, 2026-10-03
|
|
119
|
+
* (replaces P5 item 3, 2026-10-03; one door on every ending since 2026-10-15) starts a NEW run.
|
|
120
120
|
* @param {{outcome: string|null, stopReason: string|null, spentUsd: number|null, budgetUsd: number|null, lastEscalation?: any}} summary
|
|
121
121
|
* @param {{died: boolean, lastThing: string|null}} death
|
|
122
122
|
* @param {{resume?: {ok: boolean, why?: string}|null, destinationRefused?: string|null, moneyHalt?: boolean,
|
|
@@ -155,6 +155,28 @@ export function endedFor(summary: {
|
|
|
155
155
|
label: string;
|
|
156
156
|
}[];
|
|
157
157
|
} | null;
|
|
158
|
+
/**
|
|
159
|
+
* RULED 2026-10-15 (Remove from Needs you): the ONE place a run's Needs-you state is decided. `Accept` (a live door) is never removable and never ages out.
|
|
160
|
+
* A `Resume` tag leaves when the person removed it AFTER (or at) the stop it is for — a removal covers only the ending it was made on, so a later stop
|
|
161
|
+
* brings the run back — or after `needsYouDays`. `removedMs` is the newest removal that covers the current ending (or null), `expired` is true when only
|
|
162
|
+
* the age rule took a Resume run out.
|
|
163
|
+
* @param {{live: boolean, records: any[], resume: {ok?: boolean}|null|undefined, stoppedAt: string|null, nowMs: number, removedMs?: number}} f
|
|
164
|
+
* @returns {{needs: 'Accept'|'Resume'|null, removedMs: number|null, expired: boolean}}
|
|
165
|
+
*/
|
|
166
|
+
export function needsStateFor(f: {
|
|
167
|
+
live: boolean;
|
|
168
|
+
records: any[];
|
|
169
|
+
resume: {
|
|
170
|
+
ok?: boolean;
|
|
171
|
+
} | null | undefined;
|
|
172
|
+
stoppedAt: string | null;
|
|
173
|
+
nowMs: number;
|
|
174
|
+
removedMs?: number;
|
|
175
|
+
}): {
|
|
176
|
+
needs: "Accept" | "Resume" | null;
|
|
177
|
+
removedMs: number | null;
|
|
178
|
+
expired: boolean;
|
|
179
|
+
};
|
|
158
180
|
/**
|
|
159
181
|
* The Resume route's reader (P5 item 2): the listed row plus the resume plan, from the
|
|
160
182
|
* ONE {@link resumePlanFor} the Ended block asks. `null` when the runid is not listed
|
|
@@ -184,6 +206,43 @@ export function getStopContext(runid: string, opts?: {
|
|
|
184
206
|
live: boolean;
|
|
185
207
|
spineExists: boolean;
|
|
186
208
|
} | null;
|
|
209
|
+
/**
|
|
210
|
+
* The Remove-from-Needs-you route's reader (RULED 2026-10-15): the listed row and the Needs-you tag the runs list shows for it right now
|
|
211
|
+
* (`summarizeRow`, the one rule). `null` when the runid is not listed.
|
|
212
|
+
* @param {string} runid
|
|
213
|
+
* @param {{ home?: string, now?: number }} [opts]
|
|
214
|
+
* @returns {{row: any, needs: 'Accept'|'Resume'|null}|null}
|
|
215
|
+
*/
|
|
216
|
+
export function getNeedsContext(runid: string, opts?: {
|
|
217
|
+
home?: string;
|
|
218
|
+
now?: number;
|
|
219
|
+
}): {
|
|
220
|
+
row: any;
|
|
221
|
+
needs: "Accept" | "Resume" | null;
|
|
222
|
+
} | null;
|
|
223
|
+
/**
|
|
224
|
+
* The ONE rule for a run's Needs-you tag (P9 v2 E): `Resume` when the engine would accept a resume (the SAME `resumePlanFor` answer the Run
|
|
225
|
+
* tab's Resume card shows, already asked once per row by `endedForRow`), until the run has been stopped `needsYouDays` (exactly that old is out;
|
|
226
|
+
* no readable stop time stays in). `Accept` comes from {@link doorWaiting} (T Part 8), asked in `summarizeRow`, never here.
|
|
227
|
+
* @param {{ok?: boolean}|null|undefined} resume @param {string|null} stoppedAt @param {number} nowMs
|
|
228
|
+
* @returns {'Accept'|'Resume'|null}
|
|
229
|
+
*/
|
|
230
|
+
export function needsTag(resume: {
|
|
231
|
+
ok?: boolean;
|
|
232
|
+
} | null | undefined, stoppedAt: string | null, nowMs: number): "Accept" | "Resume" | null;
|
|
233
|
+
/**
|
|
234
|
+
* One entry per job, newest activity first: `headRunid` is the job's newest run BY TIME (never "the first row in server order"), `activityMs` the
|
|
235
|
+
* newest KNOWN time over its runs (`stoppedAt`, else `at`), `quiet` only when that time is known and older than the quiet limit. A job with a live
|
|
236
|
+
* run is never quiet; an unknown age is never guessed into quiet.
|
|
237
|
+
* @param {any[]} rows `listRuns` rows @param {number} nowMs
|
|
238
|
+
* @returns {{job: string, headRunid: string|null, quiet: boolean, activityMs: number|null}[]}
|
|
239
|
+
*/
|
|
240
|
+
export function summarizeJobs(rows: any[], nowMs: number): {
|
|
241
|
+
job: string;
|
|
242
|
+
headRunid: string | null;
|
|
243
|
+
quiet: boolean;
|
|
244
|
+
activityMs: number | null;
|
|
245
|
+
}[];
|
|
187
246
|
/**
|
|
188
247
|
* `GET /api/runs` — every listed run, NEWEST FIRST BY `at` (never by file/
|
|
189
248
|
* append order — a backfill scan appends rows in sorted-PATH order, which
|
|
@@ -197,12 +256,116 @@ export function getStopContext(runid: string, opts?: {
|
|
|
197
256
|
* SAME payload by job client-side (`groupRunsByJob` in `index.html`) rather
|
|
198
257
|
* than a second server-side endpoint, so the glyph/checkType mapping is
|
|
199
258
|
* still computed in exactly one place.
|
|
200
|
-
* @param {{ home?: string }} [opts]
|
|
259
|
+
* @param {{ home?: string, now?: number }} [opts] `now` (ms) is injectable for the Needs-you age rule
|
|
201
260
|
* @returns {any[]}
|
|
202
261
|
*/
|
|
203
262
|
export function listRuns(opts?: {
|
|
204
263
|
home?: string;
|
|
264
|
+
now?: number;
|
|
205
265
|
}): any[];
|
|
266
|
+
/**
|
|
267
|
+
* `GET /api/runs` (P9 v2 F): `{runs, jobs, limits}`. The server decides every state the left list draws — `live`, `needs`, each job's head run and
|
|
268
|
+
* quiet flag, the limits — and the page works out nothing. `scheduled` is the triggers piece (PRD v1.99, T Part 7).
|
|
269
|
+
* @param {{ home?: string, now?: number, schedules?: ReturnType<typeof createScheduleDoor> }} [opts]
|
|
270
|
+
*/
|
|
271
|
+
export function runsListPayload(opts?: {
|
|
272
|
+
home?: string;
|
|
273
|
+
now?: number;
|
|
274
|
+
schedules?: ReturnType<typeof createScheduleDoor>;
|
|
275
|
+
}): {
|
|
276
|
+
runs: any[];
|
|
277
|
+
jobs: {
|
|
278
|
+
job: string;
|
|
279
|
+
headRunid: string | null;
|
|
280
|
+
quiet: boolean;
|
|
281
|
+
activityMs: number | null;
|
|
282
|
+
}[];
|
|
283
|
+
scheduled: any[];
|
|
284
|
+
gaveUp: any[];
|
|
285
|
+
limits: Readonly<{
|
|
286
|
+
quietDays: 30;
|
|
287
|
+
olderRuns: 7;
|
|
288
|
+
needsYouDays: 7;
|
|
289
|
+
}>;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* T Part 8 — what the Accept route needs: the run's row, whether its runner is alive, whether it waits at its review door
|
|
293
|
+
* ({@link doorWaiting}), and the signed spec it was judged by (the file and hash `run-u --door --approve` takes).
|
|
294
|
+
* @param {string} runid @param {{ home?: string }} opts
|
|
295
|
+
* @returns {{row: any, live: boolean, waiting: boolean, specPath: string|null, specHash: string|null}|null}
|
|
296
|
+
*/
|
|
297
|
+
export function getAcceptContext(runid: string, opts?: {
|
|
298
|
+
home?: string;
|
|
299
|
+
}): {
|
|
300
|
+
row: any;
|
|
301
|
+
live: boolean;
|
|
302
|
+
waiting: boolean;
|
|
303
|
+
specPath: string | null;
|
|
304
|
+
specHash: string | null;
|
|
305
|
+
} | null;
|
|
306
|
+
/**
|
|
307
|
+
* The files a run delivered: its own `destination-written` spine records (userrun.js, on a minted green only), one per path, the last record of a
|
|
308
|
+
* path winning. This is the ONLY source of a delivered path anywhere in the panel — no request ever names one.
|
|
309
|
+
* @param {any[]} records
|
|
310
|
+
* @returns {{path: string, bytes: number|null, sha256: string|null}[]}
|
|
311
|
+
*/
|
|
312
|
+
export function deliveredOf(records: any[]): {
|
|
313
|
+
path: string;
|
|
314
|
+
bytes: number | null;
|
|
315
|
+
sha256: string | null;
|
|
316
|
+
}[];
|
|
317
|
+
/**
|
|
318
|
+
* `GET /api/runs/:runid/result` — the Result tab: for each rubric job line, its words and `~` rules from the signed spec, the facts it requires
|
|
319
|
+
* (worded by {@link factSentence}), and — read from the run's own delivered step file, sha256-checked against its `destination-written`
|
|
320
|
+
* record — one row per result with each fact's cell. `passed` is read from the run's own `check-run` record; the tab never re-decides. A red run
|
|
321
|
+
* delivers nothing, so it has no rows: it carries the check's own first gap instead. `null` = no such run or no result tab for it.
|
|
322
|
+
* @param {string} runid @param {{ home?: string }} opts
|
|
323
|
+
*/
|
|
324
|
+
export function getRunResult(runid: string, opts?: {
|
|
325
|
+
home?: string;
|
|
326
|
+
sessionsRoot?: string;
|
|
327
|
+
}): {
|
|
328
|
+
runid: string;
|
|
329
|
+
lines: any[];
|
|
330
|
+
pages: string[];
|
|
331
|
+
pageNote: string | null;
|
|
332
|
+
notChecked: string[];
|
|
333
|
+
door: {
|
|
334
|
+
head: "waiting for you";
|
|
335
|
+
ask: string;
|
|
336
|
+
};
|
|
337
|
+
delivered: {
|
|
338
|
+
i: number;
|
|
339
|
+
path: string;
|
|
340
|
+
name: string;
|
|
341
|
+
bytes: number | null;
|
|
342
|
+
state: "ok" | "changed" | "gone";
|
|
343
|
+
words: string | null;
|
|
344
|
+
}[];
|
|
345
|
+
gap: {
|
|
346
|
+
line: string;
|
|
347
|
+
more: number;
|
|
348
|
+
moreText: string | null;
|
|
349
|
+
} | null;
|
|
350
|
+
} | null;
|
|
351
|
+
/**
|
|
352
|
+
* `GET /api/runs/:runid/download?i=<n>` — delivered file `n` (an index into the run's own `destination-written` list), or why not. The path is
|
|
353
|
+
* the record's, never the request's; the sha256 must still match the record.
|
|
354
|
+
* @param {string} runid @param {unknown} index @param {{ home?: string }} opts
|
|
355
|
+
* @returns {{ok: true, name: string, type: string, buf: Buffer}|{ok: false, code: number, error: string}}
|
|
356
|
+
*/
|
|
357
|
+
export function getRunDownload(runid: string, index: unknown, opts?: {
|
|
358
|
+
home?: string;
|
|
359
|
+
}): {
|
|
360
|
+
ok: true;
|
|
361
|
+
name: string;
|
|
362
|
+
type: string;
|
|
363
|
+
buf: Buffer;
|
|
364
|
+
} | {
|
|
365
|
+
ok: false;
|
|
366
|
+
code: number;
|
|
367
|
+
error: string;
|
|
368
|
+
};
|
|
206
369
|
/**
|
|
207
370
|
* `GET /api/runs/:runid` — the full replay for the Run tab: steps (or
|
|
208
371
|
* iterations), counters, summary-box fields. Looks `runid` up in the run
|
|
@@ -210,11 +373,12 @@ export function listRuns(opts?: {
|
|
|
210
373
|
* header); `null` when the runid is not in the list at all (caller renders
|
|
211
374
|
* 404).
|
|
212
375
|
* @param {string} runid
|
|
213
|
-
* @param {{ home?: string }} [opts]
|
|
376
|
+
* @param {{ home?: string, schedules?: ReturnType<typeof createScheduleDoor> }} [opts]
|
|
214
377
|
* @returns {any|null}
|
|
215
378
|
*/
|
|
216
379
|
export function getRunDetail(runid: string, opts?: {
|
|
217
380
|
home?: string;
|
|
381
|
+
schedules?: ReturnType<typeof createScheduleDoor>;
|
|
218
382
|
}): any | null;
|
|
219
383
|
/**
|
|
220
384
|
* `GET /api/runs/:runid/audit` — the Audit tab's rows, off the run's own
|
|
@@ -286,11 +450,12 @@ export function getRunAudit(runid: string, opts?: {
|
|
|
286
450
|
* resolve to a real window, or the spine file is missing — the caller
|
|
287
451
|
* renders 404, same posture as {@link getRunAudit}/{@link getRunJob}.
|
|
288
452
|
* @param {string} runid
|
|
289
|
-
* @param {{ part?: number, attempt?: number, offset?: number, limit?: number }} query
|
|
453
|
+
* @param {{ all?: boolean, part?: number, attempt?: number, offset?: number, limit?: number }} query
|
|
290
454
|
* @param {{ home?: string }} [opts]
|
|
291
455
|
* @returns {any|null}
|
|
292
456
|
*/
|
|
293
457
|
export function getRunRounds(runid: string, query?: {
|
|
458
|
+
all?: boolean;
|
|
294
459
|
part?: number;
|
|
295
460
|
attempt?: number;
|
|
296
461
|
offset?: number;
|
|
@@ -298,6 +463,78 @@ export function getRunRounds(runid: string, query?: {
|
|
|
298
463
|
}, opts?: {
|
|
299
464
|
home?: string;
|
|
300
465
|
}): any | null;
|
|
466
|
+
/** the Round table's check row: the sign and the plain words, ONE place (PRD v1.106 Part 5 C) @param {number} passed @param {number} failed */
|
|
467
|
+
export function checkRowWords(passed: number, failed: number): {
|
|
468
|
+
sign: string;
|
|
469
|
+
text: string;
|
|
470
|
+
};
|
|
471
|
+
/**
|
|
472
|
+
* "71 model · 91 tool · 0 denied" — the Run summary's `calls:` row (PRD v1.106 Part 5 F). Model calls are the run's own spend records
|
|
473
|
+
* (`worker-round`, `judge-round`; the attempt-level `worker-result` echoes are not rounds and drafting is not the run's), tool and denied come
|
|
474
|
+
* from the gate-audit rows scoped to this run (the same figure `tools:` shows). No tool log saved says so, never a zero.
|
|
475
|
+
* @param {any[]} records the run's raw spine @param {{totalCalls: number, denied: number}|null} behaviour
|
|
476
|
+
* @returns {string}
|
|
477
|
+
*/
|
|
478
|
+
export function callsLineOf(records: any[], behaviour: {
|
|
479
|
+
totalCalls: number;
|
|
480
|
+
denied: number;
|
|
481
|
+
} | null): string;
|
|
482
|
+
/**
|
|
483
|
+
* "16 run · 8 passed · 8 failed (write-flight-facts tries 1-5, then tries 1-3)" — counts of what ran, never a fraction (the "checks N/M"
|
|
484
|
+
* headline stays withdrawn). Every `exit-eval` result plus every graded `outer-close` stage; the parenthesis names the steps and try ranges
|
|
485
|
+
* that failed, from the same `tries[]` the Audit reads. A run with neither says `not recorded`.
|
|
486
|
+
* @param {any[]} records the run's raw spine @param {any[]|null|undefined} parts the enriched parts
|
|
487
|
+
* @returns {string}
|
|
488
|
+
*/
|
|
489
|
+
export function checksLineOf(records: any[], parts: any[] | null | undefined): string;
|
|
490
|
+
/**
|
|
491
|
+
* The step card's extra line (PRD v1.106 Part 5 G): "5 tries · last try: 1 check passed, 1 failed", with the cut-off try named when there is one
|
|
492
|
+
* ("4 tries (the 4th cut off before its checks ran) · last try: no checks ran"). Words for what passed and failed, never a fraction. `null`
|
|
493
|
+
* when the step has no graded try (an old spine).
|
|
494
|
+
* @param {Array<{n: number, cutOff: boolean, passed: number, failed: number, ran: number}>} tries
|
|
495
|
+
* @returns {string|null}
|
|
496
|
+
*/
|
|
497
|
+
export function triesLineOf(tries: Array<{
|
|
498
|
+
n: number;
|
|
499
|
+
cutOff: boolean;
|
|
500
|
+
passed: number;
|
|
501
|
+
failed: number;
|
|
502
|
+
ran: number;
|
|
503
|
+
}>): string | null;
|
|
504
|
+
/**
|
|
505
|
+
* "tools: read 16 · grep 22 · write 5 · denied 0" — the step's tool calls by kind, then how many the gate denied (PRD v1.106 Part 5 C). The
|
|
506
|
+
* `shell_`/`ctx_` prefixes are stripped like the rest of the page. `null` when no tool log was saved (nothing honest to count).
|
|
507
|
+
* @param {Record<string, number>|null|undefined} byTool @param {number|null|undefined} blocked
|
|
508
|
+
* @returns {string|null}
|
|
509
|
+
*/
|
|
510
|
+
export function toolsTallyLine(byTool: Record<string, number> | null | undefined, blocked: number | null | undefined): string | null;
|
|
511
|
+
/**
|
|
512
|
+
* The try line's gap: the first failing reason, and how many more there are (PRD v1.106 Part 5 C). ONE function, beside {@link stageQuestionText}.
|
|
513
|
+
* @param {string|null|undefined} detail
|
|
514
|
+
* @returns {{ line: string, more: number }|null}
|
|
515
|
+
*/
|
|
516
|
+
export function gapOneLine(detail: string | null | undefined): {
|
|
517
|
+
line: string;
|
|
518
|
+
more: number;
|
|
519
|
+
} | null;
|
|
520
|
+
/**
|
|
521
|
+
* "from line 1 ~ under $450; not a red-eye" — the job line(s) a check serves and the `~` rules written under them (the signed `jobLines`).
|
|
522
|
+
* `null` when the stage serves no job line (a mandatory guard).
|
|
523
|
+
* @param {number[]} fromLine @param {any} spec
|
|
524
|
+
* @returns {string|null}
|
|
525
|
+
*/
|
|
526
|
+
export function fromLineText(fromLine: number[], spec: any): string | null;
|
|
527
|
+
/**
|
|
528
|
+
* "output judged: output/steps/1.json · 2.1 KB · sha256 674b…30" — the file a try's check read (PRD v1.106 Part 5 choice 14). ONE place owns
|
|
529
|
+
* these words. `size` is bytes under 1 KB, else KB with one decimal; the hash shows its first 4 and last 2 characters.
|
|
530
|
+
* @param {{path: string|null, bytes: number|null, sha256: string|null}} o
|
|
531
|
+
* @returns {string}
|
|
532
|
+
*/
|
|
533
|
+
export function judgedFileLine(o: {
|
|
534
|
+
path: string | null;
|
|
535
|
+
bytes: number | null;
|
|
536
|
+
sha256: string | null;
|
|
537
|
+
}): string;
|
|
301
538
|
/**
|
|
302
539
|
* 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
540
|
* in local time (hamr, P7). `null` when either part is not on record — never a made-up stamp.
|
|
@@ -343,11 +580,12 @@ export function inputsTextFromSpec(spec: any, source: string): string;
|
|
|
343
580
|
* from the resolved SPEC only (a) (b) (c) — a job-start record and `none()`
|
|
344
581
|
* carry no close/writeScope/tools at all, so those stay `'not recorded'`.
|
|
345
582
|
* @param {string} runid
|
|
346
|
-
* @param {{ home?: string }} [opts]
|
|
583
|
+
* @param {{ home?: string, schedules?: ReturnType<typeof createScheduleDoor> }} [opts]
|
|
347
584
|
* @returns {any|null}
|
|
348
585
|
*/
|
|
349
586
|
export function getRunJob(runid: string, opts?: {
|
|
350
587
|
home?: string;
|
|
588
|
+
schedules?: ReturnType<typeof createScheduleDoor>;
|
|
351
589
|
}): any | null;
|
|
352
590
|
/**
|
|
353
591
|
* The track record of a WORKFLOW: every LISTED run whose signed spec (the one its latest leg ran under) has this
|
|
@@ -395,14 +633,14 @@ export function reuseLine(record: {
|
|
|
395
633
|
} | null): string;
|
|
396
634
|
/**
|
|
397
635
|
* `start-from` (the route keeps its name) for one listed run: the card prefilled from the run's SIGNED job, which
|
|
398
|
-
*
|
|
636
|
+
* the track record (v1.101: nothing is locked, every box is open). The job boxes carry the signed values, read by the
|
|
399
637
|
* SAME readers the Job tab uses (`successFromSpec`/`guardrailsFromSpec`); the open four — Source, Destination, the
|
|
400
638
|
* $ cap and the Time cap — start at what the run had. `ok:false` when the run's signed job is not on disk (nothing
|
|
401
639
|
* to copy: change the job: Clear the card). `null` when the runid is not listed.
|
|
402
640
|
* @param {string} runid
|
|
403
641
|
* @param {{ home?: string, model?: string, provider?: string }} [opts] `model` = the Model box's current Name (the estimate counts only runs on that worker); absent = the card's own Model
|
|
404
642
|
* @returns {{ok: true, origin: {runid: string, job: string}, outcome: string|null, card: Record<string, any>, from: 'signed job',
|
|
405
|
-
*
|
|
643
|
+
* note: string|null,
|
|
406
644
|
* specHash: string, workflowKey: string, spec: any, specPath: string, specNamesModel: boolean,
|
|
407
645
|
* specWorker: {provider: string, endpoint: string, label: string},
|
|
408
646
|
* trackRecord: ReturnType<typeof trackRecordFor>, line: string}|{ok: false, error: string}|null}
|
|
@@ -420,8 +658,6 @@ export function getStartFrom(runid: string, opts?: {
|
|
|
420
658
|
outcome: string | null;
|
|
421
659
|
card: Record<string, any>;
|
|
422
660
|
from: "signed job";
|
|
423
|
-
locked: readonly string[];
|
|
424
|
-
open: readonly string[];
|
|
425
661
|
note: string | null;
|
|
426
662
|
specHash: string;
|
|
427
663
|
workflowKey: string;
|
|
@@ -439,6 +675,21 @@ export function getStartFrom(runid: string, opts?: {
|
|
|
439
675
|
ok: false;
|
|
440
676
|
error: string;
|
|
441
677
|
} | null;
|
|
678
|
+
/**
|
|
679
|
+
* T Part 3: a job's listed runs, newest first, each with its signed spec (null when not on disk), its terminal outcome and
|
|
680
|
+
* its parsed spine. The schedule door's gate (`greenFor`) reads this; nothing here builds a run context.
|
|
681
|
+
* @param {string} job
|
|
682
|
+
* @param {{ home?: string }} [opts]
|
|
683
|
+
* @returns {{runid: string, outcome: string|null, spec: any, records: any[]}[]}
|
|
684
|
+
*/
|
|
685
|
+
export function jobRunsOf(job: string, opts?: {
|
|
686
|
+
home?: string;
|
|
687
|
+
}): {
|
|
688
|
+
runid: string;
|
|
689
|
+
outcome: string | null;
|
|
690
|
+
spec: any;
|
|
691
|
+
records: any[];
|
|
692
|
+
}[];
|
|
442
693
|
/**
|
|
443
694
|
* The Reuse workflow picker's list: one entry per JOB (same-job identity = `workflowKey`, the one `getStartFrom` uses)
|
|
444
695
|
* that has at least one GREEN local run, newest green first. `runid` = that job's newest green run, so the page hands
|
|
@@ -469,7 +720,7 @@ export function listReuseJobs(opts?: {
|
|
|
469
720
|
* @param {string} id
|
|
470
721
|
* @param {{ home?: string, model?: string, provider?: string }} [opts] `model` as for {@link getStartFrom}
|
|
471
722
|
* @returns {{ok: true, origin: {runid: null, job: string, importId: string}, card: Record<string, any>, from: 'imported job',
|
|
472
|
-
*
|
|
723
|
+
* note: string|null, specHash: string, workflowKey: string, spec: any,
|
|
473
724
|
* specPath: null, trackRecord: ReturnType<typeof trackRecordFor>, line: string}|{ok: false, error: string}|null}
|
|
474
725
|
*/
|
|
475
726
|
export function getStartFromImport(id: string, opts?: {
|
|
@@ -485,8 +736,6 @@ export function getStartFromImport(id: string, opts?: {
|
|
|
485
736
|
};
|
|
486
737
|
card: Record<string, any>;
|
|
487
738
|
from: "imported job";
|
|
488
|
-
locked: readonly string[];
|
|
489
|
-
open: readonly string[];
|
|
490
739
|
note: string | null;
|
|
491
740
|
specHash: string;
|
|
492
741
|
workflowKey: string;
|
|
@@ -514,10 +763,11 @@ export function getStartFromImport(id: string, opts?: {
|
|
|
514
763
|
* only `createPanelServer` below always supplies one.
|
|
515
764
|
* @param {import('node:http').IncomingMessage} req
|
|
516
765
|
* @param {import('node:http').ServerResponse} res
|
|
517
|
-
* @param {{ home?: string, packageJsonPath?: string, port: number, token?: string, authorRoutes?: ReturnType<typeof createAuthorRoutes>, settingsRoutes?: ReturnType<typeof createSettingsRoutes>, runRoutes?: ReturnType<typeof createRunRoutes>, importRoutes?: ReturnType<typeof createImportRoutes> }} opts
|
|
766
|
+
* @param {{ home?: string, sessionsRoot?: string, packageJsonPath?: string, port: number, token?: string, authorRoutes?: ReturnType<typeof createAuthorRoutes>, settingsRoutes?: ReturnType<typeof createSettingsRoutes>, runRoutes?: ReturnType<typeof createRunRoutes>, importRoutes?: ReturnType<typeof createImportRoutes>, schedules?: ReturnType<typeof createScheduleDoor> }} opts
|
|
518
767
|
*/
|
|
519
768
|
export function handleRequest(req: import("node:http").IncomingMessage, res: import("node:http").ServerResponse, opts: {
|
|
520
769
|
home?: string;
|
|
770
|
+
sessionsRoot?: string;
|
|
521
771
|
packageJsonPath?: string;
|
|
522
772
|
port: number;
|
|
523
773
|
token?: string;
|
|
@@ -525,6 +775,7 @@ export function handleRequest(req: import("node:http").IncomingMessage, res: imp
|
|
|
525
775
|
settingsRoutes?: ReturnType<typeof createSettingsRoutes>;
|
|
526
776
|
runRoutes?: ReturnType<typeof createRunRoutes>;
|
|
527
777
|
importRoutes?: ReturnType<typeof createImportRoutes>;
|
|
778
|
+
schedules?: ReturnType<typeof createScheduleDoor>;
|
|
528
779
|
}): void;
|
|
529
780
|
/**
|
|
530
781
|
* Start the panel server. Binds `127.0.0.1` ONLY. A taken port is a LOUD,
|
|
@@ -545,8 +796,9 @@ export function handleRequest(req: import("node:http").IncomingMessage, res: imp
|
|
|
545
796
|
* @param {{ port?: number, home?: string, env?: Record<string,string|undefined>,
|
|
546
797
|
* sessionsRoot?: string, spawnFn?: (...a: any[]) => any, bareloopBin?: string,
|
|
547
798
|
* fetchImpl?: typeof fetch, settleMs?: number, userHome?: string,
|
|
548
|
-
* openFolderSpawn?: (cmd: string, args: string[], o: object) => any, platform?: string, packageJsonPath?: string
|
|
549
|
-
*
|
|
799
|
+
* openFolderSpawn?: (cmd: string, args: string[], o: object) => any, platform?: string, packageJsonPath?: string,
|
|
800
|
+
* scheduleNow?: () => number, sessionDeps?: Record<string, any>, schedulerIntervalMs?: number, acceptWaitMs?: number }} [opts] `scheduleNow`, `sessionDeps` and `schedulerIntervalMs` are TEST SEAMS (the schedule clock; createSession's stubs; the scheduler's timer, 0 = none)
|
|
801
|
+
* @returns {Promise<{ server: import('node:http').Server, port: number, token: string, schedules: ReturnType<typeof createScheduleDoor>, scheduler: ReturnType<typeof createScheduler>, close: () => Promise<void> }>}
|
|
550
802
|
*/
|
|
551
803
|
export function createPanelServer(opts?: {
|
|
552
804
|
port?: number;
|
|
@@ -561,10 +813,16 @@ export function createPanelServer(opts?: {
|
|
|
561
813
|
openFolderSpawn?: (cmd: string, args: string[], o: object) => any;
|
|
562
814
|
platform?: string;
|
|
563
815
|
packageJsonPath?: string;
|
|
816
|
+
scheduleNow?: () => number;
|
|
817
|
+
sessionDeps?: Record<string, any>;
|
|
818
|
+
schedulerIntervalMs?: number;
|
|
819
|
+
acceptWaitMs?: number;
|
|
564
820
|
}): Promise<{
|
|
565
821
|
server: import("node:http").Server;
|
|
566
822
|
port: number;
|
|
567
823
|
token: string;
|
|
824
|
+
schedules: ReturnType<typeof createScheduleDoor>;
|
|
825
|
+
scheduler: ReturnType<typeof createScheduler>;
|
|
568
826
|
close: () => Promise<void>;
|
|
569
827
|
}>;
|
|
570
828
|
/**
|
|
@@ -604,13 +862,37 @@ export const DEFAULT_PORT: 4700;
|
|
|
604
862
|
*/
|
|
605
863
|
export const SOFTGREEN_ADMITTED_ISO: "2026-08-18T00:00:00.000Z";
|
|
606
864
|
export { DIED_MTIME_MS };
|
|
865
|
+
/** The named limits of the runs list, in ONE place (P9 v2 D.13): quiet after 30 days, 7 older runs shown, a Resume tag ages out after 7 days. Sent in `GET /api/runs`; the page reads them from there. */
|
|
866
|
+
export const RUNS_LIST_LIMITS: Readonly<{
|
|
867
|
+
quietDays: 30;
|
|
868
|
+
olderRuns: 7;
|
|
869
|
+
needsYouDays: 7;
|
|
870
|
+
}>;
|
|
871
|
+
/** what the Result tab's open door asks, after the door's own head words */
|
|
872
|
+
export const RESULT_DOOR_ASK: "the checks passed; is this what you wanted?";
|
|
873
|
+
/** the two sentences for a delivered file that is no longer what the run wrote (code-owned, one spelling) */
|
|
874
|
+
export const RESULT_FILE_GONE: "the saved file is gone";
|
|
875
|
+
export const RESULT_FILE_CHANGED: "the saved file changed after the run";
|
|
876
|
+
/** the same two states for a red run's file, which is the last one the check read (not a delivered file) */
|
|
877
|
+
export const RESULT_JUDGED_GONE: "the file is gone";
|
|
878
|
+
export const RESULT_JUDGED_CHANGED: "the file changed after the check";
|
|
879
|
+
/** what a red run's rows are, said once above them */
|
|
880
|
+
export const RESULT_JUDGED_NOTE: "a red run delivers nothing; these rows are the last file the check read";
|
|
881
|
+
/** the words beside a Result-tab page link: the page is read again from the web, so it can differ from what the run read */
|
|
882
|
+
export const RESULT_PAGE_MAY_CHANGE: "The page may have changed since this run.";
|
|
883
|
+
/** the words after a fact whose limit the person agreed to in a Part 5b look ask */
|
|
884
|
+
export const RESULT_YOU_CONFIRMED: "(you confirmed)";
|
|
607
885
|
/** The default/max page size for {@link getRunRounds} — tighten-only,
|
|
608
886
|
* named so a future change is a deliberate, visible edit (item 5,
|
|
609
887
|
* 2026-09-25). */
|
|
610
888
|
export const ROUNDS_PAGE_DEFAULT: 50;
|
|
611
889
|
export const ROUNDS_PAGE_MAX: 200;
|
|
890
|
+
/** the Audit step table's page size (PRD v1.106 Part 5 choice 3): rows, not rounds */
|
|
891
|
+
export const STEP_ROWS_PAGE: 20;
|
|
892
|
+
import { createScheduleDoor } from './schedules.js';
|
|
612
893
|
import { createAuthorRoutes } from './authorroutes.js';
|
|
613
894
|
import { createSettingsRoutes } from './settingsroutes.js';
|
|
614
895
|
import { createRunRoutes } from './runroutes.js';
|
|
615
896
|
import { createImportRoutes } from './importroutes.js';
|
|
897
|
+
import { createScheduler } from './scheduler.js';
|
|
616
898
|
import { DIED_MTIME_MS } from '../runlist.js';
|
package/types/panel/status.d.ts
CHANGED
|
@@ -34,6 +34,26 @@ export function partStatusFor(part: {
|
|
|
34
34
|
sign: string;
|
|
35
35
|
word: string;
|
|
36
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* The words one door wears: header phrase, the `last answer:` line (with the time `HH:MM` once decided) and the table rows.
|
|
39
|
+
* @param {{at: string|null, paths: string[], decision: string|null, text: string|null, decidedAt: string|null}} door
|
|
40
|
+
* @returns {{ head: string, last: string, rows: Array<{path: string|null, text: string, time: string|null}> }}
|
|
41
|
+
*/
|
|
42
|
+
export function doorViewOf(door: {
|
|
43
|
+
at: string | null;
|
|
44
|
+
paths: string[];
|
|
45
|
+
decision: string | null;
|
|
46
|
+
text: string | null;
|
|
47
|
+
decidedAt: string | null;
|
|
48
|
+
}): {
|
|
49
|
+
head: string;
|
|
50
|
+
last: string;
|
|
51
|
+
rows: Array<{
|
|
52
|
+
path: string | null;
|
|
53
|
+
text: string;
|
|
54
|
+
time: string | null;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
37
57
|
/** the sentence a passed run carries as its reason (the Ended block's `line`) */
|
|
38
58
|
export const GOAL_MET_LINE: "goal met";
|
|
39
59
|
/** @typedef {'running'|'waiting'|'passed'|'failed'|'capped'|'stopped'|'died'} StatusKey */
|
|
@@ -68,4 +88,35 @@ export const STATUS: Readonly<{
|
|
|
68
88
|
word: "died";
|
|
69
89
|
}>;
|
|
70
90
|
}>;
|
|
91
|
+
/**
|
|
92
|
+
* PRD v1.106 Part 5 (C, choice 9): the review door's words, ONE table. The door is the only human step bareloop has; there is no "you
|
|
93
|
+
* cancelled" (cancel is deleted). `decision` is the spine's `door-decision.decision`, or null while nothing has been decided.
|
|
94
|
+
*/
|
|
95
|
+
export const DOOR_WORDS: Readonly<{
|
|
96
|
+
waiting: Readonly<{
|
|
97
|
+
head: "waiting for you";
|
|
98
|
+
row: "paused for you";
|
|
99
|
+
last: "none yet";
|
|
100
|
+
}>;
|
|
101
|
+
accept: Readonly<{
|
|
102
|
+
head: "you accepted";
|
|
103
|
+
row: "you accepted";
|
|
104
|
+
last: "you accepted";
|
|
105
|
+
}>;
|
|
106
|
+
rerun: Readonly<{
|
|
107
|
+
head: "you said rerun";
|
|
108
|
+
row: "you said rerun";
|
|
109
|
+
last: "you said rerun";
|
|
110
|
+
}>;
|
|
111
|
+
pause: Readonly<{
|
|
112
|
+
head: "paused";
|
|
113
|
+
row: "paused";
|
|
114
|
+
last: "paused";
|
|
115
|
+
}>;
|
|
116
|
+
}>;
|
|
117
|
+
/** the marker a part wears for who did its work (PRD v1.106 Part 5 C/G): the model, or a person asked */
|
|
118
|
+
export const ACTOR_MARKER: Readonly<{
|
|
119
|
+
model: "[model]";
|
|
120
|
+
human: "[human · ask]";
|
|
121
|
+
}>;
|
|
71
122
|
export type StatusKey = "running" | "waiting" | "passed" | "failed" | "capped" | "stopped" | "died";
|
package/types/planrun.d.ts
CHANGED
|
@@ -523,6 +523,8 @@ export const FORBIDDEN_WRITE_SEGMENT_PATTERN: RegExp;
|
|
|
523
523
|
export const BOUND_REASON_MAX: 200;
|
|
524
524
|
/** the recorded reason for a step attempt whose round was cut at the output cap (F122 2A b) */
|
|
525
525
|
export const TRUNCATED_REASON: "output cut at the token limit (max_tokens)";
|
|
526
|
+
/** the plain sentence for a replan answer cut off at its length limit (hamr 2026-10-16, run mv9gm796) */
|
|
527
|
+
export const REPLAN_CUT_WORDS: "The replan answer was cut off at its length limit, so no new plan came back.";
|
|
526
528
|
export type CategorizedError = Error & {
|
|
527
529
|
category?: string;
|
|
528
530
|
lib?: string;
|
package/types/providerrows.d.ts
CHANGED
|
@@ -251,8 +251,8 @@ export function judgeRowFor(rows: readonly KeyRow[], judge: {
|
|
|
251
251
|
* the worker's own provider it calls the worker's endpoint, so a row on that endpoint with a line
|
|
252
252
|
* NAMED for the judge's model prices it ({@link ratesFor}); a judge model that is absent or names
|
|
253
253
|
* no line there keeps the worker's price (the worker's row, or none = the guess). A different
|
|
254
|
-
* provider resolves with no endpoint. The judge's KEY is chosen elsewhere (`
|
|
255
|
-
*
|
|
254
|
+
* provider resolves with no endpoint. The judge's KEY is chosen elsewhere (`judgeRowFor`:
|
|
255
|
+
* the worker's own row when the judge shares its provider), so the two can come from different rows.
|
|
256
256
|
* @param {{ provider: string, model?: string|null }} judge
|
|
257
257
|
* @param {string|null|undefined} workerProvider
|
|
258
258
|
* @param {string|null|undefined} workerBaseUrl
|
package/types/replay.d.ts
CHANGED
|
@@ -180,7 +180,26 @@ export function replayRun(spineEvents: any[], auditEvents?: any[], { runId, audi
|
|
|
180
180
|
startTs: number | null;
|
|
181
181
|
endTs: number | null;
|
|
182
182
|
exitEvalDetail: string | null;
|
|
183
|
+
checks: Array<{
|
|
184
|
+
type: string;
|
|
185
|
+
pass: boolean;
|
|
186
|
+
detail: string | null;
|
|
187
|
+
name: string | null;
|
|
188
|
+
}>;
|
|
189
|
+
outputJudged: Array<{
|
|
190
|
+
path: string | null;
|
|
191
|
+
bytes: number | null;
|
|
192
|
+
sha256: string | null;
|
|
193
|
+
}>;
|
|
183
194
|
}[];
|
|
195
|
+
cutOffTry: {
|
|
196
|
+
n: number;
|
|
197
|
+
iteration: any;
|
|
198
|
+
startSeq: any;
|
|
199
|
+
endSeq: any;
|
|
200
|
+
startTs: number | null;
|
|
201
|
+
endTs: number | null;
|
|
202
|
+
} | null;
|
|
184
203
|
stepStartSeq: any;
|
|
185
204
|
stepEndSeq: any;
|
|
186
205
|
stepStartTs: number | null;
|
|
@@ -291,9 +310,10 @@ export function replayRun(spineEvents: any[], auditEvents?: any[], { runId, audi
|
|
|
291
310
|
* @param {boolean|null} [draftSpendComplete] whether the drafting fold above was EXACT;
|
|
292
311
|
* `null`/`undefined`/`true` all read as exact (never a new enum value elsewhere) —
|
|
293
312
|
* only an explicit `false` triggers the floor wording.
|
|
313
|
+
* @param {number|null} [runUsd] the run's own spend as a number (excluding drafting), so the sum can be written
|
|
294
314
|
* @returns {string}
|
|
295
315
|
*/
|
|
296
|
-
export function moneyWithDraft(spendCore: string, draftSpentUsd: number | null, budgetUsd?: number | null, draftSpendComplete?: boolean | null): string;
|
|
316
|
+
export function moneyWithDraft(spendCore: string, draftSpentUsd: number | null, budgetUsd?: number | null, draftSpendComplete?: boolean | null, runUsd?: number | null): string;
|
|
297
317
|
/**
|
|
298
318
|
* Render a {@link replayRun} summary as one printable page: a header block
|
|
299
319
|
* (goal/shape/signed/branch/outcome/spent), a timeline (steps or iterations,
|
package/types/reviewdoor.d.ts
CHANGED
|
@@ -4,6 +4,33 @@
|
|
|
4
4
|
* @param {any[]} events the run's spine, parsed
|
|
5
5
|
* @returns {any|null} */
|
|
6
6
|
export function doorRecordOf(events: any[]): any | null;
|
|
7
|
+
/**
|
|
8
|
+
* Was this run's door ACCEPTED, honoured? A `door-decision` record with `decision: 'accept'` and `honoured: true`
|
|
9
|
+
* (written by {@link answerReviewDoor}, whichever surface asked: the CLI, or the panel's Accept). Pure, off the
|
|
10
|
+
* run's spine. A refused accept (`honoured: false`) does not count.
|
|
11
|
+
* @param {any[]} events the run's spine, parsed
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
*/
|
|
14
|
+
export function doorAccepted(events: any[]): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* T D.2 — a SCHEDULED run's green is held until the person accepts it (T10): a run that carries a `trigger.json` (a scheduled
|
|
17
|
+
* start wrote it) and holds no honoured accept. One shared predicate: `getStartFrom` reads its outcome as `'green-held'` (so
|
|
18
|
+
* the Reuse start refuses it unchanged), `listReuseJobs` skips it, and the schedule gate does not count it. A hand run is never held.
|
|
19
|
+
* @param {any[]} events the run's spine, parsed @param {any} triggerRec the run's `trigger.json`, or null
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
export function heldScheduled(events: any[], triggerRec: any): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Is this run WAITING for the person's Accept? Its spine holds a door record, the door has not expired ({@link doorAgeGate}),
|
|
25
|
+
* and no honoured `accept` or `rerun` decision follows it (a `pause` keeps the same three doors open). One owner: the runs
|
|
26
|
+
* list's Needs-you `Accept` tag, the Run tab's Accept button and the scheduled NEXT line all read this.
|
|
27
|
+
* @param {any[]} events the run's spine, parsed
|
|
28
|
+
* @param {{now?: () => number}} [opts]
|
|
29
|
+
* @returns {boolean}
|
|
30
|
+
*/
|
|
31
|
+
export function doorWaiting(events: any[], opts?: {
|
|
32
|
+
now?: () => number;
|
|
33
|
+
}): boolean;
|
|
7
34
|
/**
|
|
8
35
|
* IS THE DOOR STILL OPEN? The same 60-day TTL a hitl checkpoint keeps
|
|
9
36
|
* (`PAUSE_TTL_MS`) and deliberately the same NUMBER — the door replaced `cancel`
|