bareloop 0.43.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/bareloop.context.md +52 -13
  3. package/package.json +2 -2
  4. package/src/authorflow.js +26 -11
  5. package/src/authoring.js +1 -1
  6. package/src/authorjob.js +27 -15
  7. package/src/authorreadout.js +7 -6
  8. package/src/declaredclose.js +4 -1
  9. package/src/draftspend.js +20 -2
  10. package/src/facts.js +54 -9
  11. package/src/factwords.js +58 -6
  12. package/src/inputlook.js +38 -3
  13. package/src/job.js +1 -1
  14. package/src/kinds.js +5 -0
  15. package/src/pagedropwords.js +64 -0
  16. package/src/pagemap.js +264 -0
  17. package/src/pagerules.js +319 -0
  18. package/src/panel/authorroutes.js +34 -30
  19. package/src/panel/authorsession.js +29 -5
  20. package/src/panel/index.html +465 -65
  21. package/src/panel/jobplan.js +4 -2
  22. package/src/panel/runroutes.js +46 -22
  23. package/src/panel/server.js +791 -28
  24. package/src/panel/status.js +33 -0
  25. package/src/panelmoney.js +29 -0
  26. package/src/planrun.js +53 -4
  27. package/src/promptregisters.js +4 -0
  28. package/src/replay.js +118 -7
  29. package/src/rubricstep.js +123 -11
  30. package/src/runlist.js +2 -1
  31. package/src/schedulewords.js +6 -3
  32. package/src/source.js +18 -0
  33. package/src/thinking.js +63 -2
  34. package/src/userrun.js +28 -14
  35. package/types/authorflow.d.ts +42 -6
  36. package/types/authorjob.d.ts +10 -2
  37. package/types/declaredclose.d.ts +2 -0
  38. package/types/draftspend.d.ts +27 -1
  39. package/types/facts.d.ts +15 -0
  40. package/types/factwords.d.ts +26 -3
  41. package/types/inputlook.d.ts +15 -0
  42. package/types/kinds.d.ts +5 -0
  43. package/types/pagedropwords.d.ts +26 -0
  44. package/types/pagemap.d.ts +154 -0
  45. package/types/pagerules.d.ts +86 -0
  46. package/types/panel/authorroutes.d.ts +17 -14
  47. package/types/panel/runroutes.d.ts +2 -2
  48. package/types/panel/server.d.ts +155 -2
  49. package/types/panel/status.d.ts +51 -0
  50. package/types/panelmoney.d.ts +5 -0
  51. package/types/replay.d.ts +21 -1
  52. package/types/rubricstep.d.ts +57 -1
  53. package/types/runlist.d.ts +4 -1
  54. package/types/schedulewords.d.ts +4 -2
  55. package/types/source.d.ts +11 -0
  56. package/types/thinking.d.ts +39 -0
  57. package/types/userrun.d.ts +4 -0
@@ -3,6 +3,8 @@
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" / "can't change it" / "not confirmed")
6
+ * - `human` {no, source: 'confirm'|'try-3'|'look', question, answer, line?, at}: one question the person was asked and what they answered (the answer is secret-masked
7
+ * before it is written; the row is written only when BOTH exist, so a question nobody answered leaves no row)
6
8
  * - `call` {no, step, label, model, thinking, thinkingSupport, thinkingStatus?, costUsd (null stays null), unpricedRounds, at}: one METERED model call
7
9
  * (`thinking` = the tick's mode: off | on; `thinkingSupport` = bare-agent's lookup for the model: true | false | 'unverified';
8
10
  * `thinkingStatus` = bare-agent's own verdict for the call's run: applied | unsupported | ignored | unknown, present only when thinkingMode was sent)
@@ -58,12 +60,33 @@ export function sessionDirForRow(row: {
58
60
  home?: string;
59
61
  sessionsRoot?: string;
60
62
  }): string | null;
63
+ /**
64
+ * PRD v1.106 Part 5 Q1: one answered question as the two lines the Audit's drafting table shows under its step. ONE place owns the words.
65
+ * @param {{question: string, answer: string, source?: string, line?: number, at?: string}} h
66
+ * @returns {{question: string, answer: string, source: string, line?: number, at?: string, askedText: string, saidText: string}}
67
+ */
68
+ export function humanRowWords(h: {
69
+ question: string;
70
+ answer: string;
71
+ source?: string;
72
+ line?: number;
73
+ at?: string;
74
+ }): {
75
+ question: string;
76
+ answer: string;
77
+ source: string;
78
+ line?: number;
79
+ at?: string;
80
+ askedText: string;
81
+ saidText: string;
82
+ };
61
83
  /**
62
84
  * Fold `<dir>/draft-log.jsonl` into one record per drafting step, each with its calls. A line that cannot be parsed is
63
85
  * skipped (a torn last line must not hide the rest).
64
86
  * @param {string} dir
65
87
  * @returns {{no: number, id: string, label: string, status: 'running'|'done'|'failed', startedAt: string, endedAt: string|null, wallMs: number|null,
66
- * calls: {label: string, model: string|null, costUsd: number|null, unpricedRounds: number, at: string}[]}[]}
88
+ * calls: {label: string, model: string|null, costUsd: number|null, unpricedRounds: number, at: string}[],
89
+ * humans: ReturnType<typeof humanRowWords>[]}[]}
67
90
  */
68
91
  export function readDraftSteps(dir: string): {
69
92
  no: number;
@@ -80,6 +103,7 @@ export function readDraftSteps(dir: string): {
80
103
  unpricedRounds: number;
81
104
  at: string;
82
105
  }[];
106
+ humans: ReturnType<typeof humanRowWords>[];
83
107
  }[];
84
108
  /**
85
109
  * The DRAFTING PART of a run (hamr 2026-10-06, option A): the first part of the run's views, built from the session's
@@ -109,6 +133,8 @@ export const DRAFT_LOG_FILE: "draft-log.jsonl";
109
133
  * - `step-start` {no, id, label, at}
110
134
  * - `step-end` {no, status: 'done'|'failed', label, at} (a re-opened step writes `step-reopen` {no, at} first)
111
135
  * - `step-label` {no, label, at} (the step reads `label` from now on, status and times untouched: the thinking row's "the model thought anyway" / "can't change it" / "not confirmed")
136
+ * - `human` {no, source: 'confirm'|'try-3'|'look', question, answer, line?, at}: one question the person was asked and what they answered (the answer is secret-masked
137
+ * before it is written; the row is written only when BOTH exist, so a question nobody answered leaves no row)
112
138
  * - `call` {no, step, label, model, thinking, thinkingSupport, thinkingStatus?, costUsd (null stays null), unpricedRounds, at}: one METERED model call
113
139
  * (`thinking` = the tick's mode: off | on; `thinkingSupport` = bare-agent's lookup for the model: true | false | 'unverified';
114
140
  * `thinkingStatus` = bare-agent's own verdict for the call's run: applied | unsupported | ignored | unknown, present only when thinkingMode was sent)
package/types/facts.d.ts CHANGED
@@ -23,6 +23,14 @@ export function factClaims(part: any, results: unknown): {
23
23
  quote: string;
24
24
  values?: Record<string, string>;
25
25
  }[];
26
+ /**
27
+ * One amount of a money fact in the fact's OWN signed forms, never a hardcoded sign: `symbolFirst` puts the currency sign before the amount
28
+ * ("$144") when the fact has one, else the unit after it ("144 US dollars"); not `symbolFirst` (a rule's bound) prefers the unit.
29
+ * @param {any} f a money fact @param {string|number} n @param {boolean} symbolFirst @returns {string}
30
+ */
31
+ export function moneyAs(f: any, n: string | number, symbolFirst: boolean): string;
32
+ /** @param {any} f a fact with a rule @returns {string} the rule in words, e.g. "at most 450 US dollars" */
33
+ export function ruleWords(f: any): string;
26
34
  /**
27
35
  * One fact as a plain row, written by code from the signed fields:
28
36
  * `fact · price — money, at most 450 US dollars, after "Price", quoted from "Flights"`.
@@ -69,3 +77,10 @@ export const FACT_OPS: readonly string[];
69
77
  export const MAX_FACTS: 10;
70
78
  /** every field a part of class `fact` may carry (the shared ones are checked by rubricStepShapeReds) */
71
79
  export const FACT_PART_FIELDS: readonly string[];
80
+ /** the limits bareguard 0.24 puts on a money fact's lists (ruling #65), enforced here first so the red is ours and plain */
81
+ export const MAX_CURRENCIES: 3;
82
+ export const MAX_UNITS: 10;
83
+ export const MAX_UNIT_CHARS: 100;
84
+ export function asList(v: unknown): string[];
85
+ export function moneyForms(f: any): string[];
86
+ export function boundOk(shape: string, v: unknown): boolean;
@@ -9,16 +9,35 @@ export function countExact(source: string, words: string): number;
9
9
  * @param {string} source @param {string} words @returns {number}
10
10
  */
11
11
  export function countPhrase(source: string, words: string): number;
12
+ /**
13
+ * What the page-word map (src/pagemap.js) KEPT and DROPPED for one input: the sets the second proof reads. A look from the old search-and-read
14
+ * path has no `dropped` list, so it gives null and the label is proved against the code-made list as before.
15
+ * @param {{facts?: any[], dropped?: (import('./pagerules.js').Dropped & {source?: string})[]}|null|undefined} look @param {string} source
16
+ * @returns {{labels: Set<string>, units: Set<string>, currencies: Set<string>, words: Set<string>, dropped: Map<string, import('./pagerules.js').Dropped>}|null}
17
+ */
18
+ export function keptSetOf(look: {
19
+ facts?: any[];
20
+ dropped?: (import("./pagerules.js").Dropped & {
21
+ source?: string;
22
+ })[];
23
+ } | null | undefined, source: string): {
24
+ labels: Set<string>;
25
+ units: Set<string>;
26
+ currencies: Set<string>;
27
+ words: Set<string>;
28
+ dropped: Map<string, import("./pagerules.js").Dropped>;
29
+ } | null;
12
30
  /**
13
31
  * The reds for ONE fact part against its frozen source text.
14
- * @param {{part: any, text: string, line: number|string, scan?: import('./labelscan.js').LabelScan}} o
32
+ * @param {{part: any, text: string, line: number|string, scan?: import('./labelscan.js').LabelScan, kept?: ReturnType<typeof keptSetOf>}} o
15
33
  * @returns {{code: string, detail: string}[]}
16
34
  */
17
- export function checkFactWords({ part, text, line, scan }: {
35
+ export function checkFactWords({ part, text, line, scan, kept }: {
18
36
  part: any;
19
37
  text: string;
20
38
  line: number | string;
21
39
  scan?: import("./labelscan.js").LabelScan;
40
+ kept?: ReturnType<typeof keptSetOf>;
22
41
  }): {
23
42
  code: string;
24
43
  detail: string;
@@ -27,9 +46,13 @@ export function checkFactWords({ part, text, line, scan }: {
27
46
  * The reds for every fact part of a declaration. A fact part whose input text is not at hand is a red: a check whose words were never
28
47
  * proved against the source is never signed.
29
48
  * @param {any} declaration @param {Record<string, string>|null} texts label -> frozen text
49
+ * @param {{facts?: any[], dropped?: any[]}|null} [look] the page-word map's report: what it kept and dropped (null = no map, the code-made list is the proof)
30
50
  * @returns {{code: string, path: string, detail: string}[]}
31
51
  */
32
- export function checkDeclarationWords(declaration: any, texts: Record<string, string> | null): {
52
+ export function checkDeclarationWords(declaration: any, texts: Record<string, string> | null, look?: {
53
+ facts?: any[];
54
+ dropped?: any[];
55
+ } | null): {
33
56
  code: string;
34
57
  path: string;
35
58
  detail: string;
@@ -148,6 +148,12 @@ export function lookPrompt({ jobLines, labels, lists }: {
148
148
  labels: string[];
149
149
  lists: Record<string, string>;
150
150
  }): string;
151
+ /**
152
+ * A fact the page-word map kept (src/pagerules.js `KeptFact`), as the look report carries it: a phrase fact holds its alternative
153
+ * phrases; any other holds its labels (the first is the primary), and a money fact its units and currencies.
154
+ * @param {import('./pagerules.js').KeptFact} k @param {string} source @returns {LookFact}
155
+ */
156
+ export function lookFactOf(k: import("./pagerules.js").KeptFact, source: string): LookFact;
151
157
  /**
152
158
  * Turn the AI's raw report into a VERIFIED one, by code, at $0. A fact is carried only when: its shape is a real shape, its source is
153
159
  * a real input, and its label is ON THE LIST for that input and shape (an id is resolved to its exact words). Anything else is moved
@@ -207,6 +213,9 @@ export const LOOK_REPORT_TOOL: "report_look";
207
213
  /** how many hits one search returns at most, and how long a snippet or a read line is */
208
214
  export const LOOK_MAX_HITS: 40;
209
215
  export { LABEL_SHAPES };
216
+ /**
217
+ * `words` / `labels` / `unit` / `currency`: what the page-word map kept (idea A core)
218
+ */
210
219
  export type LookFact = {
211
220
  line: number;
212
221
  name: string;
@@ -220,6 +229,11 @@ export type LookFact = {
220
229
  rule?: any;
221
230
  aiProposal?: boolean;
222
231
  forRule?: number;
232
+ reading?: import("./pagerules.js").Reading;
233
+ words?: string[];
234
+ labels?: string[];
235
+ unit?: string[];
236
+ currency?: string[];
223
237
  };
224
238
  export type LookNotFound = {
225
239
  line: number;
@@ -234,5 +248,6 @@ export type LookReport = {
234
248
  text?: string;
235
249
  }[];
236
250
  notFound: LookNotFound[];
251
+ dropped?: import("./pagerules.js").Dropped[];
237
252
  };
238
253
  import { LABEL_SHAPES } from './labelscan.js';
package/types/kinds.d.ts CHANGED
@@ -645,6 +645,11 @@ export type Ctx = {
645
645
  timeoutMs?: number;
646
646
  deadlineMs?: number;
647
647
  };
648
+ onRubricEvidence?: (e: {
649
+ step: number;
650
+ evidence: any[];
651
+ evidenceTotal?: number;
652
+ }) => void;
648
653
  onStage?: (row: {
649
654
  stage: string | null;
650
655
  kind: string | null;
@@ -0,0 +1,26 @@
1
+ /** @param {Dropped} d @returns {string} the plain sentence for one drop */
2
+ export function dropSentence(d: Dropped): string;
3
+ /**
4
+ * One row per fact that lost something: the sentences (one per dropped word, then the fact's own), and whether the whole fact went.
5
+ * @param {Dropped[]|undefined|null} dropped
6
+ * @returns {{line: number, rule?: number, fact: string, full: boolean, sentences: string[]}[]}
7
+ */
8
+ export function dropRows(dropped: Dropped[] | undefined | null): {
9
+ line: number;
10
+ rule?: number;
11
+ fact: string;
12
+ full: boolean;
13
+ sentences: string[];
14
+ }[];
15
+ /**
16
+ * A row as one Not-checked line, in the person's own words: `line N: "<the ~ rule as written, or the fact's name>" — <sentences>`.
17
+ * @param {{line: number, rule?: number, fact: string, sentences: string[]}} row @param {any[]|null|undefined} jobLines @param {(l: any) => string[]} rulesOf
18
+ * @returns {string}
19
+ */
20
+ export function dropLine(row: {
21
+ line: number;
22
+ rule?: number;
23
+ fact: string;
24
+ sentences: string[];
25
+ }, jobLines: any[] | null | undefined, rulesOf: (l: any) => string[]): string;
26
+ export type Dropped = import("./pagerules.js").Dropped;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * What the AI is told, for ONE page input. Ported from the POC prompt: the job, the `~` rules, "a result is one item", the answer form
3
+ * and the shape meanings.
4
+ * @param {{jobLines: {n: number, text: string, rule?: string, rules?: string[]}[], label: string, text: string}} o
5
+ * @returns {string}
6
+ */
7
+ export function mapPrompt({ jobLines, label, text }: {
8
+ jobLines: {
9
+ n: number;
10
+ text: string;
11
+ rule?: string;
12
+ rules?: string[];
13
+ }[];
14
+ label: string;
15
+ text: string;
16
+ }): string;
17
+ /**
18
+ * Read the AI's answer: ONE JSON object of the answer form, and nothing else. No repair (hamr's ruling): text that is not JSON, JSON of
19
+ * another form, or a fact whose shape or lists are not of the form is a malformed answer.
20
+ * @param {unknown} text @returns {{ok: true, mapping: import('./pagerules.js').PageMap}|{ok: false}}
21
+ */
22
+ export function parseMapAnswer(text: unknown): {
23
+ ok: true;
24
+ mapping: import("./pagerules.js").PageMap;
25
+ } | {
26
+ ok: false;
27
+ };
28
+ /**
29
+ * Run the map: one call per page input.
30
+ * @param {{texts: Record<string, string>, inputs: {n: number, label: string}[], jobLines: {n: number, text: string, rule?: string, rules?: string[]}[],
31
+ * provider?: any, maxTokens?: number, ceilingUsd?: number|null, callTimeoutMs?: number,
32
+ * onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number, thinkingStatus?: string, raw?: string}) => void,
33
+ * createLoop?: (o: {system: string, onLlmResult: any, provider: any, rates?: {in: number, out: number}}) => any,
34
+ * rates?: {in: number, out: number}|null, callOptions?: Record<string, any>, sleep?: (ms: number) => Promise<void>, prove?: Function}} o
35
+ * @returns {Promise<{state: 'PRESENT'|'ABSENT', facts: any, reason: string|null, cause: string|null, meta: any, raw: string,
36
+ * raws: ReturnType<typeof scrubRaw>[], calls: {label: string, costUsd: number|null, unpricedRounds: number}[],
37
+ * budgetStop: 'cap-halt'|'pricing-red'|null}>}
38
+ */
39
+ export function runPageMap({ texts, inputs, jobLines, provider, maxTokens, ceilingUsd, callTimeoutMs, onCall, createLoop, rates, callOptions, sleep, }: {
40
+ texts: Record<string, string>;
41
+ inputs: {
42
+ n: number;
43
+ label: string;
44
+ }[];
45
+ jobLines: {
46
+ n: number;
47
+ text: string;
48
+ rule?: string;
49
+ rules?: string[];
50
+ }[];
51
+ provider?: any;
52
+ maxTokens?: number;
53
+ ceilingUsd?: number | null;
54
+ callTimeoutMs?: number;
55
+ onCall?: (call: {
56
+ label: string;
57
+ costUsd: number | null;
58
+ unpricedRounds: number;
59
+ thinkingStatus?: string;
60
+ raw?: string;
61
+ }) => void;
62
+ createLoop?: (o: {
63
+ system: string;
64
+ onLlmResult: any;
65
+ provider: any;
66
+ rates?: {
67
+ in: number;
68
+ out: number;
69
+ };
70
+ }) => any;
71
+ rates?: {
72
+ in: number;
73
+ out: number;
74
+ } | null;
75
+ callOptions?: Record<string, any>;
76
+ sleep?: (ms: number) => Promise<void>;
77
+ prove?: Function;
78
+ }): Promise<{
79
+ state: "PRESENT" | "ABSENT";
80
+ facts: any;
81
+ reason: string | null;
82
+ cause: string | null;
83
+ meta: any;
84
+ raw: string;
85
+ raws: ReturnType<typeof scrubRaw>[];
86
+ calls: {
87
+ label: string;
88
+ costUsd: number | null;
89
+ unpricedRounds: number;
90
+ }[];
91
+ budgetStop: "cap-halt" | "pricing-red" | null;
92
+ }>;
93
+ /** @param {unknown} q a recorded question @returns {string|null} the reading a reading ask proposed, or null when it was another kind of question */
94
+ export function readingOfQuestion(q: unknown): string | null;
95
+ /**
96
+ * PRD v1.107 Part 5b ("the look reads, code asks"): the early asks CODE forces before any paid drafting, from the look's own result. Every `~` rule is
97
+ * one of three kinds:
98
+ * - PAGE: a kept page word carries it (a kept fact with `forRule` and no `reading`): no ask.
99
+ * - READING: a kept fact carries it with a `reading` (a limit the page cannot hold, e.g. "not a red-eye" -> departure time between 06:00 and 21:00).
100
+ * Asked only when a bound of the reading is NOT in the words the person wrote (the same test as `rubric-fact-unmarked`, `boundInWords`); a
101
+ * bound they wrote themselves is theirs and nothing is asked. `agree` writes the reading as a `~` rule in plain words (the answer text is
102
+ * `reading`), so the drafter is told it and the fact signs plain.
103
+ * - CAN'T CHECK: no kept fact carries it (PRD v1.106 Part 5 Q3 T2). `drop it` leaves it Not checked.
104
+ * One ask per rule text on a line; readings first, then can't-check asks, each in line order. The caller applies the machine's cap and the answer gate.
105
+ * @param {{facts?: {line: number, forRule?: number, name?: string, shape?: string, unit?: string[], currency?: string[], reading?: import('./pagerules.js').Reading}[]}|null|undefined} look
106
+ * @param {{n: number, text?: string, rule?: string, rules?: string[]}[]|null|undefined} jobLines
107
+ * @returns {LookAsk[]}
108
+ */
109
+ export function askableFromLook(look: {
110
+ facts?: {
111
+ line: number;
112
+ forRule?: number;
113
+ name?: string;
114
+ shape?: string;
115
+ unit?: string[];
116
+ currency?: string[];
117
+ reading?: import("./pagerules.js").Reading;
118
+ }[];
119
+ } | null | undefined, jobLines: {
120
+ n: number;
121
+ text?: string;
122
+ rule?: string;
123
+ rules?: string[];
124
+ }[] | null | undefined): LookAsk[];
125
+ /**
126
+ * PRD v1.107 Part 5b: more asks than the machine's cap means the job needs a rewrite, not a questionnaire. The code-owned stop words list EVERY ask and
127
+ * its reading as the suggestion (no extra paid call). A secret in a rule is masked.
128
+ * @param {LookAsk[]} asks @returns {string}
129
+ */
130
+ export function tooManyAsksText(asks: LookAsk[]): string;
131
+ /** the label of the one paid call, as the drafting book and the progress line see it */
132
+ export const MAP_CALL_LABEL: "page-map";
133
+ /** the shapes the map may name; a `place` is read by code as a phrase */
134
+ export const MAP_SHAPES: readonly string[];
135
+ export const MAP_SYSTEM: "You map a person's job to the exact words of ONE saved web page. You never grade and never invent words: every string you return must be copied from the page text, character for character.";
136
+ /** the plain-word stop when the answer was not readable twice (no Resume: a draft is not a run) */
137
+ export const MAP_UNREADABLE: "The AI's answer about the page was not readable twice. Nothing was signed.";
138
+ /** the answer that drops a `~` rule the page cannot check: the person types it (any case, a closing full stop allowed) */
139
+ export const DROP_IT: "drop it";
140
+ export function isDropIt(text: unknown): boolean;
141
+ export function isAgree(text: unknown): boolean;
142
+ /**
143
+ * `reading` = the plain words an `agree` writes as a `~` rule (a reading ask only)
144
+ */
145
+ export type LookAsk = {
146
+ line: number;
147
+ rule: number;
148
+ source: "look";
149
+ question: string;
150
+ ask: "reading" | "cant-check";
151
+ ruleText: string;
152
+ reading?: string;
153
+ };
154
+ import { scrubRaw } from './text.js';
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Which job line, and which `~` rule under it, a map fact carries out. The map writes `from` as "line N", "line N rule M", "rule M" or
3
+ * "job line"; anything else is the first job line.
4
+ * @param {unknown} from @param {{n: number, text?: string, rule?: string, rules?: string[]}[]} jobLines
5
+ * @returns {{line: number, rule?: number}}
6
+ */
7
+ export function tieOf(from: unknown, jobLines: {
8
+ n: number;
9
+ text?: string;
10
+ rule?: string;
11
+ rules?: string[];
12
+ }[]): {
13
+ line: number;
14
+ rule?: number;
15
+ };
16
+ /**
17
+ * Is this a legal `reading` for a fact of this shape? Only a time or money fact carries one, with the op set and the bound types of a signed
18
+ * fact rule (src/facts.js): no other key, no missing bound. A malformed one makes the whole map answer malformed (no repair).
19
+ * @param {unknown} shape @param {unknown} r @returns {boolean}
20
+ */
21
+ export function validReading(shape: unknown, r: unknown): boolean;
22
+ /**
23
+ * Apply the rules to the AI's map over one saved page. Returns the kept facts and, for everything a rule removed, one `dropped` entry.
24
+ * The order of the rules is the POC's (apply-v3.mjs): found-on-page, then the rules that read the page. A place is read as a phrase.
25
+ * @param {string} text the frozen page text @param {PageMap} mapping @param {{n: number, text?: string, rule?: string, rules?: string[]}[]} jobLines
26
+ * @returns {{facts: KeptFact[], dropped: Dropped[]}}
27
+ */
28
+ export function applyPageRules(text: string, mapping: PageMap, jobLines: {
29
+ n: number;
30
+ text?: string;
31
+ rule?: string;
32
+ rules?: string[];
33
+ }[]): {
34
+ facts: KeptFact[];
35
+ dropped: Dropped[];
36
+ };
37
+ export function linesOf(text: string): string[];
38
+ /**
39
+ * what a `~` rule means as a limit (PRD v1.107 Part 5b): the same shape as a signed fact rule
40
+ */
41
+ export type Reading = {
42
+ op: string;
43
+ value?: string | number;
44
+ lo?: string | number;
45
+ hi?: string | number;
46
+ };
47
+ /**
48
+ * the AI's map, one fact
49
+ */
50
+ export type MapFact = {
51
+ name: string;
52
+ from?: string;
53
+ shape: string;
54
+ words?: string[];
55
+ currency?: string[];
56
+ unit?: string[];
57
+ reading?: Reading;
58
+ };
59
+ export type PageMap = {
60
+ facts: MapFact[];
61
+ };
62
+ /**
63
+ * `words` = what the page proves: the labels of a money/number/time fact, or the alternative phrases of a phrase fact (a place is a phrase)
64
+ */
65
+ export type KeptFact = {
66
+ line: number;
67
+ name: string;
68
+ shape: string;
69
+ from: string;
70
+ words: string[];
71
+ unit?: string[];
72
+ currency?: string[];
73
+ forRule?: number;
74
+ reading?: Reading;
75
+ };
76
+ /**
77
+ * `other` = what the drop is compared to: the first label (R1), the primary form (R1L), the fact it would have repeated (S1)
78
+ */
79
+ export type Dropped = {
80
+ line: number;
81
+ rule?: number;
82
+ fact: string;
83
+ word?: string;
84
+ reason: string;
85
+ other?: string;
86
+ };
@@ -33,6 +33,19 @@ export function checkHumanGuard(req: import("node:http").IncomingMessage, { toke
33
33
  ok: false;
34
34
  reason: string;
35
35
  };
36
+ /**
37
+ * The one-at-a-time authoring session store (build spec: "one authoring
38
+ * session at a time — a second Start is refused while one is live"). A
39
+ * session is "live" from creation until its phase settles into a terminal
40
+ * one; a terminal session is kept (so its chat/hash stay readable) but no
41
+ * longer counts against the one-at-a-time rule.
42
+ */
43
+ /**
44
+ * PRD v1.106 Part 5 Q2: the "fetching the page..." step is done once the run's own refetch has written `refrozenAt` (source.json, written last).
45
+ * Settled when the state is read, so nothing polls and the step never stays running after the fetch.
46
+ * @param {any} state
47
+ */
48
+ export function settleFetchingStep(state: any): void;
36
49
  /**
37
50
  * Builds this file's own request handler, holding its session store in a
38
51
  * closure (one per `createPanelServer` call, exactly like `handleRequest`'s
@@ -79,7 +92,6 @@ export function createAuthorRoutes(opts: {
79
92
  workflowKey: string;
80
93
  } | null, inputs: import("../jobblock.js").JobInput[], o?: {
81
94
  headless?: boolean;
82
- filesJob?: boolean;
83
95
  }) => Promise<{
84
96
  ok: true;
85
97
  session: ReturnType<typeof createSession>;
@@ -94,19 +106,6 @@ export function createAuthorRoutes(opts: {
94
106
  /** a headless session the starter has removed from disk leaves its map too @param {string} id */
95
107
  forgetHeadless: (id: string) => void;
96
108
  };
97
- /**
98
- * The panel's OWN 2-decimal money render for a chat line — hamr's ruling
99
- * 2026-09-28 (2nd addendum, panel money 2-decimals): a positive amount below
100
- * a cent reads `<$0.01`, never `$0.00` (a real cost must never read as
101
- * free). This is the server-side (Node, chat-text) sibling of
102
- * `src/panel/index.html`'s own inline `panelMoney` — the two can't share
103
- * code (one runs in the browser as inlined script, the other in Node), the
104
- * same duplication `src/replay.js`'s `money()`/`index.html`'s `money()`
105
- * already carry, so both apply the identical rule rather than drift.
106
- * @param {number} n a non-negative dollar amount
107
- * @returns {string}
108
- */
109
- export function panelMoney2(n: number): string;
110
109
  /**
111
110
  * THE ONLY function in this file (or anywhere in the panel) that spawns a
112
111
  * run. Refuses unless the session is `prepared` AND the hash the request
@@ -136,5 +135,9 @@ export function signRun(session: ReturnType<typeof createSession>, claimedHash:
136
135
  export const OLD_JOB_REUSE_WORDS: "This run was made by an older bareloop. Its saved job doesn't pass today's checks, so it can't be reused. Start a new job instead.";
137
136
  export const OLD_JOB_RESUME_WORDS: "This run was made by an older bareloop. Its saved job doesn't pass today's checks, so it can't be resumed. Use Reuse workflow on a newer run, or start a new job.";
138
137
  export const OLD_JOB_ACCEPT_WORDS: "This run was made by an older bareloop. Its saved job doesn't pass today's checks, so it can't be accepted. Use Reuse workflow on a newer run, or start a new job.";
138
+ export const OLD_JOB_RERUN_WORDS: "This run was made by an older bareloop. Its saved job doesn't pass today's checks, so it can't be rerun. Use Reuse workflow on a newer run, or start a new job.";
139
+ export const OLD_JOB_PAUSE_WORDS: "This run was made by an older bareloop. Its saved job doesn't pass today's checks, so it can't be paused from here. Use Reuse workflow on a newer run, or start a new job.";
140
+ export { panelMoney2 };
139
141
  import { spawn as realSpawn } from 'node:child_process';
140
142
  import { createSession } from './authorsession.js';
143
+ import { panelMoney2 } from '../panelmoney.js';
@@ -4,12 +4,12 @@
4
4
  * recorded...`, `CHECKPOINT EXPIRED — ...`); anything else (validator reds, exit statuses, stack traces, argv) becomes ONE fixed
5
5
  * sentence naming the log. The one owner for the Resume and Accept routes.
6
6
  * @param {string} tail the log tail
7
- * @param {'resume'|'accept'} kind
7
+ * @param {'resume'|'accept'|'rerun'|'pause'} kind
8
8
  * @param {string} logFile
9
9
  * @param {(stage: string) => string|null} [questionOf] stage name to the panel's plain question (`null` = none)
10
10
  * @returns {string}
11
11
  */
12
- export function engineRefusalWords(tail: string, kind: "resume" | "accept", logFile: string, questionOf?: (stage: string) => string | null): string;
12
+ export function engineRefusalWords(tail: string, kind: "resume" | "accept" | "rerun" | "pause", logFile: string, questionOf?: (stage: string) => string | null): string;
13
13
  /**
14
14
  * The run routes' handler. `getResumeContext(runid)` is the server's own reader (the same
15
15
  * `resumePlanFor` the Ended block uses, so the page never offers what this refuses):