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.
Files changed (283) hide show
  1. package/README.md +236 -112
  2. package/dist/audit.d.ts +73 -15
  3. package/dist/audit.d.ts.map +1 -1
  4. package/dist/audit.js +131 -61
  5. package/dist/audit.js.map +1 -1
  6. package/dist/canonical-state.d.ts +114 -40
  7. package/dist/canonical-state.d.ts.map +1 -1
  8. package/dist/canonical-state.js +276 -182
  9. package/dist/canonical-state.js.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +41 -12
  12. package/dist/cli.js.map +1 -1
  13. package/dist/cognitive-governance.d.ts +0 -2
  14. package/dist/cognitive-governance.d.ts.map +1 -1
  15. package/dist/cognitive-governance.js +0 -4
  16. package/dist/cognitive-governance.js.map +1 -1
  17. package/dist/committed-round.d.ts +97 -156
  18. package/dist/committed-round.d.ts.map +1 -1
  19. package/dist/committed-round.js +223 -419
  20. package/dist/committed-round.js.map +1 -1
  21. package/dist/constraint-lifecycle.d.ts +73 -0
  22. package/dist/constraint-lifecycle.d.ts.map +1 -0
  23. package/dist/constraint-lifecycle.js +174 -0
  24. package/dist/constraint-lifecycle.js.map +1 -0
  25. package/dist/context-boundary.d.ts +83 -0
  26. package/dist/context-boundary.d.ts.map +1 -0
  27. package/dist/context-boundary.js +101 -0
  28. package/dist/context-boundary.js.map +1 -0
  29. package/dist/contract-items.d.ts +12 -8
  30. package/dist/contract-items.d.ts.map +1 -1
  31. package/dist/contract-items.js +31 -26
  32. package/dist/contract-items.js.map +1 -1
  33. package/dist/contract-requirement.d.ts +47 -0
  34. package/dist/contract-requirement.d.ts.map +1 -0
  35. package/dist/contract-requirement.js +51 -0
  36. package/dist/contract-requirement.js.map +1 -0
  37. package/dist/criterion-status.d.ts +35 -0
  38. package/dist/criterion-status.d.ts.map +1 -0
  39. package/dist/criterion-status.js +89 -0
  40. package/dist/criterion-status.js.map +1 -0
  41. package/dist/enforcement-gate.d.ts +26 -38
  42. package/dist/enforcement-gate.d.ts.map +1 -1
  43. package/dist/enforcement-gate.js +196 -319
  44. package/dist/enforcement-gate.js.map +1 -1
  45. package/dist/engine.d.ts +9 -103
  46. package/dist/engine.d.ts.map +1 -1
  47. package/dist/engine.js +27 -897
  48. package/dist/engine.js.map +1 -1
  49. package/dist/errors.d.ts +41 -0
  50. package/dist/errors.d.ts.map +1 -0
  51. package/dist/errors.js +69 -0
  52. package/dist/errors.js.map +1 -0
  53. package/dist/evidence-claims.d.ts +33 -31
  54. package/dist/evidence-claims.d.ts.map +1 -1
  55. package/dist/evidence-claims.js +58 -72
  56. package/dist/evidence-claims.js.map +1 -1
  57. package/dist/evidence-posture-view.d.ts +19 -0
  58. package/dist/evidence-posture-view.d.ts.map +1 -0
  59. package/dist/evidence-posture-view.js +116 -0
  60. package/dist/evidence-posture-view.js.map +1 -0
  61. package/dist/evidence-posture.d.ts +73 -0
  62. package/dist/evidence-posture.d.ts.map +1 -0
  63. package/dist/evidence-posture.js +281 -0
  64. package/dist/evidence-posture.js.map +1 -0
  65. package/dist/evidence-provider.d.ts +55 -37
  66. package/dist/evidence-provider.d.ts.map +1 -1
  67. package/dist/evidence-provider.js +182 -332
  68. package/dist/evidence-provider.js.map +1 -1
  69. package/dist/explain.d.ts +166 -27
  70. package/dist/explain.d.ts.map +1 -1
  71. package/dist/explain.js +372 -58
  72. package/dist/explain.js.map +1 -1
  73. package/dist/field-accounting.d.ts.map +1 -1
  74. package/dist/field-accounting.js +3 -22
  75. package/dist/field-accounting.js.map +1 -1
  76. package/dist/generate-schema.js +13 -8
  77. package/dist/generate-schema.js.map +1 -1
  78. package/dist/git-boundary.d.ts +128 -0
  79. package/dist/git-boundary.d.ts.map +1 -0
  80. package/dist/git-boundary.js +625 -0
  81. package/dist/git-boundary.js.map +1 -0
  82. package/dist/git-facts.d.ts +22 -0
  83. package/dist/git-facts.d.ts.map +1 -0
  84. package/dist/git-facts.js +29 -0
  85. package/dist/git-facts.js.map +1 -0
  86. package/dist/git-motion.d.ts +37 -0
  87. package/dist/git-motion.d.ts.map +1 -0
  88. package/dist/git-motion.js +59 -0
  89. package/dist/git-motion.js.map +1 -0
  90. package/dist/history.d.ts +44 -0
  91. package/dist/history.d.ts.map +1 -0
  92. package/dist/history.js +77 -0
  93. package/dist/history.js.map +1 -0
  94. package/dist/index.d.ts +19 -9
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +12 -4
  97. package/dist/index.js.map +1 -1
  98. package/dist/init.d.ts +16 -0
  99. package/dist/init.d.ts.map +1 -1
  100. package/dist/init.js +80 -14
  101. package/dist/init.js.map +1 -1
  102. package/dist/loop-compiler.d.ts +39 -23
  103. package/dist/loop-compiler.d.ts.map +1 -1
  104. package/dist/loop-compiler.js +368 -565
  105. package/dist/loop-compiler.js.map +1 -1
  106. package/dist/loop-store.d.ts +6 -5
  107. package/dist/loop-store.d.ts.map +1 -1
  108. package/dist/loop-store.js +153 -35
  109. package/dist/loop-store.js.map +1 -1
  110. package/dist/mcp/round-lifecycle.d.ts +255 -81
  111. package/dist/mcp/round-lifecycle.d.ts.map +1 -1
  112. package/dist/mcp/round-lifecycle.js +1552 -539
  113. package/dist/mcp/round-lifecycle.js.map +1 -1
  114. package/dist/mcp/server.d.ts.map +1 -1
  115. package/dist/mcp/server.js +42 -18
  116. package/dist/mcp/server.js.map +1 -1
  117. package/dist/mcp/session.d.ts +81 -39
  118. package/dist/mcp/session.d.ts.map +1 -1
  119. package/dist/mcp/session.js +956 -282
  120. package/dist/mcp/session.js.map +1 -1
  121. package/dist/mcp/submission-boundary.d.ts +23 -31
  122. package/dist/mcp/submission-boundary.d.ts.map +1 -1
  123. package/dist/mcp/submission-boundary.js +242 -92
  124. package/dist/mcp/submission-boundary.js.map +1 -1
  125. package/dist/mcp/submission-handle.d.ts +66 -0
  126. package/dist/mcp/submission-handle.d.ts.map +1 -0
  127. package/dist/mcp/submission-handle.js +173 -0
  128. package/dist/mcp/submission-handle.js.map +1 -0
  129. package/dist/mcp/tools.d.ts +17 -7
  130. package/dist/mcp/tools.d.ts.map +1 -1
  131. package/dist/mcp/tools.js +707 -244
  132. package/dist/mcp/tools.js.map +1 -1
  133. package/dist/open-question.d.ts +7 -0
  134. package/dist/open-question.d.ts.map +1 -0
  135. package/dist/open-question.js +14 -0
  136. package/dist/open-question.js.map +1 -0
  137. package/dist/policy-metrics.d.ts +31 -21
  138. package/dist/policy-metrics.d.ts.map +1 -1
  139. package/dist/policy-metrics.js +117 -64
  140. package/dist/policy-metrics.js.map +1 -1
  141. package/dist/policy.d.ts +11 -39
  142. package/dist/policy.d.ts.map +1 -1
  143. package/dist/policy.js +12 -12
  144. package/dist/policy.js.map +1 -1
  145. package/dist/progress.d.ts +45 -0
  146. package/dist/progress.d.ts.map +1 -0
  147. package/dist/progress.js +136 -0
  148. package/dist/progress.js.map +1 -0
  149. package/dist/prompt-assembler.d.ts +20 -6
  150. package/dist/prompt-assembler.d.ts.map +1 -1
  151. package/dist/prompt-assembler.js +475 -479
  152. package/dist/prompt-assembler.js.map +1 -1
  153. package/dist/prompt-coverage.d.ts +0 -6
  154. package/dist/prompt-coverage.d.ts.map +1 -1
  155. package/dist/prompt-coverage.js +4 -8
  156. package/dist/prompt-coverage.js.map +1 -1
  157. package/dist/prompt-freshness.d.ts +6 -17
  158. package/dist/prompt-freshness.d.ts.map +1 -1
  159. package/dist/prompt-freshness.js +27 -22
  160. package/dist/prompt-freshness.js.map +1 -1
  161. package/dist/prompt-policy.d.ts +14 -7
  162. package/dist/prompt-policy.d.ts.map +1 -1
  163. package/dist/prompt-policy.js +21 -5
  164. package/dist/prompt-policy.js.map +1 -1
  165. package/dist/protocol.d.ts +843 -424
  166. package/dist/protocol.d.ts.map +1 -1
  167. package/dist/protocol.js +73 -102
  168. package/dist/protocol.js.map +1 -1
  169. package/dist/recovery-guidance.d.ts +13 -0
  170. package/dist/recovery-guidance.d.ts.map +1 -0
  171. package/dist/recovery-guidance.js +15 -0
  172. package/dist/recovery-guidance.js.map +1 -0
  173. package/dist/recovery.d.ts +68 -0
  174. package/dist/recovery.d.ts.map +1 -0
  175. package/dist/recovery.js +234 -0
  176. package/dist/recovery.js.map +1 -0
  177. package/dist/replay.d.ts +42 -8
  178. package/dist/replay.d.ts.map +1 -1
  179. package/dist/replay.js +311 -84
  180. package/dist/replay.js.map +1 -1
  181. package/dist/result-facts.d.ts +45 -0
  182. package/dist/result-facts.d.ts.map +1 -0
  183. package/dist/result-facts.js +86 -0
  184. package/dist/result-facts.js.map +1 -0
  185. package/dist/round-contract.d.ts +38 -8
  186. package/dist/round-contract.d.ts.map +1 -1
  187. package/dist/round-contract.js +71 -31
  188. package/dist/round-contract.js.map +1 -1
  189. package/dist/round-coordinator.d.ts +41 -89
  190. package/dist/round-coordinator.d.ts.map +1 -1
  191. package/dist/round-coordinator.js +250 -267
  192. package/dist/round-coordinator.js.map +1 -1
  193. package/dist/round-driver.d.ts +56 -16
  194. package/dist/round-driver.d.ts.map +1 -1
  195. package/dist/round-driver.js +225 -39
  196. package/dist/round-driver.js.map +1 -1
  197. package/dist/round-facts.d.ts +33 -52
  198. package/dist/round-facts.d.ts.map +1 -1
  199. package/dist/round-facts.js +51 -160
  200. package/dist/round-facts.js.map +1 -1
  201. package/dist/round-guidance.d.ts +56 -0
  202. package/dist/round-guidance.d.ts.map +1 -0
  203. package/dist/round-guidance.js +161 -0
  204. package/dist/round-guidance.js.map +1 -0
  205. package/dist/round-outcome.d.ts +21 -0
  206. package/dist/round-outcome.d.ts.map +1 -0
  207. package/dist/round-outcome.js +31 -0
  208. package/dist/round-outcome.js.map +1 -0
  209. package/dist/round-transaction.d.ts +168 -21
  210. package/dist/round-transaction.d.ts.map +1 -1
  211. package/dist/round-transaction.js +837 -38
  212. package/dist/round-transaction.js.map +1 -1
  213. package/dist/self-eval.d.ts +37 -61
  214. package/dist/self-eval.d.ts.map +1 -1
  215. package/dist/self-eval.js +413 -392
  216. package/dist/self-eval.js.map +1 -1
  217. package/dist/storage.d.ts +16 -0
  218. package/dist/storage.d.ts.map +1 -1
  219. package/dist/storage.js +54 -4
  220. package/dist/storage.js.map +1 -1
  221. package/dist/subgoal-state.d.ts +2 -3
  222. package/dist/subgoal-state.d.ts.map +1 -1
  223. package/dist/subgoal-state.js +2 -3
  224. package/dist/subgoal-state.js.map +1 -1
  225. package/dist/submission-guidance.d.ts +29 -21
  226. package/dist/submission-guidance.d.ts.map +1 -1
  227. package/dist/submission-guidance.js +308 -260
  228. package/dist/submission-guidance.js.map +1 -1
  229. package/dist/submission-receipt.d.ts +16 -8
  230. package/dist/submission-receipt.d.ts.map +1 -1
  231. package/dist/submission-receipt.js +145 -51
  232. package/dist/submission-receipt.js.map +1 -1
  233. package/dist/token-utils.d.ts +28 -7
  234. package/dist/token-utils.d.ts.map +1 -1
  235. package/dist/token-utils.js +27 -11
  236. package/dist/token-utils.js.map +1 -1
  237. package/dist/transaction-identity.d.ts +9 -0
  238. package/dist/transaction-identity.d.ts.map +1 -0
  239. package/dist/transaction-identity.js +69 -0
  240. package/dist/transaction-identity.js.map +1 -0
  241. package/dist/verification-actions.d.ts.map +1 -1
  242. package/dist/verification-actions.js +10 -11
  243. package/dist/verification-actions.js.map +1 -1
  244. package/dist/verification-gate.d.ts +23 -47
  245. package/dist/verification-gate.d.ts.map +1 -1
  246. package/dist/verification-gate.js +202 -423
  247. package/dist/verification-gate.js.map +1 -1
  248. package/dist/version.d.ts +1 -1
  249. package/dist/version.js +1 -1
  250. package/dist/working-memory.d.ts +187 -0
  251. package/dist/working-memory.d.ts.map +1 -0
  252. package/dist/working-memory.js +767 -0
  253. package/dist/working-memory.js.map +1 -0
  254. package/dist/workspace-checkpoint.d.ts +88 -0
  255. package/dist/workspace-checkpoint.d.ts.map +1 -0
  256. package/dist/workspace-checkpoint.js +326 -0
  257. package/dist/workspace-checkpoint.js.map +1 -0
  258. package/dist/workspace-restore.d.ts +68 -0
  259. package/dist/workspace-restore.d.ts.map +1 -0
  260. package/dist/workspace-restore.js +604 -0
  261. package/dist/workspace-restore.js.map +1 -0
  262. package/dist/workspace.d.ts +0 -9
  263. package/dist/workspace.d.ts.map +1 -1
  264. package/dist/workspace.js +0 -17
  265. package/dist/workspace.js.map +1 -1
  266. package/loop_policy.json +4 -18
  267. package/package.json +68 -68
  268. package/skills/forge/SKILL.md +212 -0
  269. package/skills/forge/references/evidence-and-contracts.md +204 -0
  270. package/skills/forge/references/recovery-and-views.md +188 -0
  271. package/dist/cognitive-facts.d.ts +0 -30
  272. package/dist/cognitive-facts.d.ts.map +0 -1
  273. package/dist/cognitive-facts.js +0 -113
  274. package/dist/cognitive-facts.js.map +0 -1
  275. package/dist/loop-extras-parser.d.ts +0 -81
  276. package/dist/loop-extras-parser.d.ts.map +0 -1
  277. package/dist/loop-extras-parser.js +0 -213
  278. package/dist/loop-extras-parser.js.map +0 -1
  279. package/dist/loop-projection.d.ts +0 -6
  280. package/dist/loop-projection.d.ts.map +0 -1
  281. package/dist/loop-projection.js +0 -28
  282. package/dist/loop-projection.js.map +0 -1
  283. 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"}
@@ -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":"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"}
@@ -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