loopforge 3.9.4 → 4.0.7
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/README.md +236 -112
- package/dist/audit.d.ts +73 -15
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +131 -61
- package/dist/audit.js.map +1 -1
- package/dist/canonical-state.d.ts +114 -40
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +276 -182
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +41 -12
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-governance.d.ts +0 -2
- package/dist/cognitive-governance.d.ts.map +1 -1
- package/dist/cognitive-governance.js +0 -4
- package/dist/cognitive-governance.js.map +1 -1
- package/dist/committed-round.d.ts +97 -156
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +223 -419
- package/dist/committed-round.js.map +1 -1
- package/dist/constraint-lifecycle.d.ts +73 -0
- package/dist/constraint-lifecycle.d.ts.map +1 -0
- package/dist/constraint-lifecycle.js +174 -0
- package/dist/constraint-lifecycle.js.map +1 -0
- package/dist/context-boundary.d.ts +83 -0
- package/dist/context-boundary.d.ts.map +1 -0
- package/dist/context-boundary.js +101 -0
- package/dist/context-boundary.js.map +1 -0
- package/dist/contract-items.d.ts +12 -8
- package/dist/contract-items.d.ts.map +1 -1
- package/dist/contract-items.js +31 -26
- package/dist/contract-items.js.map +1 -1
- package/dist/contract-requirement.d.ts +47 -0
- package/dist/contract-requirement.d.ts.map +1 -0
- package/dist/contract-requirement.js +51 -0
- package/dist/contract-requirement.js.map +1 -0
- package/dist/criterion-status.d.ts +35 -0
- package/dist/criterion-status.d.ts.map +1 -0
- package/dist/criterion-status.js +89 -0
- package/dist/criterion-status.js.map +1 -0
- package/dist/enforcement-gate.d.ts +26 -38
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +196 -319
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +9 -103
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +27 -897
- package/dist/engine.js.map +1 -1
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/evidence-claims.d.ts +33 -31
- package/dist/evidence-claims.d.ts.map +1 -1
- package/dist/evidence-claims.js +58 -72
- package/dist/evidence-claims.js.map +1 -1
- package/dist/evidence-posture-view.d.ts +19 -0
- package/dist/evidence-posture-view.d.ts.map +1 -0
- package/dist/evidence-posture-view.js +116 -0
- package/dist/evidence-posture-view.js.map +1 -0
- package/dist/evidence-posture.d.ts +73 -0
- package/dist/evidence-posture.d.ts.map +1 -0
- package/dist/evidence-posture.js +281 -0
- package/dist/evidence-posture.js.map +1 -0
- package/dist/evidence-provider.d.ts +55 -37
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +182 -332
- package/dist/evidence-provider.js.map +1 -1
- package/dist/explain.d.ts +166 -27
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +372 -58
- package/dist/explain.js.map +1 -1
- package/dist/field-accounting.d.ts.map +1 -1
- package/dist/field-accounting.js +3 -22
- package/dist/field-accounting.js.map +1 -1
- package/dist/generate-schema.js +13 -8
- package/dist/generate-schema.js.map +1 -1
- package/dist/git-boundary.d.ts +128 -0
- package/dist/git-boundary.d.ts.map +1 -0
- package/dist/git-boundary.js +625 -0
- package/dist/git-boundary.js.map +1 -0
- package/dist/git-facts.d.ts +22 -0
- package/dist/git-facts.d.ts.map +1 -0
- package/dist/git-facts.js +29 -0
- package/dist/git-facts.js.map +1 -0
- package/dist/git-motion.d.ts +37 -0
- package/dist/git-motion.d.ts.map +1 -0
- package/dist/git-motion.js +59 -0
- package/dist/git-motion.js.map +1 -0
- package/dist/history.d.ts +44 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +77 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +16 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +80 -14
- package/dist/init.js.map +1 -1
- package/dist/loop-compiler.d.ts +39 -23
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +368 -565
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +6 -5
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +153 -35
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/round-lifecycle.d.ts +255 -81
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +1552 -539
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +42 -18
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +81 -39
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +956 -282
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/submission-boundary.d.ts +23 -31
- package/dist/mcp/submission-boundary.d.ts.map +1 -1
- package/dist/mcp/submission-boundary.js +242 -92
- package/dist/mcp/submission-boundary.js.map +1 -1
- package/dist/mcp/submission-handle.d.ts +66 -0
- package/dist/mcp/submission-handle.d.ts.map +1 -0
- package/dist/mcp/submission-handle.js +173 -0
- package/dist/mcp/submission-handle.js.map +1 -0
- package/dist/mcp/tools.d.ts +17 -7
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +707 -244
- package/dist/mcp/tools.js.map +1 -1
- package/dist/open-question.d.ts +7 -0
- package/dist/open-question.d.ts.map +1 -0
- package/dist/open-question.js +14 -0
- package/dist/open-question.js.map +1 -0
- package/dist/policy-metrics.d.ts +31 -21
- package/dist/policy-metrics.d.ts.map +1 -1
- package/dist/policy-metrics.js +117 -64
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +11 -39
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +12 -12
- package/dist/policy.js.map +1 -1
- package/dist/progress.d.ts +45 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +136 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-assembler.d.ts +20 -6
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +475 -479
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-coverage.d.ts +0 -6
- package/dist/prompt-coverage.d.ts.map +1 -1
- package/dist/prompt-coverage.js +4 -8
- package/dist/prompt-coverage.js.map +1 -1
- package/dist/prompt-freshness.d.ts +6 -17
- package/dist/prompt-freshness.d.ts.map +1 -1
- package/dist/prompt-freshness.js +27 -22
- package/dist/prompt-freshness.js.map +1 -1
- package/dist/prompt-policy.d.ts +14 -7
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +21 -5
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +843 -424
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +73 -102
- package/dist/protocol.js.map +1 -1
- package/dist/recovery-guidance.d.ts +13 -0
- package/dist/recovery-guidance.d.ts.map +1 -0
- package/dist/recovery-guidance.js +15 -0
- package/dist/recovery-guidance.js.map +1 -0
- package/dist/recovery.d.ts +68 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +234 -0
- package/dist/recovery.js.map +1 -0
- package/dist/replay.d.ts +42 -8
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +311 -84
- package/dist/replay.js.map +1 -1
- package/dist/result-facts.d.ts +45 -0
- package/dist/result-facts.d.ts.map +1 -0
- package/dist/result-facts.js +86 -0
- package/dist/result-facts.js.map +1 -0
- package/dist/round-contract.d.ts +38 -8
- package/dist/round-contract.d.ts.map +1 -1
- package/dist/round-contract.js +71 -31
- package/dist/round-contract.js.map +1 -1
- package/dist/round-coordinator.d.ts +41 -89
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +250 -267
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +56 -16
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +225 -39
- package/dist/round-driver.js.map +1 -1
- package/dist/round-facts.d.ts +33 -52
- package/dist/round-facts.d.ts.map +1 -1
- package/dist/round-facts.js +51 -160
- package/dist/round-facts.js.map +1 -1
- package/dist/round-guidance.d.ts +56 -0
- package/dist/round-guidance.d.ts.map +1 -0
- package/dist/round-guidance.js +161 -0
- package/dist/round-guidance.js.map +1 -0
- package/dist/round-outcome.d.ts +21 -0
- package/dist/round-outcome.d.ts.map +1 -0
- package/dist/round-outcome.js +31 -0
- package/dist/round-outcome.js.map +1 -0
- package/dist/round-transaction.d.ts +168 -21
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +837 -38
- package/dist/round-transaction.js.map +1 -1
- package/dist/self-eval.d.ts +37 -61
- package/dist/self-eval.d.ts.map +1 -1
- package/dist/self-eval.js +413 -392
- package/dist/self-eval.js.map +1 -1
- package/dist/storage.d.ts +16 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +54 -4
- package/dist/storage.js.map +1 -1
- package/dist/subgoal-state.d.ts +2 -3
- package/dist/subgoal-state.d.ts.map +1 -1
- package/dist/subgoal-state.js +2 -3
- package/dist/subgoal-state.js.map +1 -1
- package/dist/submission-guidance.d.ts +29 -21
- package/dist/submission-guidance.d.ts.map +1 -1
- package/dist/submission-guidance.js +308 -260
- package/dist/submission-guidance.js.map +1 -1
- package/dist/submission-receipt.d.ts +16 -8
- package/dist/submission-receipt.d.ts.map +1 -1
- package/dist/submission-receipt.js +145 -51
- package/dist/submission-receipt.js.map +1 -1
- package/dist/token-utils.d.ts +28 -7
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +27 -11
- package/dist/token-utils.js.map +1 -1
- package/dist/transaction-identity.d.ts +9 -0
- package/dist/transaction-identity.d.ts.map +1 -0
- package/dist/transaction-identity.js +69 -0
- package/dist/transaction-identity.js.map +1 -0
- package/dist/verification-actions.d.ts.map +1 -1
- package/dist/verification-actions.js +10 -11
- package/dist/verification-actions.js.map +1 -1
- package/dist/verification-gate.d.ts +23 -47
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +202 -423
- package/dist/verification-gate.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +187 -0
- package/dist/working-memory.d.ts.map +1 -0
- package/dist/working-memory.js +767 -0
- package/dist/working-memory.js.map +1 -0
- package/dist/workspace-checkpoint.d.ts +88 -0
- package/dist/workspace-checkpoint.d.ts.map +1 -0
- package/dist/workspace-checkpoint.js +326 -0
- package/dist/workspace-checkpoint.js.map +1 -0
- package/dist/workspace-restore.d.ts +68 -0
- package/dist/workspace-restore.d.ts.map +1 -0
- package/dist/workspace-restore.js +604 -0
- package/dist/workspace-restore.js.map +1 -0
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +0 -17
- package/dist/workspace.js.map +1 -1
- package/loop_policy.json +4 -18
- package/package.json +68 -68
- package/skills/forge/SKILL.md +212 -0
- package/skills/forge/references/evidence-and-contracts.md +204 -0
- package/skills/forge/references/recovery-and-views.md +188 -0
- package/dist/cognitive-facts.d.ts +0 -30
- package/dist/cognitive-facts.d.ts.map +0 -1
- package/dist/cognitive-facts.js +0 -113
- package/dist/cognitive-facts.js.map +0 -1
- package/dist/loop-extras-parser.d.ts +0 -81
- package/dist/loop-extras-parser.d.ts.map +0 -1
- package/dist/loop-extras-parser.js +0 -213
- package/dist/loop-extras-parser.js.map +0 -1
- package/dist/loop-projection.d.ts +0 -6
- package/dist/loop-projection.d.ts.map +0 -1
- package/dist/loop-projection.js +0 -28
- package/dist/loop-projection.js.map +0 -1
- package/skills/perception/SKILL.md +0 -389
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Recovery, Prompt Levels, and Read-Only Views
|
|
2
|
+
|
|
3
|
+
This reference explains concepts and flows. The live `submission_guidance`
|
|
4
|
+
remains the only source for field applicability, legal shapes, real ids, and
|
|
5
|
+
usable commands.
|
|
6
|
+
|
|
7
|
+
## Backtrack and Agent recovery
|
|
8
|
+
|
|
9
|
+
A progress stall requires observable Git history over the lookback window. Only
|
|
10
|
+
the Git workspace coverage participates: a custom evidence provider's
|
|
11
|
+
changed/unknown facts are recorded and displayed but cannot prevent a stall or
|
|
12
|
+
keep the prompt at L1. Committed work counts as motion through the Git HEAD
|
|
13
|
+
change, even when the closing worktree is clean, and the round you are
|
|
14
|
+
submitting counts too: if it moved files or committed, it cancels the verdict.
|
|
15
|
+
If machine history cannot be read, no stall verdict is made.
|
|
16
|
+
|
|
17
|
+
LoopForge can trigger a machine backtrack when a rule requires it. The Agent may
|
|
18
|
+
also abandon the current branch deliberately by submitting a top-level
|
|
19
|
+
`recovery: { target_checkpoint_id, reason }` instead of an `evaluation`, using
|
|
20
|
+
the current `submissionHandle`.
|
|
21
|
+
|
|
22
|
+
Recovery is an action, not an evaluation:
|
|
23
|
+
|
|
24
|
+
- it requires none of the four core evaluation fields;
|
|
25
|
+
- it requires the current handle and a non-empty reason;
|
|
26
|
+
- the target must resolve to a checkpoint ref visible through the held
|
|
27
|
+
transaction or a committed evaluation transaction on the current branch;
|
|
28
|
+
- error-level verification findings on the target round do not disqualify it,
|
|
29
|
+
because it is recovery material, not a success claim;
|
|
30
|
+
- the reason is recorded verbatim and rendered in the Recovery Brief as
|
|
31
|
+
report-only text, never as a LoopForge instruction.
|
|
32
|
+
|
|
33
|
+
LoopForge commits the rollback directive, executes the restore itself, verifies
|
|
34
|
+
the complete Git fingerprint boundary and recorded HEAD, then compiles the redo
|
|
35
|
+
prompt. A failed restore leaves the directive pending, sets the session to
|
|
36
|
+
`stalled`, and `resume` retries the SAME directive. The directive survives the
|
|
37
|
+
redo as an audit event. A restore is not round progress and never satisfies a
|
|
38
|
+
redo-time verification check.
|
|
39
|
+
|
|
40
|
+
When backtrack occurs:
|
|
41
|
+
|
|
42
|
+
1. Read the protected Recovery Brief and the current prompt. Do not restore the
|
|
43
|
+
workspace yourself: LoopForge already did, and re-running git reset/stash/
|
|
44
|
+
checkout would move outside the verified target.
|
|
45
|
+
2. The redo prompt is already compiled from the verified post-restore baseline.
|
|
46
|
+
Submit its work and evidence using the new `submissionHandle` returned with
|
|
47
|
+
it.
|
|
48
|
+
3. Do not repeat a failed approach without addressing its cause. Carry valid
|
|
49
|
+
discoveries into the redo evaluation.
|
|
50
|
+
|
|
51
|
+
If the rolled-back work used an active contract, follow the Recovery Brief and
|
|
52
|
+
live `submission_guidance` for that contract. Do not blindly restate or replace
|
|
53
|
+
it; a successor proposal is subject to the same contract lifecycle rules.
|
|
54
|
+
|
|
55
|
+
## Workspace checkpoints
|
|
56
|
+
|
|
57
|
+
A workspace checkpoint is recovery material, not history. Its authority is the
|
|
58
|
+
`CheckpointRef` inside a transaction snapshot; the content-addressed manifest
|
|
59
|
+
and blobs hold only what HEAD cannot reproduce.
|
|
60
|
+
|
|
61
|
+
- The before-image is captured automatically when a round is prepared.
|
|
62
|
+
- `loopforge_checkpoint` creates an explicit mid-round savepoint. The call is
|
|
63
|
+
the Agent's decision that this state is worth restoring to.
|
|
64
|
+
- A checkpoint creates no round, no commit, and no progress. It is visible as a
|
|
65
|
+
recovery target only through the held transaction's ref list.
|
|
66
|
+
- A successful checkpoint leaves the `submissionHandle` unchanged.
|
|
67
|
+
- Re-capturing the same state is idempotent; distinct savepoints are capped.
|
|
68
|
+
- Checkpoints never feed Canonical State, prompts, acceptance, progress, or
|
|
69
|
+
stall.
|
|
70
|
+
|
|
71
|
+
## Prompt levels
|
|
72
|
+
|
|
73
|
+
L0, L1, and L2 select prompt density only; they never control which evaluation
|
|
74
|
+
fields you may use. Each density can accompany a clean-context model
|
|
75
|
+
conversation or an in-context retry; read `contextMode` and `contextReason`.
|
|
76
|
+
The runtime selection rules and the context boundary live in the prompt's
|
|
77
|
+
`## Round Boundary` section, its context-strategy label, and the MCP server
|
|
78
|
+
instructions; this reference does not restate them.
|
|
79
|
+
|
|
80
|
+
The optional `currentUserInstruction` on `loopforge_next` / `loopforge_resume`
|
|
81
|
+
is invocation input: it is rendered in the prompt and recorded in that prompt's
|
|
82
|
+
artifact, and never rewrites the task, Objective, criteria, constraints or
|
|
83
|
+
Round Contract. Omitting it means the prompt says "not provided"; never infer
|
|
84
|
+
it from the old task or transcript.
|
|
85
|
+
|
|
86
|
+
Every level renders the same protected normative set: active criteria, every
|
|
87
|
+
active constraint, the active contract and all its items, every active sub-goal
|
|
88
|
+
id, every current verification finding, and the committed recovery brief. A
|
|
89
|
+
clean-context prompt also renders the previous committed round's **Machine
|
|
90
|
+
Evidence** when present. Display caps may shorten
|
|
91
|
+
descriptions but never remove ids. If a protected active id is missing,
|
|
92
|
+
LoopForge fails closed with `projection_incomplete` instead of delivering a
|
|
93
|
+
partial prompt. The delivered prompt equals the compiled `renderedPrompt` on
|
|
94
|
+
every path.
|
|
95
|
+
|
|
96
|
+
A clean-context (`fresh_invocation`) prompt may render a bounded **Working
|
|
97
|
+
Memory** section derived from committed history. L1 and L2 render the same full fixed projection: up to
|
|
98
|
+
five reports, eight assumptions, six open questions, three recoveries and
|
|
99
|
+
three contract redirects, with per-item text limits. L0 stays compact (one
|
|
100
|
+
report, two assumptions, two questions, one recovery and one redirect). The
|
|
101
|
+
underlying source window is five committed rounds for reports, assumptions and
|
|
102
|
+
redirects; open questions remain active until resolved, and recovery directives
|
|
103
|
+
have their own fixed limit. These limits are code constants, not policy. The
|
|
104
|
+
default whole-prompt ceilings are
|
|
105
|
+
5,000 / 50,000 / 100,000 characters for L0 / L1 / L2. They are safety ceilings,
|
|
106
|
+
not routine targets for memory omission.
|
|
107
|
+
|
|
108
|
+
Working Memory is optional but INDIVISIBLE: if it cannot fit, it is dropped
|
|
109
|
+
whole, and the artifact records `rendered:false` with source counts. Every
|
|
110
|
+
omission has source/rendered/omitted counts and a deterministic abbreviation
|
|
111
|
+
notice. It declares no normative ids and never changes coverage, level,
|
|
112
|
+
freshness, gates or success. A `same_round_retry` renders neither Working Memory
|
|
113
|
+
nor Machine Evidence at any density; it relies on the rejection notice and
|
|
114
|
+
Verification Gate. A `fresh_invocation` retry with `rejection_escalation` may
|
|
115
|
+
render the previous posture plus the rejected attempt's transient, explicitly
|
|
116
|
+
`not committed` posture. A `fresh_invocation` / `invocation_recovery` prompt is
|
|
117
|
+
the cold replacement of a lost in-context retry for the SAME round/attempt:
|
|
118
|
+
`loopforge_resume(loopId, invocationLost: true)` recompiles it at L2 with the
|
|
119
|
+
rejection notice, findings, previous Machine Evidence, bounded Working Memory
|
|
120
|
+
and the rejected attempt's posture. The state
|
|
121
|
+
file carries the full fixed Working Memory window.
|
|
122
|
+
|
|
123
|
+
## Read-only views
|
|
124
|
+
|
|
125
|
+
Use these to inspect, never to advance:
|
|
126
|
+
|
|
127
|
+
- `loopforge_status` `view: "session"`: live session status, current round,
|
|
128
|
+
the current held `submissionHandle`, `machineBackedSuccessTrajectory`,
|
|
129
|
+
`evidencePosture`/`machineBackedSuccess`, checkpoints, capability, current
|
|
130
|
+
guidance, and the latest committed receipt. Accepts the
|
|
131
|
+
live `sessionId` or the durable `loopId`. A persisted STOPPED loop is
|
|
132
|
+
readable by `loopId` without resuming: it returns `status`, the committed
|
|
133
|
+
`loopStopReason`, the Agent stop facts, `stopDetail` and `last_submission`.
|
|
134
|
+
A running/paused/stalled loop without a live session still means call
|
|
135
|
+
`loopforge_resume`. It never returns the held prompt: a
|
|
136
|
+
null handle, a missing live session, or a lost prompt means call
|
|
137
|
+
`loopforge_resume`. A paused or stalled session may still show its handle, but
|
|
138
|
+
submissions are refused until resume.
|
|
139
|
+
- `loopforge_status` `view: "loop"`: committed health counts.
|
|
140
|
+
- `loopforge_status` `view: "all"`: every persisted loop summary.
|
|
141
|
+
- `loopforge_status` `view: "audit"`: end-of-loop verification completeness.
|
|
142
|
+
- `loopforge_status` `view: "explain"` (with `round`): the per-round "why" view
|
|
143
|
+
over committed facts, including the committed report (even without an
|
|
144
|
+
execution_report), context boundary, assumptions, open questions, blocker,
|
|
145
|
+
the persisted `evidencePosture`, full before/after command observations,
|
|
146
|
+
committed checkpoints and restore marker, and rollback digest facts.
|
|
147
|
+
- `loopforge_replay`: committed round timeline and diffs. Pass `loopId` (or a
|
|
148
|
+
live `sessionId`) and optional `start` / `end`. With no window it returns the
|
|
149
|
+
last 10 committed rounds; an explicit window may span at most 50 rounds. The
|
|
150
|
+
response includes `window: { start, end, totalRounds, hasBefore, hasAfter }`
|
|
151
|
+
and each round's `attempt`, `context_mode`, `context_reason`, `outcome`,
|
|
152
|
+
bounded `report`, `wrong_assumptions`, `retracted_constraints`,
|
|
153
|
+
`open_questions`, `blocker` and `contract_redirect`. `rollbacks` carries
|
|
154
|
+
`failedRounds`, `approaches` and `wrongAssumptions`. Legacy envelope rows stay
|
|
155
|
+
visible instead of disappearing.
|
|
156
|
+
- `loopforge_status` `view: "session"` / `view: "loop"` / `view: "audit"`: live
|
|
157
|
+
and committed health views.
|
|
158
|
+
|
|
159
|
+
The optional `.loopforge/state/<loopId>-state.md` is derived and regenerable.
|
|
160
|
+
Read it when the prompt points to it (current governance + bounded Working
|
|
161
|
+
Memory + diagnostic views), but never treat it as a second source of truth:
|
|
162
|
+
committed typed rounds are authoritative, the file's Agent-reported summaries
|
|
163
|
+
are claims, and a state hash that differs from the prompt header means the file
|
|
164
|
+
is stale. Re-align with status or resume.
|
|
165
|
+
|
|
166
|
+
## Gates and delegation
|
|
167
|
+
|
|
168
|
+
The two gate tools are opt-in through `policy.gate.enabled`. When enabled:
|
|
169
|
+
|
|
170
|
+
- `loopforge_gate_check` preflights a structured action with its description,
|
|
171
|
+
scope, effects, reversibility, and authorization. It takes the current
|
|
172
|
+
`submissionHandle` and the action; it never executes or approves anything.
|
|
173
|
+
- Classification is conservative: anything not provably safe becomes
|
|
174
|
+
`user_required`.
|
|
175
|
+
- Present the approval question, record the decision with
|
|
176
|
+
`loopforge_gate_resolve` (`sessionId` + `gateId`), and cite the approved gate
|
|
177
|
+
id in `evaluation.gate_ids`.
|
|
178
|
+
- LoopForge records the process, but cannot prove a human was present because
|
|
179
|
+
the Agent submits the resolve call.
|
|
180
|
+
- Safe investigation and dry-runs need no gate.
|
|
181
|
+
|
|
182
|
+
Delegation does not create another LoopForge mode. Give each worker a
|
|
183
|
+
self-contained subtask and hard constraints, then place the result in
|
|
184
|
+
`worker_results` with `agentId`, `subTask`, `resultSummary`, and `outcome`.
|
|
185
|
+
The result is saved with the committed transaction as an audit report. It does
|
|
186
|
+
not enter the global constraint set by itself. If you accept a worker's finding,
|
|
187
|
+
state it in top-level `discovered_constraints`; that is the only promotion
|
|
188
|
+
channel.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/** Shared cognitive facts used by typed projections and handoff views. */
|
|
2
|
-
import type { CommittedRoundView } from "./committed-round.js";
|
|
3
|
-
import type { LoopForgeResponse, LoopProjection } from "./protocol.js";
|
|
4
|
-
import type { RoundFacts } from "./round-facts.js";
|
|
5
|
-
export interface DerivedCognitiveFacts {
|
|
6
|
-
focus: LoopProjection["focus"];
|
|
7
|
-
todo: LoopProjection["todo"];
|
|
8
|
-
phase: LoopProjection["phase"];
|
|
9
|
-
delegation: LoopProjection["delegation"];
|
|
10
|
-
handoff: LoopProjection["handoff"];
|
|
11
|
-
/** v3.8: forwarded, not re-derived — the same facts the canonical state
|
|
12
|
-
* embeds, so the projection and the prompt cannot disagree. */
|
|
13
|
-
verified_subgoals: LoopProjection["verified_subgoals"];
|
|
14
|
-
}
|
|
15
|
-
/** Re-exported from round-facts.ts. The contract-fact derivation (walker,
|
|
16
|
-
* item reducer, verified sub-goals, verification debt) lives with the bundle
|
|
17
|
-
* every consumer reads — one implementation, one history window. */
|
|
18
|
-
export { deriveVerifiedSubGoals, deriveVerificationDebt } from "./round-facts.js";
|
|
19
|
-
export declare function deriveCognitiveFacts(input: {
|
|
20
|
-
compileResponse: LoopForgeResponse | null;
|
|
21
|
-
rounds: ReadonlyArray<CommittedRoundView>;
|
|
22
|
-
/** v3.8.1: the contract facts, REQUIRED and already derived by the caller via
|
|
23
|
-
* `deriveRoundFacts`. This function used to run the same reducers again over
|
|
24
|
-
* its own window, which let the projection and the prompt disagree about
|
|
25
|
-
* what the machine had verified. */
|
|
26
|
-
facts: RoundFacts;
|
|
27
|
-
verifiedClaims?: string[];
|
|
28
|
-
openGates?: string[];
|
|
29
|
-
}): DerivedCognitiveFacts;
|
|
30
|
-
//# sourceMappingURL=cognitive-facts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cognitive-facts.d.ts","sourceRoot":"","sources":["../src/cognitive-facts.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAE1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAG/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAW,MAAM,eAAe,CAAC;AAChF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAC7B,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACzC,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACnC;oEACgE;IAChE,iBAAiB,EAAE,cAAc,CAAC,mBAAmB,CAAC,CAAC;CACxD;AAID;;qEAEqE;AACrE,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAkDlF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,eAAe,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC1C,MAAM,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAC1C;;;yCAGqC;IACrC,KAAK,EAAE,UAAU,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB,GAAG,qBAAqB,CA2ExB"}
|
package/dist/cognitive-facts.js
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/** Shared cognitive facts used by typed projections and handoff views. */
|
|
2
|
-
import { deriveTodoId } from "./cognitive-governance.js";
|
|
3
|
-
const TODO_LIMIT = 10;
|
|
4
|
-
/** Re-exported from round-facts.ts. The contract-fact derivation (walker,
|
|
5
|
-
* item reducer, verified sub-goals, verification debt) lives with the bundle
|
|
6
|
-
* every consumer reads — one implementation, one history window. */
|
|
7
|
-
export { deriveVerifiedSubGoals, deriveVerificationDebt } from "./round-facts.js";
|
|
8
|
-
function deriveTodo(compileResponse, contractItemStatuses) {
|
|
9
|
-
const candidates = [];
|
|
10
|
-
const seen = new Set();
|
|
11
|
-
const push = (item, reason, source, priority) => {
|
|
12
|
-
const text = item.trim().slice(0, 200);
|
|
13
|
-
if (!text)
|
|
14
|
-
return;
|
|
15
|
-
const id = deriveTodoId(text);
|
|
16
|
-
if (seen.has(id))
|
|
17
|
-
return;
|
|
18
|
-
seen.add(id);
|
|
19
|
-
candidates.push({ id, item: text, reason, source, priority });
|
|
20
|
-
};
|
|
21
|
-
// v3.8: the only sources are machine/cognitive derivations — the agent's
|
|
22
|
-
// own next_action and emerged_subtasks are no longer projection inputs
|
|
23
|
-
// (emerged items already surface through their compiled sub-goals).
|
|
24
|
-
//
|
|
25
|
-
// The ACTIVE contract's unresolved items come first: they are the bounded
|
|
26
|
-
// work this round executes under, and `insufficient` items are the
|
|
27
|
-
// verification debt the agent must close.
|
|
28
|
-
for (const item of contractItemStatuses?.items ?? []) {
|
|
29
|
-
if (item.status === "insufficient") {
|
|
30
|
-
push(item.description, "contract item claimed met, not machine-verified", "derived", 10);
|
|
31
|
-
}
|
|
32
|
-
else if (item.status === "pending") {
|
|
33
|
-
push(item.description, "pending contract item", "derived", 8);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
for (const subGoal of compileResponse?.sub_goals ?? []) {
|
|
37
|
-
const goal = subGoal;
|
|
38
|
-
if (goal.status === "blocked") {
|
|
39
|
-
push(goal.description, "blocked sub-goal", "derived", 9);
|
|
40
|
-
}
|
|
41
|
-
else if (goal.status === "pending") {
|
|
42
|
-
push(goal.description, "pending sub-goal", "derived", goal.priority ?? 5);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return candidates
|
|
46
|
-
.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id))
|
|
47
|
-
.slice(0, TODO_LIMIT);
|
|
48
|
-
}
|
|
49
|
-
export function deriveCognitiveFacts(input) {
|
|
50
|
-
const { compileResponse, rounds } = input;
|
|
51
|
-
const latestFocus = [...rounds].reverse().find((round) => round.outcome !== "failed" &&
|
|
52
|
-
typeof round.evaluation?.output_summary === "string" &&
|
|
53
|
-
round.evaluation.output_summary.length > 0);
|
|
54
|
-
const milestones = compileResponse?.rolling_summary?.milestones ?? [];
|
|
55
|
-
const boundaries = milestones
|
|
56
|
-
.filter((milestone) => typeof milestone.round_range?.end === "number")
|
|
57
|
-
.map((milestone) => ({ label: milestone.label, round: milestone.round_range.end }));
|
|
58
|
-
const lastBoundary = boundaries[boundaries.length - 1];
|
|
59
|
-
const workerRounds = rounds.filter((round) => (round.evaluation?.worker_results?.length ?? 0) > 0);
|
|
60
|
-
const latestWorkerRound = workerRounds[workerRounds.length - 1];
|
|
61
|
-
const workerResults = latestWorkerRound?.evaluation?.worker_results ?? [];
|
|
62
|
-
const rolling = compileResponse?.rolling_summary;
|
|
63
|
-
// v3.8.1: read from the caller's derivation — do NOT re-run the reducers
|
|
64
|
-
// here. Two derivations of the same fact is how the projection and the
|
|
65
|
-
// prompt start disagreeing.
|
|
66
|
-
const { activeContract, itemStatuses: contractItemStatuses, verifiedSubGoals, verificationDebt, } = input.facts;
|
|
67
|
-
const currentTask = activeContract?.work_item ??
|
|
68
|
-
compileResponse?.loop_objective?.objective ?? "";
|
|
69
|
-
const latestOutcome = rolling?.key_outcomes[rolling.key_outcomes.length - 1] ??
|
|
70
|
-
latestFocus?.evaluation?.output_summary ?? "";
|
|
71
|
-
const remainingCriteria = (compileResponse?.criterion_statuses ?? [])
|
|
72
|
-
.filter((criterion) => criterion.status !== "verified" && criterion.status !== "claimed")
|
|
73
|
-
.map((criterion) => criterion.text)
|
|
74
|
-
.slice(0, 5);
|
|
75
|
-
const summary = [
|
|
76
|
-
currentTask ? `Current task: ${currentTask.slice(0, 200)}` : "",
|
|
77
|
-
latestOutcome ? `Latest outcome: ${latestOutcome.slice(0, 200)}` : "",
|
|
78
|
-
remainingCriteria.length > 0
|
|
79
|
-
? `Remaining criteria: ${remainingCriteria.join("; ").slice(0, 300)}`
|
|
80
|
-
: "",
|
|
81
|
-
].filter(Boolean).join(" | ");
|
|
82
|
-
const latestBlocker = [...rounds].reverse()
|
|
83
|
-
.find((round) => round.outcome === "blocked")?.evaluation?.blocker;
|
|
84
|
-
return {
|
|
85
|
-
focus: latestFocus
|
|
86
|
-
? { what: latestFocus.evaluation.output_summary.slice(0, 200), since_round: latestFocus.round }
|
|
87
|
-
: null,
|
|
88
|
-
todo: deriveTodo(compileResponse, contractItemStatuses),
|
|
89
|
-
phase: lastBoundary
|
|
90
|
-
? { current: lastBoundary.label, label: lastBoundary.label, boundaries }
|
|
91
|
-
: null,
|
|
92
|
-
delegation: {
|
|
93
|
-
pending: workerResults.filter((worker) => worker.outcome === "partial" || worker.outcome === "failed").length,
|
|
94
|
-
last_results: workerResults.slice(-5).map((worker) => ({
|
|
95
|
-
agentId: worker.agentId,
|
|
96
|
-
outcome: worker.outcome,
|
|
97
|
-
round: latestWorkerRound?.round ?? 0,
|
|
98
|
-
summary: worker.resultSummary.slice(0, 200),
|
|
99
|
-
})),
|
|
100
|
-
},
|
|
101
|
-
handoff: {
|
|
102
|
-
summary,
|
|
103
|
-
verified: [...(input.verifiedClaims ?? [])],
|
|
104
|
-
open_risks: [...new Set([
|
|
105
|
-
...(latestBlocker ? [latestBlocker] : []),
|
|
106
|
-
...verificationDebt,
|
|
107
|
-
...(input.openGates ?? []).slice(0, 3).map((gate) => `user gate pending: ${gate}`),
|
|
108
|
-
])],
|
|
109
|
-
},
|
|
110
|
-
verified_subgoals: verifiedSubGoals,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
//# sourceMappingURL=cognitive-facts.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cognitive-facts.js","sourceRoot":"","sources":["../src/cognitive-facts.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAG1E,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAgBzD,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB;;qEAEqE;AACrE,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAElF,SAAS,UAAU,CACjB,eAAyC,EACzC,oBAAmD;IAEnD,MAAM,UAAU,GAA2B,EAAE,CAAC;IAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,IAAI,GAAG,CACX,IAAY,EACZ,MAAc,EACd,MAAiB,EACjB,QAAgB,EACV,EAAE;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO;QACzB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC;IAEF,yEAAyE;IACzE,uEAAuE;IACvE,oEAAoE;IACpE,EAAE;IACF,0EAA0E;IAC1E,mEAAmE;IACnE,0CAA0C;IAC1C,KAAK,MAAM,IAAI,IAAI,oBAAoB,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,iDAAiD,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3F,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,uBAAuB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,SAAS,IAAI,EAAE,EAAE,CAAC;QACvD,MAAM,IAAI,GAAG,OAAkB,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;QAC3D,CAAC;aAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,kBAAkB,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,OAAO,UAAU;SACd,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACnE,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAUpC;IACC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAC1C,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CACvD,KAAK,CAAC,OAAO,KAAK,QAAQ;QAC1B,OAAO,KAAK,CAAC,UAAU,EAAE,cAAc,KAAK,QAAQ;QACpD,KAAK,CAAC,UAAU,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAC3C,CAAC;IACF,MAAM,UAAU,GAAG,eAAe,EAAE,eAAe,EAAE,UAAU,IAAI,EAAE,CAAC;IACtE,MAAM,UAAU,GAAG,UAAU;SAC1B,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,SAAS,CAAC,WAAW,EAAE,GAAG,KAAK,QAAQ,CAAC;SACrE,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACtF,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3C,CAAC,KAAK,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CACpD,CAAC;IACF,MAAM,iBAAiB,GAAG,YAAY,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,aAAa,GAAG,iBAAiB,EAAE,UAAU,EAAE,cAAc,IAAI,EAAE,CAAC;IAC1E,MAAM,OAAO,GAAG,eAAe,EAAE,eAAe,CAAC;IACjD,yEAAyE;IACzE,uEAAuE;IACvE,4BAA4B;IAC5B,MAAM,EACJ,cAAc,EACd,YAAY,EAAE,oBAAoB,EAClC,gBAAgB,EAChB,gBAAgB,GACjB,GAAG,KAAK,CAAC,KAAK,CAAC;IAChB,MAAM,WAAW,GAAG,cAAc,EAAE,SAAS;QAC3C,eAAe,EAAE,cAAc,EAAE,SAAS,IAAI,EAAE,CAAC;IACnD,MAAM,aAAa,GAAG,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1E,WAAW,EAAE,UAAU,EAAE,cAAc,IAAI,EAAE,CAAC;IAChD,MAAM,iBAAiB,GAAG,CAAC,eAAe,EAAE,kBAAkB,IAAI,EAAE,CAAC;SAClE,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,UAAU,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC;SACxF,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;SAClC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACf,MAAM,OAAO,GAAG;QACd,WAAW,CAAC,CAAC,CAAC,iBAAiB,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAC/D,aAAa,CAAC,CAAC,CAAC,mBAAmB,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QACrE,iBAAiB,CAAC,MAAM,GAAG,CAAC;YAC1B,CAAC,CAAC,uBAAuB,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;YACrE,CAAC,CAAC,EAAE;KACP,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,aAAa,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE;SACxC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC;IAErE,OAAO;QACL,KAAK,EAAE,WAAW;YAChB,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,UAAW,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,KAAK,EAAE;YAChG,CAAC,CAAC,IAAI;QACR,IAAI,EAAE,UAAU,CAAC,eAAe,EAAE,oBAAoB,CAAC;QACvD,KAAK,EAAE,YAAY;YACjB,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE;YACxE,CAAC,CAAC,IAAI;QACR,UAAU,EAAE;YACV,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CACvC,MAAM,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,OAAO,KAAK,QAAQ,CAC5D,CAAC,MAAM;YACR,YAAY,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACrD,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,iBAAiB,EAAE,KAAK,IAAI,CAAC;gBACpC,OAAO,EAAE,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;aAC5C,CAAC,CAAC;SACJ;QACD,OAAO,EAAE;YACP,OAAO;YACP,QAAQ,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC;YAC3C,UAAU,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC;oBACtB,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzC,GAAG,gBAAgB;oBACnB,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,sBAAsB,IAAI,EAAE,CAAC;iBACnF,CAAC,CAAC;SACJ;QACD,iBAAiB,EAAE,gBAAgB;KACpC,CAAC;AACJ,CAAC"}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/** LoopExtrasParser — Typed extraction pipeline for LoopForgeRequest extras.
|
|
2
|
-
*
|
|
3
|
-
* invokeLoopCompile receives a LoopForgeRequest whose extra fields arrive as
|
|
4
|
-
* untyped `Record<string, unknown>`. Before this module, each field was
|
|
5
|
-
* extracted inline with ad-hoc `typeof` guards spread across ~80 lines of
|
|
6
|
-
* engine.ts — brittle, untestable, and error-prone.
|
|
7
|
-
*
|
|
8
|
-
* ExtractionContext provides a single-pass, type-safe extraction surface:
|
|
9
|
-
* - Every method returns a typed value or a sensible default.
|
|
10
|
-
* - Type mismatches and out-of-range values are collected in `errors[]`
|
|
11
|
-
* rather than thrown, so the caller can log them and continue with
|
|
12
|
-
* best-effort defaults.
|
|
13
|
-
* - All extraction is synchronous and pure (no I/O, no side effects).
|
|
14
|
-
*
|
|
15
|
-
* Design constraint: zero runtime dependencies (Node.js stdlib only).
|
|
16
|
-
*/
|
|
17
|
-
import type { VerificationFlag } from "./protocol.js";
|
|
18
|
-
export interface FieldError {
|
|
19
|
-
field: string;
|
|
20
|
-
message: string;
|
|
21
|
-
received: unknown;
|
|
22
|
-
}
|
|
23
|
-
export declare class ExtractionContext {
|
|
24
|
-
/** Errors collected during extraction. The caller decides whether to
|
|
25
|
-
* log them, fail fast, or ignore them. Never populated for optional
|
|
26
|
-
* fields when the value is absent — only for present-but-invalid values. */
|
|
27
|
-
readonly errors: FieldError[];
|
|
28
|
-
private readonly source;
|
|
29
|
-
constructor(source: Record<string, unknown>);
|
|
30
|
-
/** Extract a required string field. Returns `fallback` (default `""`) when
|
|
31
|
-
* the key is missing or the value is not a string. */
|
|
32
|
-
string(key: string, fallback?: string): string;
|
|
33
|
-
/** Extract an optional string field. Returns `null` when absent,
|
|
34
|
-
* `fallback` when present-but-invalid. */
|
|
35
|
-
optionalString(key: string, fallback?: string | null): string | null;
|
|
36
|
-
/** Extract a number field. Returns `fallback` when missing or invalid.
|
|
37
|
-
* Applies optional `min`/`max` clamping and `truncate` (Math.trunc). */
|
|
38
|
-
number(key: string, fallback: number, opts?: {
|
|
39
|
-
min?: number;
|
|
40
|
-
max?: number;
|
|
41
|
-
truncate?: boolean;
|
|
42
|
-
}): number;
|
|
43
|
-
/** Extract a boolean field. Returns `fallback` when missing or invalid. */
|
|
44
|
-
boolean(key: string, fallback?: boolean): boolean;
|
|
45
|
-
/** Extract a string array. Returns `fallback` (default `[]`) when missing.
|
|
46
|
-
* Filters out non-string elements and records them as errors individually. */
|
|
47
|
-
stringArray(key: string, fallback?: string[]): string[];
|
|
48
|
-
/** Extract a plain object field. Returns `null` when missing or when the
|
|
49
|
-
* value is not a non-array object. Does not recurse into the value. */
|
|
50
|
-
object(key: string): Record<string, unknown> | null;
|
|
51
|
-
/** Check whether a key exists (is not undefined). Useful for gating
|
|
52
|
-
* optional structured fields — call object() only when present. */
|
|
53
|
-
has(key: string): boolean;
|
|
54
|
-
}
|
|
55
|
-
export interface ParsedLoopExtras {
|
|
56
|
-
loop_id: string;
|
|
57
|
-
round: number;
|
|
58
|
-
goal_id: string;
|
|
59
|
-
domain: string;
|
|
60
|
-
next_task_proposal: string;
|
|
61
|
-
plan_source: string | null;
|
|
62
|
-
constraints_from_plan: string[];
|
|
63
|
-
new_since_last_round: string;
|
|
64
|
-
force_level: string;
|
|
65
|
-
external_context: string;
|
|
66
|
-
max_rounds: number | undefined;
|
|
67
|
-
verification_flags: VerificationFlag[];
|
|
68
|
-
attempt: number;
|
|
69
|
-
consecutive_rejections: number;
|
|
70
|
-
rejection_notice: string;
|
|
71
|
-
last_round_result: Record<string, unknown> | null;
|
|
72
|
-
loop_objective: Record<string, unknown> | null;
|
|
73
|
-
}
|
|
74
|
-
/** Parse all extras fields from a LoopForgeRequest into a typed structure.
|
|
75
|
-
* Extraction errors are attached to the returned context for inspection.
|
|
76
|
-
* This function never throws — every field has a safe default. */
|
|
77
|
-
export declare function parseLoopExtras(extras: Record<string, unknown>, taskId: string): {
|
|
78
|
-
parsed: ParsedLoopExtras;
|
|
79
|
-
ctx: ExtractionContext;
|
|
80
|
-
};
|
|
81
|
-
//# sourceMappingURL=loop-extras-parser.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loop-extras-parser.d.ts","sourceRoot":"","sources":["../src/loop-extras-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,qBAAa,iBAAiB;IAC5B;;iFAE6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAM;IAEnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;gBAErC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAM3C;2DACuD;IACvD,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAK,GAAG,MAAM;IAY1C;+CAC2C;IAC3C,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,MAAM,GAAG,IAAI;IAY1E;6EACyE;IACzE,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,QAAQ,CAAC,EAAE,OAAO,CAAC;KACpB,GAAG,MAAM;IAkBV,2EAA2E;IAC3E,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO;IAc/C;mFAC+E;IAC/E,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IA4B3D;4EACwE;IACxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAcnD;wEACoE;IACpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAG1B;AAID,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,kBAAkB,EAAE,gBAAgB,EAAE,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAClD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAChD;AAED;;mEAEmE;AACnE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,gBAAgB,CAAC;IAAC,GAAG,EAAE,iBAAiB,CAAA;CAAE,CAgCtD"}
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/** LoopExtrasParser — Typed extraction pipeline for LoopForgeRequest extras.
|
|
2
|
-
*
|
|
3
|
-
* invokeLoopCompile receives a LoopForgeRequest whose extra fields arrive as
|
|
4
|
-
* untyped `Record<string, unknown>`. Before this module, each field was
|
|
5
|
-
* extracted inline with ad-hoc `typeof` guards spread across ~80 lines of
|
|
6
|
-
* engine.ts — brittle, untestable, and error-prone.
|
|
7
|
-
*
|
|
8
|
-
* ExtractionContext provides a single-pass, type-safe extraction surface:
|
|
9
|
-
* - Every method returns a typed value or a sensible default.
|
|
10
|
-
* - Type mismatches and out-of-range values are collected in `errors[]`
|
|
11
|
-
* rather than thrown, so the caller can log them and continue with
|
|
12
|
-
* best-effort defaults.
|
|
13
|
-
* - All extraction is synchronous and pure (no I/O, no side effects).
|
|
14
|
-
*
|
|
15
|
-
* Design constraint: zero runtime dependencies (Node.js stdlib only).
|
|
16
|
-
*/
|
|
17
|
-
// ── ExtractionContext ──────────────────────────────────────────────────────
|
|
18
|
-
export class ExtractionContext {
|
|
19
|
-
/** Errors collected during extraction. The caller decides whether to
|
|
20
|
-
* log them, fail fast, or ignore them. Never populated for optional
|
|
21
|
-
* fields when the value is absent — only for present-but-invalid values. */
|
|
22
|
-
errors = [];
|
|
23
|
-
source;
|
|
24
|
-
constructor(source) {
|
|
25
|
-
this.source = source;
|
|
26
|
-
}
|
|
27
|
-
// ── Primitives ─────────────────────────────────────────────────────────
|
|
28
|
-
/** Extract a required string field. Returns `fallback` (default `""`) when
|
|
29
|
-
* the key is missing or the value is not a string. */
|
|
30
|
-
string(key, fallback = "") {
|
|
31
|
-
const raw = this.source[key];
|
|
32
|
-
if (raw === undefined || raw === null)
|
|
33
|
-
return fallback;
|
|
34
|
-
if (typeof raw === "string")
|
|
35
|
-
return raw;
|
|
36
|
-
this.errors.push({
|
|
37
|
-
field: key,
|
|
38
|
-
message: `expected string, got ${typeof raw}`,
|
|
39
|
-
received: raw,
|
|
40
|
-
});
|
|
41
|
-
return fallback;
|
|
42
|
-
}
|
|
43
|
-
/** Extract an optional string field. Returns `null` when absent,
|
|
44
|
-
* `fallback` when present-but-invalid. */
|
|
45
|
-
optionalString(key, fallback = null) {
|
|
46
|
-
const raw = this.source[key];
|
|
47
|
-
if (raw === undefined || raw === null)
|
|
48
|
-
return null;
|
|
49
|
-
if (typeof raw === "string")
|
|
50
|
-
return raw;
|
|
51
|
-
this.errors.push({
|
|
52
|
-
field: key,
|
|
53
|
-
message: `expected string or null, got ${typeof raw}`,
|
|
54
|
-
received: raw,
|
|
55
|
-
});
|
|
56
|
-
return fallback;
|
|
57
|
-
}
|
|
58
|
-
/** Extract a number field. Returns `fallback` when missing or invalid.
|
|
59
|
-
* Applies optional `min`/`max` clamping and `truncate` (Math.trunc). */
|
|
60
|
-
number(key, fallback, opts) {
|
|
61
|
-
const raw = this.source[key];
|
|
62
|
-
if (raw === undefined || raw === null)
|
|
63
|
-
return fallback;
|
|
64
|
-
if (typeof raw === "number" && !Number.isNaN(raw)) {
|
|
65
|
-
let value = raw;
|
|
66
|
-
if (opts?.truncate)
|
|
67
|
-
value = Math.trunc(value);
|
|
68
|
-
if (opts?.min !== undefined)
|
|
69
|
-
value = Math.max(opts.min, value);
|
|
70
|
-
if (opts?.max !== undefined)
|
|
71
|
-
value = Math.min(opts.max, value);
|
|
72
|
-
return value;
|
|
73
|
-
}
|
|
74
|
-
this.errors.push({
|
|
75
|
-
field: key,
|
|
76
|
-
message: `expected number, got ${typeof raw}`,
|
|
77
|
-
received: raw,
|
|
78
|
-
});
|
|
79
|
-
return fallback;
|
|
80
|
-
}
|
|
81
|
-
/** Extract a boolean field. Returns `fallback` when missing or invalid. */
|
|
82
|
-
boolean(key, fallback = false) {
|
|
83
|
-
const raw = this.source[key];
|
|
84
|
-
if (raw === undefined || raw === null)
|
|
85
|
-
return fallback;
|
|
86
|
-
if (typeof raw === "boolean")
|
|
87
|
-
return raw;
|
|
88
|
-
this.errors.push({
|
|
89
|
-
field: key,
|
|
90
|
-
message: `expected boolean, got ${typeof raw}`,
|
|
91
|
-
received: raw,
|
|
92
|
-
});
|
|
93
|
-
return fallback;
|
|
94
|
-
}
|
|
95
|
-
// ── Collections ────────────────────────────────────────────────────────
|
|
96
|
-
/** Extract a string array. Returns `fallback` (default `[]`) when missing.
|
|
97
|
-
* Filters out non-string elements and records them as errors individually. */
|
|
98
|
-
stringArray(key, fallback = []) {
|
|
99
|
-
const raw = this.source[key];
|
|
100
|
-
if (raw === undefined || raw === null)
|
|
101
|
-
return fallback;
|
|
102
|
-
if (!Array.isArray(raw)) {
|
|
103
|
-
this.errors.push({
|
|
104
|
-
field: key,
|
|
105
|
-
message: `expected array, got ${typeof raw}`,
|
|
106
|
-
received: raw,
|
|
107
|
-
});
|
|
108
|
-
return fallback;
|
|
109
|
-
}
|
|
110
|
-
const result = [];
|
|
111
|
-
for (let i = 0; i < raw.length; i++) {
|
|
112
|
-
if (typeof raw[i] === "string") {
|
|
113
|
-
result.push(raw[i]);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
this.errors.push({
|
|
117
|
-
field: `${key}[${i}]`,
|
|
118
|
-
message: `expected string in array, got ${typeof raw[i]}`,
|
|
119
|
-
received: raw[i],
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return result;
|
|
124
|
-
}
|
|
125
|
-
// ── Structured ─────────────────────────────────────────────────────────
|
|
126
|
-
/** Extract a plain object field. Returns `null` when missing or when the
|
|
127
|
-
* value is not a non-array object. Does not recurse into the value. */
|
|
128
|
-
object(key) {
|
|
129
|
-
const raw = this.source[key];
|
|
130
|
-
if (raw === undefined || raw === null)
|
|
131
|
-
return null;
|
|
132
|
-
if (typeof raw === "object" && !Array.isArray(raw)) {
|
|
133
|
-
return raw;
|
|
134
|
-
}
|
|
135
|
-
this.errors.push({
|
|
136
|
-
field: key,
|
|
137
|
-
message: `expected object, got ${Array.isArray(raw) ? "array" : typeof raw}`,
|
|
138
|
-
received: raw,
|
|
139
|
-
});
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
/** Check whether a key exists (is not undefined). Useful for gating
|
|
143
|
-
* optional structured fields — call object() only when present. */
|
|
144
|
-
has(key) {
|
|
145
|
-
return this.source[key] !== undefined;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/** Parse all extras fields from a LoopForgeRequest into a typed structure.
|
|
149
|
-
* Extraction errors are attached to the returned context for inspection.
|
|
150
|
-
* This function never throws — every field has a safe default. */
|
|
151
|
-
export function parseLoopExtras(extras, taskId) {
|
|
152
|
-
const ctx = new ExtractionContext(extras);
|
|
153
|
-
const parsed = {
|
|
154
|
-
loop_id: ctx.string("loop_id") || taskId,
|
|
155
|
-
round: ctx.number("round", 1, { min: 1, truncate: true }),
|
|
156
|
-
goal_id: ctx.string("goal_id"),
|
|
157
|
-
domain: ctx.string("domain"),
|
|
158
|
-
next_task_proposal: ctx.string("next_task_proposal"),
|
|
159
|
-
plan_source: ctx.optionalString("plan_source"),
|
|
160
|
-
constraints_from_plan: ctx.stringArray("constraints_from_plan"),
|
|
161
|
-
new_since_last_round: ctx.string("new_since_last_round"),
|
|
162
|
-
force_level: ctx.string("force_level", "auto"),
|
|
163
|
-
external_context: ctx.string("external_context"),
|
|
164
|
-
max_rounds: ctx.has("max_rounds")
|
|
165
|
-
? ctx.number("max_rounds", 0, { min: 1, truncate: true }) || undefined
|
|
166
|
-
: undefined,
|
|
167
|
-
verification_flags: validateVerificationFlags(extras.verification_flags, ctx),
|
|
168
|
-
attempt: ctx.number("attempt", 1, { min: 1, truncate: true }),
|
|
169
|
-
consecutive_rejections: ctx.number("consecutive_rejections", 0, {
|
|
170
|
-
min: 0,
|
|
171
|
-
truncate: true,
|
|
172
|
-
}),
|
|
173
|
-
rejection_notice: ctx.string("rejection_notice"),
|
|
174
|
-
last_round_result: ctx.object("last_round_result"),
|
|
175
|
-
loop_objective: ctx.object("loop_objective"),
|
|
176
|
-
};
|
|
177
|
-
return { parsed, ctx };
|
|
178
|
-
}
|
|
179
|
-
// ── Specialized validators ────────────────────────────────────────────────
|
|
180
|
-
function validateVerificationFlags(raw, ctx) {
|
|
181
|
-
if (!Array.isArray(raw)) {
|
|
182
|
-
if (raw !== undefined && raw !== null) {
|
|
183
|
-
ctx.errors.push({
|
|
184
|
-
field: "verification_flags",
|
|
185
|
-
message: `expected array, got ${typeof raw}`,
|
|
186
|
-
received: raw,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
const flags = [];
|
|
192
|
-
for (let i = 0; i < raw.length; i++) {
|
|
193
|
-
const item = raw[i];
|
|
194
|
-
if (item !== null &&
|
|
195
|
-
typeof item === "object" &&
|
|
196
|
-
!Array.isArray(item) &&
|
|
197
|
-
typeof item.severity === "string" &&
|
|
198
|
-
typeof item.field === "string" &&
|
|
199
|
-
typeof item.check === "string" &&
|
|
200
|
-
typeof item.detail === "string") {
|
|
201
|
-
flags.push(item);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
ctx.errors.push({
|
|
205
|
-
field: `verification_flags[${i}]`,
|
|
206
|
-
message: "expected VerificationFlag object",
|
|
207
|
-
received: item,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
return flags;
|
|
212
|
-
}
|
|
213
|
-
//# sourceMappingURL=loop-extras-parser.js.map
|