bareloop 0.25.1 → 0.27.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 +525 -0
- package/bareloop.context.md +384 -84
- package/package.json +4 -3
- package/src/authorflow.js +947 -104
- package/src/authoring.js +33 -15
- package/src/authorjob.js +161 -5
- package/src/authorscout.js +16 -11
- package/src/cardauthor.js +28 -18
- package/src/cli.js +10 -1
- package/src/detectlang.js +200 -0
- package/src/index.js +26 -30
- package/src/job.js +45 -28
- package/src/planrun.js +81 -2
- package/src/promptregisters.js +14 -0
- package/src/providers.js +51 -3
- package/src/source.js +290 -33
- package/src/text.js +45 -0
- package/src/validate.js +25 -1
- package/types/authorflow.d.ts +476 -105
- package/types/authoring.d.ts +23 -8
- package/types/authorjob.d.ts +22 -4
- package/types/cardauthor.d.ts +7 -3
- package/types/detectlang.d.ts +38 -0
- package/types/index.d.ts +16 -16
- package/types/job.d.ts +24 -2
- package/types/planrun.d.ts +41 -1
- package/types/providers.d.ts +37 -2
- package/types/source.d.ts +87 -0
- package/types/text.d.ts +35 -0
- package/types/validate.d.ts +22 -0
package/types/authoring.d.ts
CHANGED
|
@@ -431,14 +431,29 @@ export const MAX_STAGES: 12;
|
|
|
431
431
|
*/
|
|
432
432
|
export const DENIED_COMMANDS: readonly string[];
|
|
433
433
|
/**
|
|
434
|
-
* THE
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
*
|
|
434
|
+
* THE TYPES GENRE TEMPLATE — six policy points, VERBATIM against
|
|
435
|
+
* `docs/product/ITEM33-BUILD.md`'s own frozen block (PRD item 33 M3 piece 4,
|
|
436
|
+
* D6 = A ruling, 2026-09-14). This SUPERSEDES the original prereg-frozen
|
|
437
|
+
* version (`docs/logs/2026-08-08-close-authoring-gate2-poc-prereg.md`,
|
|
438
|
+
* addendum 3, the closed record — never edited): that version stated the
|
|
439
|
+
* WORK-stage skeleton (typecheck, typecheck-outside, tests-kept,
|
|
440
|
+
* suite-green) and the checker's STRICT form as UNCONDITIONAL law, which
|
|
441
|
+
* conflicted with ruling 6's "a genre never adds a check the goal did not
|
|
442
|
+
* ask for" — run mtv8jihy's unasked `tsc --strict` stage is the case this
|
|
443
|
+
* cures. Composing those stages/strictness only for a CONFIRMED check
|
|
444
|
+
* (never a code matcher over prose, same reasoning as the no-shell law) is
|
|
445
|
+
* enforced here, in the prompt; the GUARDS (changed-from-seed,
|
|
446
|
+
* no-suppressions) are enforced separately and structurally
|
|
447
|
+
* (`classGuards` + `validateDeclaration`'s `guards-absent` red), so making
|
|
448
|
+
* the WORK stages conditional here changes nothing about their own
|
|
449
|
+
* always-on status. The suite reads the SIGNED block and compares BYTES, so
|
|
450
|
+
* a paraphrase here fails the build rather than quietly changing what every
|
|
451
|
+
* future close is authored against. It deliberately carries no
|
|
452
|
+
* repo-specific command, path, env value or regex: round 2 measured that
|
|
453
|
+
* stating the LAW still leaves the model to find the VALUE, and that is the
|
|
454
|
+
* axis the build must keep live.
|
|
440
455
|
*/
|
|
441
|
-
export const TYPES_GENRE_TEMPLATE: "1. The
|
|
456
|
+
export const TYPES_GENRE_TEMPLATE: "1. The STRICT form of the language's type checker is used only when a confirmed check \u2014 or,\n absent a confirm turn, the goal and interview answers \u2014 asks for strict typing; otherwise the\n checker runs at whatever strictness the repo's own scripts already use.\n2. Tools are invoked so their binaries actually resolve \u2014 through the project's own package\n runner or language module runner, never a bare binary name.\n3. The WORK stages \u2014 typecheck (error count IN the target scope, baseline 0, lower-is-better),\n typecheck-outside (error count OUTSIDE the target scope, baseline measured at seed, a\n ceiling \u2014 required whenever a typecheck stage is scoped to a subset of the tree), tests-kept\n (a floor on tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or\n deselected test did not run and must not count), and suite-green (the suite exits clean AND\n reports zero failing tests \u2014 two assertions) \u2014 are composed ONLY for the checks a confirmed\n plan asked for (or, absent a confirm turn, the goal and interview answers). The GUARDS \u2014\n changed-from-seed and no-suppressions \u2014 are composed always, first-red-wins:\n changed-from-seed before every WORK stage, no-suppressions last.\n4. One population per stage \u2014 two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
|
|
442
457
|
export const GENRE_LANGUAGES: readonly string[];
|
|
443
458
|
/**
|
|
444
459
|
* THE GENRE, as data. The suppression batteries are the hand-written closes'
|
|
@@ -457,7 +472,7 @@ export const GENRE_LANGUAGES: readonly string[];
|
|
|
457
472
|
*/
|
|
458
473
|
export const TYPES_GENRE: Readonly<{
|
|
459
474
|
name: "TYPES";
|
|
460
|
-
template: "1. The
|
|
475
|
+
template: "1. The STRICT form of the language's type checker is used only when a confirmed check — or,\n absent a confirm turn, the goal and interview answers — asks for strict typing; otherwise the\n checker runs at whatever strictness the repo's own scripts already use.\n2. Tools are invoked so their binaries actually resolve — through the project's own package\n runner or language module runner, never a bare binary name.\n3. The WORK stages — typecheck (error count IN the target scope, baseline 0, lower-is-better),\n typecheck-outside (error count OUTSIDE the target scope, baseline measured at seed, a\n ceiling — required whenever a typecheck stage is scoped to a subset of the tree), tests-kept\n (a floor on tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or\n deselected test did not run and must not count), and suite-green (the suite exits clean AND\n reports zero failing tests — two assertions) — are composed ONLY for the checks a confirmed\n plan asked for (or, absent a confirm turn, the goal and interview answers). The GUARDS —\n changed-from-seed and no-suppressions — are composed always, first-red-wins:\n changed-from-seed before every WORK stage, no-suppressions last.\n4. One population per stage — two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
|
|
461
476
|
languages: Readonly<{
|
|
462
477
|
js: Readonly<{
|
|
463
478
|
suppressions: readonly (Readonly<{
|
package/types/authorjob.d.ts
CHANGED
|
@@ -85,13 +85,16 @@ export function refuseLockedKind(kind: string): Refusal;
|
|
|
85
85
|
* onPhase?: (phase: string, data?: any) => void,
|
|
86
86
|
* onCall?: (call: {label: string, costUsd: number|null, unpricedRounds: number}) => void,
|
|
87
87
|
* seedFn?: Function, scoutFn?: Function, listingFn?: Function,
|
|
88
|
-
* authorFn?: Function, authorOpts?: object,
|
|
89
|
-
* signerFix?: Function|any, proposeFn?: Function, compileOpts?: object
|
|
88
|
+
* authorFn?: Function, authorOpts?: object, writeScope?: string[]|null,
|
|
89
|
+
* signerFix?: Function|any, proposeFn?: Function, compileOpts?: object,
|
|
90
|
+
* ask?: ((step: {kind: string, [k: string]: any}) => Promise<string|null>)|null,
|
|
91
|
+
* confirmGenerate?: Function|null, isRepo?: boolean,
|
|
92
|
+
* langResult?: {kind: string, candidates?: string[], [k: string]: any}|null}} o
|
|
90
93
|
* @returns {Promise<{ok: boolean, refusal: Refusal|null, verdictType: string|null,
|
|
91
94
|
* closeDecl: any, seedRef: string|null, authoring: any, judged: any, reds: Red[],
|
|
92
|
-
* stop: string|null, cost: any, interview: any}>}
|
|
95
|
+
* stop: string|null, cost: any, interview: any, confirmed: any}>}
|
|
93
96
|
*/
|
|
94
|
-
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, }: {
|
|
97
|
+
export function authorCloseForJob({ answers, repoPath, lang, verdictType, questions, generate, provider, seedRef, scout, listing, ceilingUsd, writeScope, judgeModel, onPhase, onCall, seedFn, scoutFn, listingFn, authorFn, authorOpts, signerFix, proposeFn, compileOpts, ask, confirmGenerate, isRepo, langResult, }: {
|
|
95
98
|
answers: Record<string | number, any>;
|
|
96
99
|
repoPath?: string | null;
|
|
97
100
|
lang: string;
|
|
@@ -115,9 +118,21 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
|
|
|
115
118
|
listingFn?: Function;
|
|
116
119
|
authorFn?: Function;
|
|
117
120
|
authorOpts?: object;
|
|
121
|
+
writeScope?: string[] | null;
|
|
118
122
|
signerFix?: Function | any;
|
|
119
123
|
proposeFn?: Function;
|
|
120
124
|
compileOpts?: object;
|
|
125
|
+
ask?: ((step: {
|
|
126
|
+
kind: string;
|
|
127
|
+
[k: string]: any;
|
|
128
|
+
}) => Promise<string | null>) | null;
|
|
129
|
+
confirmGenerate?: Function | null;
|
|
130
|
+
isRepo?: boolean;
|
|
131
|
+
langResult?: {
|
|
132
|
+
kind: string;
|
|
133
|
+
candidates?: string[];
|
|
134
|
+
[k: string]: any;
|
|
135
|
+
} | null;
|
|
121
136
|
}): Promise<{
|
|
122
137
|
ok: boolean;
|
|
123
138
|
refusal: Refusal | null;
|
|
@@ -130,6 +145,7 @@ export function authorCloseForJob({ answers, repoPath, lang, verdictType, questi
|
|
|
130
145
|
stop: string | null;
|
|
131
146
|
cost: any;
|
|
132
147
|
interview: any;
|
|
148
|
+
confirmed: any;
|
|
133
149
|
}>;
|
|
134
150
|
/**
|
|
135
151
|
* Fold an authored close into the OPERATOR's own half of the spec.
|
|
@@ -259,6 +275,8 @@ export const REFUSAL_CATEGORY: "close-unauthorable";
|
|
|
259
275
|
* carry. Named as data so the refusal and the fold can never disagree about
|
|
260
276
|
* which half of the spec is which. */
|
|
261
277
|
export const AUTHORED_SPEC_FIELDS: readonly string[];
|
|
278
|
+
export const CONFIRM_AUTHORED_FIELDS: readonly string[];
|
|
279
|
+
export const PLAIN_FOLDER_DEFERRED_FIELDS: readonly string[];
|
|
262
280
|
export type Red = {
|
|
263
281
|
code: string;
|
|
264
282
|
path: string;
|
package/types/cardauthor.d.ts
CHANGED
|
@@ -200,8 +200,12 @@ export function proposalTool(box: {
|
|
|
200
200
|
execute: (args: any) => Promise<string>;
|
|
201
201
|
};
|
|
202
202
|
/**
|
|
203
|
-
* The compile prompt.
|
|
204
|
-
* input, and paraphrasing a lay answer is how a rubric stops being the
|
|
203
|
+
* The compile prompt. The Judge Examples answer travels VERBATIM — it is the
|
|
204
|
+
* whole input, and paraphrasing a lay answer is how a rubric stops being the
|
|
205
|
+
* person's. PRD item 33 M3 piece 3: this used to be two answers (Q6+Q7);
|
|
206
|
+
* `SOFTGREEN_JUDGE_EXAMPLES_KEY` is the ONE key both the interview and this
|
|
207
|
+
* compile now read, so a future renumbering of the form moves both readers
|
|
208
|
+
* together rather than drifting.
|
|
205
209
|
*
|
|
206
210
|
* The rulebook is rendered from `JUDGE_RULES` itself rather than restated, for
|
|
207
211
|
* the reason the catalogue block is rendered from the catalogue: the prompt and
|
|
@@ -288,7 +292,7 @@ export function foldJudgedArtifacts(closeDecl: any, { card, cases, judgeModel }:
|
|
|
288
292
|
judgeModel: string;
|
|
289
293
|
}): any;
|
|
290
294
|
/**
|
|
291
|
-
* ONE compile call: the person's
|
|
295
|
+
* ONE compile call: the person's Judge Examples answer in, a proposal out.
|
|
292
296
|
*
|
|
293
297
|
* It buys ONE artifact through ONE channel, and the malformed-emission ladder is
|
|
294
298
|
* the SHARED one (`askStructured`) rather than a second copy — a second ladder is
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect a code job's language from its repository, $0, no provider.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} sourcePath the Source path (a folder, or a file inside one)
|
|
5
|
+
* @returns {{kind: 'resolved', lang: string, dir: string, manifest: string} | {kind: 'language-unsupported', language: string, dir: string, manifest: string, refusal: LangRefusal} | {kind: 'no-code-job'} | {kind: 'ambiguous', candidates: string[], dir: string}}
|
|
6
|
+
*/
|
|
7
|
+
export function detectLanguage(sourcePath: string): {
|
|
8
|
+
kind: "resolved";
|
|
9
|
+
lang: string;
|
|
10
|
+
dir: string;
|
|
11
|
+
manifest: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "language-unsupported";
|
|
14
|
+
language: string;
|
|
15
|
+
dir: string;
|
|
16
|
+
manifest: string;
|
|
17
|
+
refusal: LangRefusal;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "no-code-job";
|
|
20
|
+
} | {
|
|
21
|
+
kind: "ambiguous";
|
|
22
|
+
candidates: string[];
|
|
23
|
+
dir: string;
|
|
24
|
+
};
|
|
25
|
+
export type Red = {
|
|
26
|
+
code: string;
|
|
27
|
+
path: string;
|
|
28
|
+
detail: string;
|
|
29
|
+
[k: string]: any;
|
|
30
|
+
};
|
|
31
|
+
export type LangRefusal = {
|
|
32
|
+
kind: "request-red";
|
|
33
|
+
verb: string | null;
|
|
34
|
+
path: string;
|
|
35
|
+
detail: string;
|
|
36
|
+
options: string[];
|
|
37
|
+
red: Red;
|
|
38
|
+
};
|
package/types/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { makeSpine } from "./spine.js";
|
|
2
2
|
export { runPlan } from "./planrun.js";
|
|
3
|
+
export { workBranchName } from "./workbranch.js";
|
|
4
|
+
export { answerReviewDoor } from "./reviewdoor.js";
|
|
3
5
|
export { main as cliMain } from "./cli.js";
|
|
4
6
|
export { ralph, runClose, runStages, CLOSE_FAULTS } from "./ralph.js";
|
|
5
7
|
export { globToPrefix, scanSecrets } from "./validate.js";
|
|
@@ -7,26 +9,24 @@ export { validateJob, jobSpecHash, checkApproval, checkMenu, resolveWorkerModel,
|
|
|
7
9
|
export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS, closeStagesOf } from "./plan.js";
|
|
8
10
|
export { snapshotScope, evalExits } from "./exits.js";
|
|
9
11
|
export { runJob, ACCOUNTED_ROUND_TYPES } from "./run.js";
|
|
10
|
-
export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists, QUARANTINED_VERDICTS,
|
|
12
|
+
export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists, QUARANTINED_VERDICTS, quarantinesCredit, newestEligibleVersion, reuseEligibility, recordDoor } from "./bridges.js";
|
|
11
13
|
export { renderListing, selectionPrompt } from "./selection.js";
|
|
12
|
-
export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate, applyDoorDecision, writeRunGreenRow
|
|
14
|
+
export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate, applyDoorDecision, writeRunGreenRow } from "./reuse.js";
|
|
13
15
|
export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES, VOUCHED_RATE_SOURCES, rateProvenance, spendProvenance } from "./ledger.js";
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
20
|
-
export {
|
|
21
|
-
export {
|
|
22
|
-
export {
|
|
23
|
-
export {
|
|
24
|
-
export { authorClose, authorPrompt, declarationSchema, makeLoopGenerate, MAX_REVISIONS, QUESTION_SETS, GREEN_QUESTIONS, SOFTGREEN_QUESTIONS, CLASS_STATEMENTS, questionsFor, requiredAnswersFor } from "./authorflow.js";
|
|
25
|
-
export { validateCloseDecl, runDeclaredStages, declaredStages, isDeclaredClose, guardNames, closeGrade, DECLARED_GENRES, DECLARED_GAP_PREFIX, DECLARED_GAP_KEEP, DECLARED_CLOSE_CLASSES, CLOSE_DECL_FIELDS, HUMAN_PAUSE, HITL_PAUSE, HITL_DECISION_RED, HUMAN_CHECKPOINTS } from "./declaredclose.js";
|
|
16
|
+
export { readShimArm, READ_SHIM_CAP, READ_SHIM_STRATEGY, READ_SHIM_DIFF_STRATEGY } from "./readshim.js";
|
|
17
|
+
export { runDeclaredClose, runStage, seedRead, seedAtHead, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, HUMAN_DECISIONS, SEED_EXEMPT_KINDS, normalizeHumanRuling, resolveHumanRuling, JUDGE_ATTEMPTS, MAX_JUDGED_PATHS, DOOR_OPEN_OUTCOMES, REVIEW_DOOR_CLASSES, doorOpens, mechanicalStages } from "./kinds.js";
|
|
18
|
+
export { JUDGE_MODEL, resolveJudge, JUDGE_RULES, JUDGE_RULE_IDS, runLocate, decide, CALIBRATION_SIZE, validateCalibrationSet, expectedOf } from "./judged.js";
|
|
19
|
+
export { proposeJudgedArtifacts, signJudgedArtifacts, foldJudgedArtifacts } from "./cardauthor.js";
|
|
20
|
+
export { runCalibration, INJECTION_LOCATE_BATTERY, CASUALTY_AXES } from "./calibrate.js";
|
|
21
|
+
export { validateDeclaration, KIND_CATALOGUE, LOCKED_KINDS, genreEnv, genreInstruments, LOCKED_CLASSES, CLASS_BATTERIES, classGuards, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
|
|
22
|
+
export { runAuthorScout, buildSeedListing, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SCOUT_RETRY_CAUSES } from "./authorscout.js";
|
|
23
|
+
export { scrubRaw, RAW_PERSIST_MAX } from "./text.js";
|
|
24
|
+
export { authorClose, MAX_REVISIONS, questionsFor, requiredAnswersFor } from "./authorflow.js";
|
|
25
|
+
export { validateCloseDecl, runDeclaredStages, declaredStages, HUMAN_PAUSE } from "./declaredclose.js";
|
|
26
26
|
export { runBehaviour, formatBehaviour } from "./behaviour.js";
|
|
27
27
|
export { replayRun, formatReplay } from "./replay.js";
|
|
28
|
-
export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind,
|
|
28
|
+
export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind, AUTHORED_SPEC_FIELDS } from "./authorjob.js";
|
|
29
29
|
export { PROMPT_REGISTERS, isPromptFile } from "./promptregisters.js";
|
|
30
30
|
export { exportBundle, bundleHash, readBundle, resolveBundleSpec, checkEnvelope, bless, verifyBlessing, appendHistory, checkBundleDeps } from "./bundle.js";
|
|
31
31
|
export { readCloseScripts, checkCloseAbsolutePaths, absolutePathLiteralsOf, hashCloseScriptBytes, checkCloseByteSignature, checkStageByteSignature, signCloseScripts } from "./close-integrity.js";
|
|
32
|
-
export { prepareSource, proveDestination, copyOut, readSourceManifest, frontDoorFromManifest, datedDestination, pickDelivery } from "./source.js";
|
|
32
|
+
export { prepareSource, proveDestination, copyOut, readSourceManifest, frontDoorFromManifest, datedDestination, pickDelivery, missingDependencies } from "./source.js";
|
package/types/job.d.ts
CHANGED
|
@@ -4,6 +4,27 @@
|
|
|
4
4
|
* against the worker-surface package, a locked VERDICT type is demand against
|
|
5
5
|
* bareloop's OWN menu. Inferring it downstream is how a bareloop refusal gets
|
|
6
6
|
* filed as an upstream bug (the BA-2 misattribution class). */
|
|
7
|
+
/**
|
|
8
|
+
* The `baseUrl` SHAPE rule, standalone (PRD item 28 ruling (d); PRD item 33
|
|
9
|
+
* close-out, "authoring provider selectable" — a bad `--base-url` used to
|
|
10
|
+
* refuse only at the end of the interview, through `validateJob`, after every
|
|
11
|
+
* question had already been asked). ONE spelling of the rule: `validateJob`
|
|
12
|
+
* below calls this, and so does `scripts/run-interview.mjs`, at flag-parse
|
|
13
|
+
* time, before it asks anything.
|
|
14
|
+
*
|
|
15
|
+
* No network access, so this checks SHAPE only: `https://` for a real
|
|
16
|
+
* endpoint, or `http://` restricted to a loopback host (a local
|
|
17
|
+
* OpenAI-compatible server, the only legitimate reason to skip TLS) — and
|
|
18
|
+
* never a credential embedded IN the URL itself. A URL travels into logs and
|
|
19
|
+
* spine records far more casually than a header ever would, and secrets never
|
|
20
|
+
* enter the tree/spine (hard line #3) — a baseUrl carrying `user:pass@host`
|
|
21
|
+
* is exactly that leak, just spelled differently. The returned message never
|
|
22
|
+
* echoes the value it judged — it is printed as-is by both callers, and a
|
|
23
|
+
* value that failed for carrying credentials must not repeat them back.
|
|
24
|
+
* @param {unknown} value
|
|
25
|
+
* @returns {string|null} a red `detail`, or `null` when the value is admitted
|
|
26
|
+
*/
|
|
27
|
+
export function validateBaseUrl(value: unknown): string | null;
|
|
7
28
|
/**
|
|
8
29
|
* Validate an operator-owned job spec (`schema: "job-v1"`). Never throws on
|
|
9
30
|
* JSON text or plain parsed data — the ingest contract; a live object with a
|
|
@@ -209,8 +230,9 @@ export const CADENCE_UNITS: readonly string[];
|
|
|
209
230
|
* rows on the cost axis, and budgets sized for one do not transfer. */
|
|
210
231
|
/** PRD item 28 (2026-09-06/09-09): `openai-api` — an OpenAI-shaped provider,
|
|
211
232
|
* constructed via `src/providers.js`'s factory. hamr's ruling (PRD 30.7):
|
|
212
|
-
* `deepseek-
|
|
213
|
-
* models
|
|
233
|
+
* `deepseek-flash` is THE secondary provider (one, not a menu of half-tested
|
|
234
|
+
* models; F171, item 34 L16: swapped from the retired `deepseek-chat`); the
|
|
235
|
+
* factory's tier table is where that lives, not this menu.
|
|
214
236
|
*
|
|
215
237
|
* PRD item 31.3 (hamr, 2026-09-09: *"anthropic, openai, gemini drop ollama for
|
|
216
238
|
* now"*): `gemini-api` joins. That is bareloop matching bare-agent's own
|
package/types/planrun.d.ts
CHANGED
|
@@ -73,7 +73,6 @@
|
|
|
73
73
|
* No gap → the empty string, so the brief renders byte-identically to the pre-F86
|
|
74
74
|
* one. A labelled empty section would be an invitation to explain an absence the
|
|
75
75
|
* run never observed (a stall never judged its exits at all).
|
|
76
|
-
*
|
|
77
76
|
* @param {string | null | undefined} gap the step's last exit gap text
|
|
78
77
|
* @returns {string} the labelled block, or '' when there is nothing to show
|
|
79
78
|
*/
|
|
@@ -473,6 +472,47 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
|
|
|
473
472
|
} | null | undefined;
|
|
474
473
|
resumable?: boolean | undefined;
|
|
475
474
|
}): Promise<string>;
|
|
475
|
+
/**
|
|
476
|
+
* The runtime belt for F178 (`.git`) and F177's follow-up (`node_modules`) —
|
|
477
|
+
* hamr's option B ruling (2026-09-14): block WRITING to either, never reading.
|
|
478
|
+
* bareguard's `fs.deny` cannot be used for this: it blocks ALL fs actions,
|
|
479
|
+
* reads included (node_modules/bareguard/src/primitives/fs.js:43 "paths/
|
|
480
|
+
* prefixes denied for all fs actions", checked at :62-66 before the read/write
|
|
481
|
+
* split) — putting `.git` there (the first version of this fix) silently also
|
|
482
|
+
* blocked READING it, which was never ruled, and the worker's persona never
|
|
483
|
+
* names `.git`/`node_modules` as a denied book the way it does the gate audit
|
|
484
|
+
* / `.smoke` / `.litectx` (`tests/tools.test.js`'s persona/fence-drift guard
|
|
485
|
+
* caught exactly this in the full gate). `fs.deny` is exact prefix-containment
|
|
486
|
+
* only anyway — no glob, no path-segment match — confirmed by reading
|
|
487
|
+
* `within()` (node_modules/bareguard/src/primitives/fs.js:26-31): `p === b ||
|
|
488
|
+
* p.startsWith(b + '/')` against each configured entry, nothing else; it
|
|
489
|
+
* could not express `node_modules`-at-any-depth even if reads were not a
|
|
490
|
+
* problem (`node_modules/bareguard/src/glob.js`'s `matchAny`/`globToRegex`
|
|
491
|
+
* exist in the same package but `fs.js` never imports them).
|
|
492
|
+
*
|
|
493
|
+
* `tools.denyArgPatterns` (`node_modules/bareguard/src/primitives/tools.js`,
|
|
494
|
+
* step 3 of `Gate#_stepEval` — the SAME step `fsCheck` runs at, checked right
|
|
495
|
+
* after it in the `??` chain, `node_modules/bareguard/src/gate.js`) is the
|
|
496
|
+
* supported hook used instead: a RegExp tested against
|
|
497
|
+
* `JSON.stringify(action)`, keyed by `action.type` so it is scoped to
|
|
498
|
+
* `write`/`edit` ONLY — a `read` action never reaches this pattern at all.
|
|
499
|
+
* Because `fsCheck` runs first in the same `??` chain, this pattern is
|
|
500
|
+
* reached only when the path already passed `writeScope`/`deny` — exactly the
|
|
501
|
+
* residual case (an in-fence write/edit whose path still carries a `.git` or
|
|
502
|
+
* `node_modules` segment; `.git` itself is never IN a signed fence in the
|
|
503
|
+
* first place, `scopeContained` in src/validate.js refuses that at
|
|
504
|
+
* declaration time — this is the belt for a validator regression). Matched
|
|
505
|
+
* whole-segment (a look-alike like `.github`/`.gitignore`/`my.git`/
|
|
506
|
+
* `node_modules_util` stays admitted). Safe to match the WHOLE serialized
|
|
507
|
+
* action (not just the `path` field in isolation) because bareloop's own
|
|
508
|
+
* action shape (`toolAction`, src/tools.js) never carries a write's byte
|
|
509
|
+
* CONTENT in the action object — `args.bytes` is a length, not the text — so
|
|
510
|
+
* there is no written-file-content collision risk the way there would be for
|
|
511
|
+
* bareguard's own `content.denyPatterns` primitive (which explicitly strips
|
|
512
|
+
* payload fields for exactly that reason).
|
|
513
|
+
* @type {RegExp}
|
|
514
|
+
*/
|
|
515
|
+
export const FORBIDDEN_WRITE_SEGMENT_PATTERN: RegExp;
|
|
476
516
|
/**
|
|
477
517
|
* The recorded bound-reason's ceiling in the WORKER-facing note. The reason is a
|
|
478
518
|
* provider string (`denied:<tool>` today, ~20 bytes), not a gap, so this is a
|
package/types/providers.d.ts
CHANGED
|
@@ -8,6 +8,34 @@
|
|
|
8
8
|
* @returns {ProviderTableEntry}
|
|
9
9
|
*/
|
|
10
10
|
export function resolveProvider(providerName: string): ProviderTableEntry;
|
|
11
|
+
/**
|
|
12
|
+
* Report whether a resolved API key VALUE (not its presence — every call
|
|
13
|
+
* site already presence-checks separately) carries a shape an HTTP header
|
|
14
|
+
* value cannot: F181 (`docs/logs/FINDINGS.md`) — a two-line secret-store
|
|
15
|
+
* entry put the key plus a trailing metadata line into `OPENAI_API_KEY`
|
|
16
|
+
* (`"<key>\nmeta"`), the presence-only check at every door passed, and Node
|
|
17
|
+
* crashed `TypeError [ERR_INVALID_CHAR]` inside its own header-encode at the
|
|
18
|
+
* FIRST paid call — after the run had already announced itself as spending,
|
|
19
|
+
* inside the paid span. curl tolerated the same value (HTTP 200 twice), so a
|
|
20
|
+
* curl probe is a blind instrument for this defect class.
|
|
21
|
+
*
|
|
22
|
+
* NEVER TRIMS OR REPAIRS — this reports a reason, it does not fix the value
|
|
23
|
+
* (hamr's retry-never-repair rule, in spirit: refuse loud, don't guess at
|
|
24
|
+
* what the operator's secret store meant). Every call site that gets a
|
|
25
|
+
* non-null reason back refuses at $0, in the SAME place and the SAME way it
|
|
26
|
+
* already refuses a missing key — this function only adds a second shape of
|
|
27
|
+
* refusal to a door that already had one.
|
|
28
|
+
*
|
|
29
|
+
* The checked shape is deliberately wider than what Node's own header
|
|
30
|
+
* encoder rejects (Node tolerates a bare tab in a header value): CR, LF, TAB,
|
|
31
|
+
* every other C0/DEL control character, and leading/trailing whitespace all
|
|
32
|
+
* refuse, because every one of them is evidence the resolved value is not a
|
|
33
|
+
* clean single-line secret — the same class of "reads as one plausible thing,
|
|
34
|
+
* is actually stale/wrong/multi-line" this finding named.
|
|
35
|
+
* @param {string} value the resolved key value — never logged, never echoed by any caller
|
|
36
|
+
* @returns {string|null} a plain-English reason class, or `null` when the value is clean
|
|
37
|
+
*/
|
|
38
|
+
export function apiKeyProblem(value: string): string | null;
|
|
11
39
|
/**
|
|
12
40
|
* Construct ONE provider instance for `providerName`/`model`, applying
|
|
13
41
|
* `exposeErrorBody: true` (F153 — every constructed provider, unconditionally)
|
|
@@ -108,10 +136,17 @@ export const ANTHROPIC_TIER_MODELS: Readonly<{
|
|
|
108
136
|
* of a single-model provider, not a second model masquerading as `haiku`.
|
|
109
137
|
* If DeepSeek (or a future openai-api entrant) ever ships a genuinely
|
|
110
138
|
* cheaper tier model, this is where it would earn its own `haiku` id.
|
|
139
|
+
*
|
|
140
|
+
* MODEL SWAP (F171, item 34 L16, hamr 2026-09-13): `deepseek-chat` is no
|
|
141
|
+
* longer served — a live `/models` call returned only `deepseek-flash` and
|
|
142
|
+
* `deepseek-v4-pro`. Switched to `deepseek-flash` (DeepSeek V4.1). F149's
|
|
143
|
+
* `legacyMaxTokens` measurement (asked 64 output tokens, got 665/608) was
|
|
144
|
+
* taken on `deepseek-chat` and is UNVERIFIED on `deepseek-flash` — kept on
|
|
145
|
+
* below as the fail-safe (over-capping direction is safe; see F171).
|
|
111
146
|
*/
|
|
112
147
|
export const OPENAI_TIER_MODELS: Readonly<{
|
|
113
|
-
sonnet: "deepseek-
|
|
114
|
-
haiku: "deepseek-
|
|
148
|
+
sonnet: "deepseek-flash";
|
|
149
|
+
haiku: "deepseek-flash";
|
|
115
150
|
}>;
|
|
116
151
|
/**
|
|
117
152
|
* Gemini (PRD item 31.3, hamr 2026-09-09: "anthropic, openai, gemini drop
|
package/types/source.d.ts
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walk from `startDir` upward — inclusive — to the NEAREST ancestor whose own
|
|
3
|
+
* directory carries a `.git` entry, whether that entry is a real repo
|
|
4
|
+
* directory or a linked-worktree/submodule FILE (the caller decides what a
|
|
5
|
+
* file means for it). `null` when no ancestor up to the filesystem root has
|
|
6
|
+
* one. ONE helper for "find the repo boundary walking up", shared by
|
|
7
|
+
* {@link looksLikeRepoSource} and `prepareSource`'s own authoritative routing
|
|
8
|
+
* below (PRD item 33 M3 ruling 2 addendum, 2026-09-13: a Source that is a
|
|
9
|
+
* SUBFOLDER inside a git repo is a repo job, not a plain-folder one — the
|
|
10
|
+
* boundary is the nearest ancestor's `.git`, never only a `.git` sitting
|
|
11
|
+
* directly inside Source itself) and reused by `src/detectlang.js`'s own
|
|
12
|
+
* upward walk (`walkChain`), which only needs the boundary directory, not
|
|
13
|
+
* which kind of `.git` it is — never three copies of this rule.
|
|
14
|
+
* @param {string} startDir an absolute directory to start the walk FROM
|
|
15
|
+
* @returns {{dir: string, isFile: boolean}|null}
|
|
16
|
+
*/
|
|
17
|
+
export function nearestGitAncestor(startDir: string): {
|
|
18
|
+
dir: string;
|
|
19
|
+
isFile: boolean;
|
|
20
|
+
} | null;
|
|
21
|
+
/**
|
|
22
|
+
* A CHEAP peek (a stat, never a read) at whether `source` is going to turn
|
|
23
|
+
* out to be a REPO source — the ONE rule for that question, shared by
|
|
24
|
+
* `prepareSource` (which routes the destination check below before the real
|
|
25
|
+
* walk further down decides authoritatively) and `scripts/run-interview.mjs`
|
|
26
|
+
* (PRD item 33 M3 ruling 2), which has to know BEFORE Source is even frozen
|
|
27
|
+
* whether Destination fills the write fence or a delivery directory. Never a
|
|
28
|
+
* second, hand-typed copy of this test: a URL is never a repo; a local path
|
|
29
|
+
* is a repo source only when it is a DIRECTORY, and the NEAREST ancestor
|
|
30
|
+
* (Source itself, or an ancestor above it — ruling 2's 2026-09-13 addendum)
|
|
31
|
+
* carries a `.git` DIRECTORY. A nearest `.git` that is a FILE (a linked
|
|
32
|
+
* worktree or a submodule) is NOT repo-like here — the real walk below
|
|
33
|
+
* refuses `source-is-linked-worktree` for that case, and this peek must
|
|
34
|
+
* agree closely enough to route the destination question correctly rather
|
|
35
|
+
* than offer the write-fence wording for a source that is about to be
|
|
36
|
+
* refused. An unreadable path is reported properly by the real walk; this
|
|
37
|
+
* peek only has to agree with that walk closely enough to route correctly,
|
|
38
|
+
* never to replace it.
|
|
39
|
+
* @param {string} source
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function looksLikeRepoSource(source: string): boolean;
|
|
1
43
|
/**
|
|
2
44
|
* Build the frozen source tree — `<into>/tree/input/…` (the copy), `<into>/tree/output/`
|
|
3
45
|
* (empty, where the run writes), `<into>/source.json` (the manifest, OUTSIDE
|
|
@@ -46,6 +88,51 @@ export function prepareSource({ source, into, destination, fetchTimeoutMs }: {
|
|
|
46
88
|
manifestPath: string;
|
|
47
89
|
manifest: object;
|
|
48
90
|
} | SourceRefusal>;
|
|
91
|
+
/**
|
|
92
|
+
* THE INSTALL-GAP DETECTOR (PRD item 33 close-out, `docs/product/PRD.md:450-452`,
|
|
93
|
+
* hamr's ruling 2026-09-14, option A): `prepareSource` copies ONLY git-tracked
|
|
94
|
+
* files (hamr's ruling, `docs/product/ITEM33-BUILD.md:225-239` — unchanged by
|
|
95
|
+
* this function), so a JS/TS repo's prepared copy never carries `node_modules`,
|
|
96
|
+
* and every close stage needing a tool (`tsc`, a test runner) instrument-stops
|
|
97
|
+
* with nothing telling the person why. bareloop NEVER runs an install itself —
|
|
98
|
+
* it only ever touches gated primitives, and a shell-out to `npm`/etc from
|
|
99
|
+
* inside this module would be exactly the verb hamr's law reserves for the
|
|
100
|
+
* worker's own granted tools, never the arbiter-side machinery that decides
|
|
101
|
+
* whether a run even starts. This is a pure, $0 DETECTOR: it names the gap and
|
|
102
|
+
* the exact command a person runs THEMSELVES in the copy, then returns.
|
|
103
|
+
*
|
|
104
|
+
* JS/TS ONLY for now — a repo in another detected language returns `null`
|
|
105
|
+
* here unconditionally (no `package.json` to find), which is the correct
|
|
106
|
+
* "nothing to report" reading, not a gap in this function; per-language
|
|
107
|
+
* dependency-gap data for other languages is M3b's job
|
|
108
|
+
* (`docs/product/ITEM33-BUILD.md`, "M3b"), not this one's.
|
|
109
|
+
*
|
|
110
|
+
* SUBFOLDER-AS-REPO (ruling 2 addendum, commit 2a020d8): a subfolder job
|
|
111
|
+
* freezes the WHOLE repo, so `package.json` may sit at the repo root, at the
|
|
112
|
+
* subfolder itself, or anywhere between — the SAME nearest-manifest-walking-up
|
|
113
|
+
* rule `detectLanguage` (`src/detectlang.js`) applies, done independently here
|
|
114
|
+
* (not by importing `detectLanguage`) to avoid a `source.js` ⇄ `detectlang.js`
|
|
115
|
+
* import cycle, since `detectlang.js` already imports `nearestGitAncestor`
|
|
116
|
+
* from this module.
|
|
117
|
+
*
|
|
118
|
+
* @param {string} treeDir the prepared tree's root (`prepareSource`'s
|
|
119
|
+
* returned `tree`, e.g. `<into>/tree` — a REPO source sits directly at this
|
|
120
|
+
* root; every other kind sits under `input/` inside it and has no repo
|
|
121
|
+
* dependency story yet, so callers only need this for a `kind: 'repo'`
|
|
122
|
+
* manifest)
|
|
123
|
+
* @param {string} [sourceSubdir] the manifest's own `sourceSubdir` ('' or
|
|
124
|
+
* undefined when Source IS the repo root)
|
|
125
|
+
* @returns {{manager: string, command: string, reason: string}|null} `null`
|
|
126
|
+
* when there is nothing to report: no JS/TS manifest found walking up from
|
|
127
|
+
* `sourceSubdir` to `treeDir`, the nearest one found has no dependencies,
|
|
128
|
+
* `node_modules` is already there, or the manifest cannot even be read as
|
|
129
|
+
* JSON (an invalid `package.json` is "cannot tell", never a false alarm).
|
|
130
|
+
*/
|
|
131
|
+
export function missingDependencies(treeDir: string, sourceSubdir?: string): {
|
|
132
|
+
manager: string;
|
|
133
|
+
command: string;
|
|
134
|
+
reason: string;
|
|
135
|
+
} | null;
|
|
49
136
|
/**
|
|
50
137
|
* The delivered name (M2b fix 4, kept under D3): a destination FILE of
|
|
51
138
|
* `/home/me/profile.md` lands as `/home/me/profile-2026-09-12.md`, and a
|
package/types/text.d.ts
CHANGED
|
@@ -100,6 +100,41 @@ export function capStop({ ceilingUsd, knownUsd, spendComplete }: {
|
|
|
100
100
|
knownUsd: number;
|
|
101
101
|
spendComplete: boolean;
|
|
102
102
|
}): "cap-halt" | "pricing-red" | null;
|
|
103
|
+
/**
|
|
104
|
+
* THE ONE SPELLING of "was this throw a call casualty, or something else" —
|
|
105
|
+
* F179/F180's seam. `authorscout.js`'s `settled` narrows a rejected
|
|
106
|
+
* `loop.run()` to this one class: swallowing every throw would launder a
|
|
107
|
+
* budget `HaltError`, a programming bug, or any other named terminal into
|
|
108
|
+
* "the call failed" — the exact class of dishonesty this repo refuses (an
|
|
109
|
+
* unknown reported as a known).
|
|
110
|
+
*
|
|
111
|
+
* bare-agent 0.42.0's `OpenAIProvider.generate` used to throw a raw
|
|
112
|
+
* `SyntaxError` from an unguarded `JSON.parse(tc.function.arguments)` after
|
|
113
|
+
* a billed HTTP round (F179/BA-27) — this predicate used to admit that class
|
|
114
|
+
* too. bare-agent 0.43.0 fixed it upstream (`parseToolCalls`, shared by
|
|
115
|
+
* OpenAIProvider and Ollama): a malformed tool-call arguments string now
|
|
116
|
+
* returns a priced round with `toolCalls: []` plus its own `malformedToolCall`
|
|
117
|
+
* marker instead of throwing. Verified against 0.43.0's installed source that
|
|
118
|
+
* no provider bareloop constructs (`AnthropicProvider`, `OpenAIProvider`,
|
|
119
|
+
* `GeminiProvider` — `src/providers.js`) can still throw a JSON `SyntaxError`
|
|
120
|
+
* out of `generate()` after a billed round: Anthropic and Gemini read tool-call
|
|
121
|
+
* arguments as already-parsed objects off their own response shape (nothing to
|
|
122
|
+
* `JSON.parse`), and every provider's raw-HTTP-body parse is already
|
|
123
|
+
* try/catch-wrapped inside `_request`, converted to a plain `Error` before it
|
|
124
|
+
* can reach here. bareloop never streams a provider response. So the
|
|
125
|
+
* SyntaxError branch this predicate used to carry is dead code and was
|
|
126
|
+
* removed in the same change that bumped to bare-agent 0.43.0 — re-add it (or
|
|
127
|
+
* a narrower successor) only if a real provider is found to reintroduce the
|
|
128
|
+
* class.
|
|
129
|
+
*
|
|
130
|
+
* A `HaltError` is deliberately excluded — it is bare-agent's own governance
|
|
131
|
+
* exit (a budget/turn cap), never a transport casualty, and this predicate
|
|
132
|
+
* must never launder one into a mere "the call failed".
|
|
133
|
+
* @param {any} err
|
|
134
|
+
* @returns {string|null} a short reason when `err` is an admitted casualty class,
|
|
135
|
+
* `null` for everything else (including `HaltError`) — the caller re-throws on `null`.
|
|
136
|
+
*/
|
|
137
|
+
export function callCasualty(err: any): string | null;
|
|
103
138
|
/**
|
|
104
139
|
* ONE raw model emission, ready to persist.
|
|
105
140
|
*
|
package/types/validate.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export function globToPrefix(scope: string): string;
|
|
|
13
13
|
* resolve to a PROPER subdirectory of the run directory. Absolute paths and
|
|
14
14
|
* ".." segments escape it; "." / "./**" cover the whole run directory — where
|
|
15
15
|
* the close suite lives. Windows spellings ("..\", "C:\") count as escapes.
|
|
16
|
+
* A `.git` or `node_modules` segment anywhere in the prefix is refused too
|
|
17
|
+
* (F178/F177, see {@link FORBIDDEN_SCOPE_SEGMENTS}).
|
|
16
18
|
* Exported for the job validator: the operator's outer fence obeys the SAME
|
|
17
19
|
* law through the same code — two containment transforms would be the F9
|
|
18
20
|
* red-class one level up.
|
|
@@ -205,6 +207,26 @@ export function hasNestedQuantifier(src: string): boolean;
|
|
|
205
207
|
* @param {(code: string, path: string, detail?: string) => void} red
|
|
206
208
|
*/
|
|
207
209
|
export function sweepNestedQuantifiers(root: any, red: (code: string, path: string, detail?: string) => void): void;
|
|
210
|
+
/**
|
|
211
|
+
* Path segments a fence/scope may never contain, checked WHOLE-SEGMENT
|
|
212
|
+
* (`prefix.split('/')`), never substring — a look-alike (`.github`,
|
|
213
|
+
* `.gitignore`, `my.git`, `node_modules_util`) is a DIFFERENT name and stays
|
|
214
|
+
* admitted. hamr's ruling (option B, 2026-09-14, block both): F178 — a
|
|
215
|
+
* signed fence naming `.git` could let a worker write git's own files (refs,
|
|
216
|
+
* the seed, `info/exclude`) and hide those writes from `changedSet`. F177's
|
|
217
|
+
* follow-up — a signed fence naming `node_modules` directly is the same class
|
|
218
|
+
* of gap (an installed/worker-written file inside it is invisible to
|
|
219
|
+
* `changedSet` the same way, and installs are the person's job now, F177).
|
|
220
|
+
* This catches only a fence that NAMES one of these directly; a fence like
|
|
221
|
+
* `packages/api/**` that later reaches a NESTED `node_modules` beneath it is a
|
|
222
|
+
* distinct, only partially closeable gap — see the runtime belt in
|
|
223
|
+
* planrun.js for `node_modules` (`.git` has no such gap: there is exactly one
|
|
224
|
+
* `.git` per repo, always at the workdir root, so its own runtime belt is a
|
|
225
|
+
* single `fs.deny` entry). Both are inexpressible at declaration time, the
|
|
226
|
+
* same law #1 already applies to `..`.
|
|
227
|
+
* @type {readonly string[]}
|
|
228
|
+
*/
|
|
229
|
+
export const FORBIDDEN_SCOPE_SEGMENTS: readonly string[];
|
|
208
230
|
/**
|
|
209
231
|
* PRD item 27/M2 close-bytes signature, orchestrator-flagged gap
|
|
210
232
|
* (2026-09-06): a close stage's `cmd` can name an addressable script TWO
|