bareloop 0.41.0 → 0.43.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 +109 -1
- package/README.md +13 -21
- package/bareloop.context.md +97 -34
- package/package.json +4 -3
- package/src/authorflow.js +337 -29
- package/src/authoring.js +92 -7
- package/src/authorjob.js +84 -16
- package/src/authorreadout.js +89 -5
- package/src/authorrun.js +14 -5
- package/src/authorscout.js +108 -0
- package/src/declaredclose.js +25 -14
- package/src/facts.js +317 -0
- package/src/factwords.js +129 -0
- package/src/inputlook.js +301 -0
- package/src/interviewrun.js +0 -1
- package/src/job.js +40 -12
- package/src/jobblock.js +7 -2
- package/src/kinds.js +84 -3
- package/src/labelscan.js +170 -0
- package/src/pagefetch.js +125 -0
- package/src/panel/authorroutes.js +121 -34
- package/src/panel/authorsession.js +154 -45
- package/src/panel/index.html +845 -490
- package/src/panel/jobplan.js +34 -11
- package/src/panel/runroutes.js +163 -5
- 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 +467 -48
- package/src/planrun.js +42 -9
- package/src/providerrows.js +3 -3
- package/src/reviewdoor.js +37 -0
- package/src/rubricstep.js +804 -0
- package/src/runlist.js +35 -0
- package/src/schedulewords.js +296 -0
- package/src/source.js +166 -1
- 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/u-readout.js +1 -1
- package/src/userrun.js +20 -9
- package/types/authorflow.d.ts +267 -13
- package/types/authoring.d.ts +29 -1
- package/types/authorjob.d.ts +7 -5
- package/types/authorreadout.d.ts +27 -2
- package/types/authorscout.d.ts +70 -0
- package/types/declaredclose.d.ts +5 -3
- package/types/facts.d.ts +71 -0
- package/types/factwords.d.ts +42 -0
- package/types/inputlook.d.ts +238 -0
- package/types/job.d.ts +8 -1
- package/types/jobblock.d.ts +8 -1
- package/types/labelscan.d.ts +38 -0
- package/types/pagefetch.d.ts +71 -0
- package/types/panel/authorroutes.d.ts +34 -1
- package/types/panel/authorsession.d.ts +55 -14
- package/types/panel/jobplan.d.ts +12 -4
- 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 +145 -14
- package/types/planrun.d.ts +2 -0
- package/types/providerrows.d.ts +2 -2
- package/types/reviewdoor.d.ts +27 -0
- package/types/rubricstep.d.ts +286 -0
- package/types/runlist.d.ts +20 -0
- package/types/schedulewords.d.ts +135 -0
- package/types/source.d.ts +94 -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 +11 -0
package/src/planrun.js
CHANGED
|
@@ -45,6 +45,7 @@ import { defaultJudgeLoop } from './judged.js';
|
|
|
45
45
|
export { rateSourceFields };
|
|
46
46
|
import { createClock, isWallTimeout } from './clock.js';
|
|
47
47
|
import { isDeclaredClose, runDeclaredStages, validateCloseDecl, closeGrade, HUMAN_PAUSE, HITL_PAUSE, HITL_DECISION_RED } from './declaredclose.js';
|
|
48
|
+
import { inputLabelsOf } from './rubricstep.js';
|
|
48
49
|
import {
|
|
49
50
|
seedAtHead, seedListing, changedSet, GAP_TRIM_MARKER, GATE_AUDIT_FILE, ARBITER_BOOK_STORES,
|
|
50
51
|
HUMAN_KIND, HUMAN_DECISIONS, normalizeHumanRuling, resolveHumanRuling,
|
|
@@ -540,6 +541,10 @@ export const BOUND_REASON_MAX = 200;
|
|
|
540
541
|
|
|
541
542
|
/** the recorded reason for a step attempt whose round was cut at the output cap (F122 2A b) */
|
|
542
543
|
export const TRUNCATED_REASON = 'output cut at the token limit (max_tokens)';
|
|
544
|
+
/** the red fed to the redraft when the first replan answer was cut off (plain words, from code) */
|
|
545
|
+
const REPLAN_CUT_RED = 'Your previous answer was cut off at its length limit before the plan was complete. Answer again with a shorter plan that fits.';
|
|
546
|
+
/** the plain sentence for a replan answer cut off at its length limit (hamr 2026-10-16, run mv9gm796) */
|
|
547
|
+
export const REPLAN_CUT_WORDS = 'The replan answer was cut off at its length limit, so no new plan came back.';
|
|
543
548
|
|
|
544
549
|
/**
|
|
545
550
|
* What the NEXT attempt is told about the bound that cut the previous one.
|
|
@@ -1886,7 +1891,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
1886
1891
|
},
|
|
1887
1892
|
decisionReady: true,
|
|
1888
1893
|
decision: `The close minted ${outcome} and it STANDS. What happens to the work is yours: accept it, rerun it with your own words as the gap, or pause and come back.`
|
|
1889
|
-
+ (held ? ' This
|
|
1894
|
+
+ (held ? ' This is a rubric (soft-green) job, so its green earns no reuse until you accept it, whether or not a judge ran.' : ''),
|
|
1890
1895
|
options: [...HUMAN_DECISIONS],
|
|
1891
1896
|
meaning: 'not a verdict — the verdict is already minted and nothing an answer does can change it; this records a disposition',
|
|
1892
1897
|
});
|
|
@@ -2036,7 +2041,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2036
2041
|
}
|
|
2037
2042
|
const listed = await seedListing(workdir, s.seedRef);
|
|
2038
2043
|
const v = listed.stop === null
|
|
2039
|
-
? validateCloseDecl(job.closeDecl, { at: 'closeDecl', listing: listed.files, verdictType: job.verdictType, writeScope: job.writeScope, jobLines: Array.isArray(job.jobLines) ? job.jobLines : null })
|
|
2044
|
+
? validateCloseDecl(job.closeDecl, { at: 'closeDecl', listing: listed.files, verdictType: job.verdictType, writeScope: job.writeScope, jobLines: Array.isArray(job.jobLines) ? job.jobLines : null , inputLabels: inputLabelsOf(job.inputs) })
|
|
2040
2045
|
: { ok: false, grounded: false, reds: [{ code: 'listing-unreadable', path: 'closeDecl', detail: listed.stop }] };
|
|
2041
2046
|
emit('close-decl', {
|
|
2042
2047
|
genre: job.closeDecl.genre,
|
|
@@ -2231,7 +2236,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2231
2236
|
* denied, the wallet as its budget), granted tools only (the menu IS the
|
|
2232
2237
|
* grant), per-attempt round bound via loop.stop() (F20), every round metered
|
|
2233
2238
|
* with a phase label (F12).
|
|
2234
|
-
* @param {{granted: string[], phase: string, attemptRounds: number, attempts: number|null, writable: boolean, root?: ReturnType<typeof createRoot>|null, fence?: string[]|null, workerProvider?: any}} o
|
|
2239
|
+
* @param {{granted: string[], phase: string, attemptRounds: number, attempts: number|null, writable: boolean, root?: ReturnType<typeof createRoot>|null, fence?: string[]|null, workerProvider?: any, truncatedBounded?: boolean}} o
|
|
2235
2240
|
* `root` (Layer R): when present, the worker's write-class actions are teed
|
|
2236
2241
|
* for the within-run ratchet — staged before the gate decides, discarded on
|
|
2237
2242
|
* deny/halt, settled to landed-or-not after execution (the two axes, F43/F7).
|
|
@@ -2241,7 +2246,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2241
2246
|
* STRIKE LADDER (src/ladder.js), where that count does not exist by design — see
|
|
2242
2247
|
* the turn-allowance note below.
|
|
2243
2248
|
*/
|
|
2244
|
-
async function mkWorker({ granted, phase, attemptRounds, attempts, writable, root = null, fence = null, workerProvider = null }) {
|
|
2249
|
+
async function mkWorker({ granted, phase, attemptRounds, attempts, writable, root = null, fence = null, workerProvider = null, truncatedBounded = false }) {
|
|
2245
2250
|
// THE HARD RULE, ENFORCED STRUCTURALLY (PRD v1.57 §3). This is the ONE seam
|
|
2246
2251
|
// that grants write-class verbs — `writeScope` is empty for every other
|
|
2247
2252
|
// worker, so a read-only phase cannot reach a file whatever it intends — and
|
|
@@ -2824,8 +2829,10 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2824
2829
|
// cut round's output is discarded (bare-agent already dropped it), its spend is booked
|
|
2825
2830
|
// by the per-round meter, the tree is judged as it stands, and the step's strike
|
|
2826
2831
|
// ladder (or the fix governor) applies unchanged (hamr ruling A extended it to `fix`
|
|
2827
|
-
// after run muux1x96).
|
|
2828
|
-
|
|
2832
|
+
// after run muux1x96). hamr 2026-10-16 (run mv9gm796) extended it to the REPLAN call: the
|
|
2833
|
+
// replan drafter opts in with `truncatedBounded`. Scout and the first draft keep the
|
|
2834
|
+
// transport route below (provider-red).
|
|
2835
|
+
if (r.error && r.error.startsWith('truncated:') && (phase.startsWith('step:') || phase === 'fix' || truncatedBounded)) {
|
|
2829
2836
|
attemptBounded = { iteration: roundIteration, cause: 'truncated', reason: TRUNCATED_REASON };
|
|
2830
2837
|
emit('attempt-bounded', { phase, iteration: roundIteration, rounds: roundsThisAttempt, cap: attemptRounds, reason: TRUNCATED_REASON, stopReason: r.stopReason ?? null });
|
|
2831
2838
|
return r;
|
|
@@ -3007,9 +3014,13 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
3007
3014
|
* the hard line) — a drafter built once at full budget would let the replan
|
|
3008
3015
|
* draft spend against money the steps had already consumed.
|
|
3009
3016
|
* @param {string} phase @param {string|null} failure
|
|
3010
|
-
* @param {string} [progress] the meter's read, replan only (A's adaptation channel)
|
|
3017
|
+
* @param {string} [progress] the meter's read, replan only (A's adaptation channel)
|
|
3018
|
+
* @returns {Promise<{ok: boolean, reds: any[], plan?: any, truncated?: boolean}>} */
|
|
3011
3019
|
const obtainPlan = async (phase, failure, progress) => {
|
|
3012
|
-
const drafter = await mkWorker({ granted: [], phase: 'plan', attemptRounds: 2, attempts: 3, writable: false });
|
|
3020
|
+
const drafter = await mkWorker({ granted: [], phase: 'plan', attemptRounds: 2, attempts: 3, writable: false, truncatedBounded: phase === 'replan' });
|
|
3021
|
+
/** the LAST draftPlan call was a replan answer cut off at max_tokens: a bounded failed attempt,
|
|
3022
|
+
* never provider-red (hamr 2026-10-16); reset at the start of every call */
|
|
3023
|
+
let replanCut = false;
|
|
3013
3024
|
// The BALANCE, read live at each draft — never a snapshot and never a rate
|
|
3014
3025
|
// (addendum 2). At draft it is the whole budget (scale); at replan it is what
|
|
3015
3026
|
// is actually left, which is the adaptation channel.
|
|
@@ -3038,7 +3049,12 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
3038
3049
|
};
|
|
3039
3050
|
const draftPlan = async (/** @type {any[]|null} */ reds) => {
|
|
3040
3051
|
drafter.setIteration(reds ? 'redraft' : 'draft');
|
|
3052
|
+
replanCut = false;
|
|
3041
3053
|
const r = await drafter.ask(planPrompt(job, scoutBlob, reds, maxStepRounds, failure, scopeMenu, materials, starting, checkFacts, doorWords?.text ?? null, readShim), []);
|
|
3054
|
+
if (phase === 'replan' && typeof r.error === 'string' && r.error.startsWith('truncated:')) {
|
|
3055
|
+
replanCut = true;
|
|
3056
|
+
return '';
|
|
3057
|
+
}
|
|
3042
3058
|
return extractArtifact(r.text).code ?? '';
|
|
3043
3059
|
};
|
|
3044
3060
|
// Scrubbed HERE, once, before any consumer reads them — the judge() precedent
|
|
@@ -3055,10 +3071,17 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
3055
3071
|
return { ...v, reds: v.reds.map((r) => (typeof r.detail === 'string' ? { ...r, detail: scrub(r.detail) } : r)) };
|
|
3056
3072
|
};
|
|
3057
3073
|
let text = await draftPlan(null);
|
|
3058
|
-
|
|
3074
|
+
// A cut-off replan answer is treated like a BAD one: its half text is never validated (judging
|
|
3075
|
+
// half an artifact would mint a plan-red the drafter never earned), it becomes one plain-words
|
|
3076
|
+
// red fed to the redraft, and a cut REDRAFT (whatever the first answer was) is the bounded
|
|
3077
|
+
// failed attempt the caller escalates in words.
|
|
3078
|
+
let pv = replanCut
|
|
3079
|
+
? { ok: false, reds: [{ code: 'parse-error', path: '$', detail: REPLAN_CUT_RED }], plan: null }
|
|
3080
|
+
: validate(text);
|
|
3059
3081
|
emit('plan-validate', { ok: pv.ok, reds: pv.reds, phase: `${phase}-1` });
|
|
3060
3082
|
if (!pv.ok) {
|
|
3061
3083
|
text = await draftPlan(pv.reds);
|
|
3084
|
+
if (replanCut) return { ok: false, reds: [], truncated: true };
|
|
3062
3085
|
pv = validate(text);
|
|
3063
3086
|
emit('plan-validate', { ok: pv.ok, reds: pv.reds, phase: `${phase}-2` });
|
|
3064
3087
|
}
|
|
@@ -3658,6 +3681,16 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
3658
3681
|
planExecuted();
|
|
3659
3682
|
return relay(e, 'replan');
|
|
3660
3683
|
}
|
|
3684
|
+
if (pv.truncated) {
|
|
3685
|
+
// a bounded failed attempt in plain words, filed as plan-red (a judged no, not a casualty)
|
|
3686
|
+
// with the code-owned sentence on the escalation; the panel reads `cause` to say it
|
|
3687
|
+
emit('escalation', {
|
|
3688
|
+
category: 'plan-red', cause: 'truncated', decisionReady: true, phase: 'replan',
|
|
3689
|
+
decision: REPLAN_CUT_WORDS, options: ['change the job and run it again', 'abandon the run'], detail: TRUNCATED_REASON,
|
|
3690
|
+
});
|
|
3691
|
+
planExecuted();
|
|
3692
|
+
return 'plan-red';
|
|
3693
|
+
}
|
|
3661
3694
|
if (!pv.ok) {
|
|
3662
3695
|
for (const r of pv.reds) emit('plan-red', r);
|
|
3663
3696
|
planExecuted();
|
package/src/providerrows.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
//
|
|
10
10
|
// This module is also THE one owner of "model -> key + shape + URL": Chat's Model menu,
|
|
11
11
|
// the author doors and the run doors all resolve through `findRow` / `keyNameFor` /
|
|
12
|
-
// `
|
|
12
|
+
// `keyValueOf` / `modelChoiceFor` below — no second lookup exists. Key VALUES
|
|
13
13
|
// never pass through here — only names.
|
|
14
14
|
import { readConfig } from './config.js';
|
|
15
15
|
import { filledKeyNames } from './keysfile.js';
|
|
@@ -388,8 +388,8 @@ export function judgeRowFor(rows, judge, workerProvider, workerRow) {
|
|
|
388
388
|
* the worker's own provider it calls the worker's endpoint, so a row on that endpoint with a line
|
|
389
389
|
* NAMED for the judge's model prices it ({@link ratesFor}); a judge model that is absent or names
|
|
390
390
|
* no line there keeps the worker's price (the worker's row, or none = the guess). A different
|
|
391
|
-
* provider resolves with no endpoint. The judge's KEY is chosen elsewhere (`
|
|
392
|
-
*
|
|
391
|
+
* provider resolves with no endpoint. The judge's KEY is chosen elsewhere (`judgeRowFor`:
|
|
392
|
+
* the worker's own row when the judge shares its provider), so the two can come from different rows.
|
|
393
393
|
* @param {{ provider: string, model?: string|null }} judge
|
|
394
394
|
* @param {string|null|undefined} workerProvider
|
|
395
395
|
* @param {string|null|undefined} workerBaseUrl
|
package/src/reviewdoor.js
CHANGED
|
@@ -50,6 +50,43 @@ export function doorRecordOf(events) {
|
|
|
50
50
|
return events.findLast?.((e) => e?.type === REVIEW_DOOR) ?? null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Was this run's door ACCEPTED, honoured? A `door-decision` record with `decision: 'accept'` and `honoured: true`
|
|
55
|
+
* (written by {@link answerReviewDoor}, whichever surface asked: the CLI, or the panel's Accept). Pure, off the
|
|
56
|
+
* run's spine. A refused accept (`honoured: false`) does not count.
|
|
57
|
+
* @param {any[]} events the run's spine, parsed
|
|
58
|
+
* @returns {boolean}
|
|
59
|
+
*/
|
|
60
|
+
export function doorAccepted(events) {
|
|
61
|
+
return Array.isArray(events) && events.some((e) => e?.type === 'door-decision' && e.decision === 'accept' && e.honoured === true);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 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
|
|
66
|
+
* start wrote it) and holds no honoured accept. One shared predicate: `getStartFrom` reads its outcome as `'green-held'` (so
|
|
67
|
+
* the Reuse start refuses it unchanged), `listReuseJobs` skips it, and the schedule gate does not count it. A hand run is never held.
|
|
68
|
+
* @param {any[]} events the run's spine, parsed @param {any} triggerRec the run's `trigger.json`, or null
|
|
69
|
+
* @returns {boolean}
|
|
70
|
+
*/
|
|
71
|
+
export function heldScheduled(events, triggerRec) {
|
|
72
|
+
return !!triggerRec && typeof triggerRec === 'object' && !doorAccepted(events);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Is this run WAITING for the person's Accept? Its spine holds a door record, the door has not expired ({@link doorAgeGate}),
|
|
77
|
+
* and no honoured `accept` or `rerun` decision follows it (a `pause` keeps the same three doors open). One owner: the runs
|
|
78
|
+
* list's Needs-you `Accept` tag, the Run tab's Accept button and the scheduled NEXT line all read this.
|
|
79
|
+
* @param {any[]} events the run's spine, parsed
|
|
80
|
+
* @param {{now?: () => number}} [opts]
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
export function doorWaiting(events, opts = {}) {
|
|
84
|
+
const door = doorRecordOf(events);
|
|
85
|
+
if (!door || !doorAgeGate(door, opts.now ? { now: opts.now } : {}).ok) return false;
|
|
86
|
+
const at = events.lastIndexOf(door);
|
|
87
|
+
return !events.slice(at + 1).some((e) => e?.type === 'door-decision' && e.honoured === true && (e.decision === 'accept' || e.decision === 'rerun'));
|
|
88
|
+
}
|
|
89
|
+
|
|
53
90
|
/**
|
|
54
91
|
* IS THE DOOR STILL OPEN? The same 60-day TTL a hitl checkpoint keeps
|
|
55
92
|
* (`PAUSE_TTL_MS`) and deliberately the same NUMBER — the door replaced `cancel`
|