loopforge 2.7.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -182
- package/dist/canonical-event-writer.d.ts +20 -0
- package/dist/canonical-event-writer.d.ts.map +1 -0
- package/dist/canonical-event-writer.js +131 -0
- package/dist/canonical-event-writer.js.map +1 -0
- package/dist/canonical-state.d.ts +20 -16
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +211 -64
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -10
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-governance.d.ts +48 -0
- package/dist/cognitive-governance.d.ts.map +1 -0
- package/dist/cognitive-governance.js +226 -0
- package/dist/cognitive-governance.js.map +1 -0
- package/dist/compiler-subgoals.d.ts +7 -0
- package/dist/compiler-subgoals.d.ts.map +1 -0
- package/dist/compiler-subgoals.js +93 -0
- package/dist/compiler-subgoals.js.map +1 -0
- package/dist/derive-id.d.ts +12 -0
- package/dist/derive-id.d.ts.map +1 -0
- package/dist/derive-id.js +31 -0
- package/dist/derive-id.js.map +1 -0
- package/dist/evidence-provider.d.ts +26 -26
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +45 -71
- package/dist/evidence-provider.js.map +1 -1
- package/dist/generate-schema.js +90 -2
- package/dist/generate-schema.js.map +1 -1
- package/dist/index.d.ts +23 -29
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -26
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts +2 -2
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +321 -315
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-extras-parser.d.ts +2 -2
- package/dist/loop-extras-parser.d.ts.map +1 -1
- package/dist/loop-extras-parser.js +2 -2
- package/dist/loop-extras-parser.js.map +1 -1
- package/dist/loop-store.d.ts +20 -58
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +232 -311
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/server.d.ts +3 -3
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +123 -19
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session-projection.d.ts +5 -0
- package/dist/mcp/session-projection.d.ts.map +1 -0
- package/dist/mcp/session-projection.js +18 -0
- package/dist/mcp/session-projection.js.map +1 -0
- package/dist/mcp/session-queue.d.ts +3 -0
- package/dist/mcp/session-queue.d.ts.map +1 -0
- package/dist/mcp/session-queue.js +18 -0
- package/dist/mcp/session-queue.js.map +1 -0
- package/dist/mcp/session-types.d.ts +72 -0
- package/dist/mcp/session-types.d.ts.map +1 -0
- package/dist/mcp/session-types.js +2 -0
- package/dist/mcp/session-types.js.map +1 -0
- package/dist/mcp/session.d.ts +65 -84
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +667 -224
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts +26 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +401 -254
- package/dist/mcp/tools.js.map +1 -1
- package/dist/policy-metrics.d.ts +24 -0
- package/dist/policy-metrics.d.ts.map +1 -1
- package/dist/policy-metrics.js +174 -0
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +74 -12
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +124 -19
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts +8 -4
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +533 -86
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-policy.d.ts +8 -1
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +26 -8
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +259 -173
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +17 -96
- package/dist/protocol.js.map +1 -1
- package/dist/replay.d.ts +11 -3
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +51 -14
- package/dist/replay.js.map +1 -1
- package/dist/round-commit-store.d.ts +12 -0
- package/dist/round-commit-store.d.ts.map +1 -0
- package/dist/round-commit-store.js +13 -0
- package/dist/round-commit-store.js.map +1 -0
- package/dist/round-coordinator.d.ts +53 -22
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +140 -44
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +18 -10
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +11 -13
- package/dist/round-driver.js.map +1 -1
- package/dist/round-gate.d.ts +31 -0
- package/dist/round-gate.d.ts.map +1 -0
- package/dist/round-gate.js +259 -0
- package/dist/round-gate.js.map +1 -0
- package/dist/round-recovery.d.ts +17 -0
- package/dist/round-recovery.d.ts.map +1 -0
- package/dist/round-recovery.js +117 -0
- package/dist/round-recovery.js.map +1 -0
- package/dist/round-report.d.ts +16 -0
- package/dist/round-report.d.ts.map +1 -0
- package/dist/round-report.js +401 -0
- package/dist/round-report.js.map +1 -0
- package/dist/round-transaction.d.ts +23 -12
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +104 -91
- package/dist/round-transaction.js.map +1 -1
- package/dist/runtime-services.d.ts +59 -0
- package/dist/runtime-services.d.ts.map +1 -0
- package/dist/{engine.js → runtime-services.js} +157 -275
- package/dist/runtime-services.js.map +1 -0
- package/dist/storage.d.ts +3 -9
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +6 -17
- package/dist/storage.js.map +1 -1
- package/dist/token-utils.d.ts +12 -8
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +56 -10
- package/dist/token-utils.js.map +1 -1
- package/dist/vault-format.d.ts +4 -0
- package/dist/vault-format.d.ts.map +1 -0
- package/dist/vault-format.js +4 -0
- package/dist/vault-format.js.map +1 -0
- package/dist/vault-types.d.ts +24 -0
- package/dist/vault-types.d.ts.map +1 -0
- package/dist/vault-types.js +2 -0
- package/dist/vault-types.js.map +1 -0
- package/loop_policy.json +10 -12
- package/loopforge-policy.json +340 -0
- package/package.json +13 -7
- package/skills/perception/SKILL.md +200 -90
- package/dist/backends/interface.d.ts +0 -39
- package/dist/backends/interface.d.ts.map +0 -1
- package/dist/backends/interface.js +0 -6
- package/dist/backends/interface.js.map +0 -1
- package/dist/enforcement-gate.d.ts +0 -50
- package/dist/enforcement-gate.d.ts.map +0 -1
- package/dist/enforcement-gate.js +0 -501
- package/dist/enforcement-gate.js.map +0 -1
- package/dist/engine.d.ts +0 -59
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js.map +0 -1
- package/dist/interop.d.ts +0 -35
- package/dist/interop.d.ts.map +0 -1
- package/dist/interop.js +0 -34
- package/dist/interop.js.map +0 -1
- package/dist/run.d.ts +0 -51
- package/dist/run.d.ts.map +0 -1
- package/dist/run.js +0 -200
- package/dist/run.js.map +0 -1
- package/dist/self-eval.d.ts +0 -23
- package/dist/self-eval.d.ts.map +0 -1
- package/dist/self-eval.js +0 -151
- package/dist/self-eval.js.map +0 -1
- package/dist/verification-gate.d.ts +0 -52
- package/dist/verification-gate.d.ts.map +0 -1
- package/dist/verification-gate.js +0 -527
- package/dist/verification-gate.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,216 +1,170 @@
|
|
|
1
|
-
# LoopForge
|
|
1
|
+
# LoopForge package
|
|
2
2
|
|
|
3
|
-
LoopForge is a
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
sub-goal tracking, time-aware constraint decay, and adaptive prompt budgets.
|
|
3
|
+
LoopForge is a cognitive state runtime for AI coding agents. It keeps a long
|
|
4
|
+
task's objective, constraints, evidence, decisions and recovery state stable
|
|
5
|
+
across many Agent-driven rounds.
|
|
7
6
|
|
|
8
|
-
The Agent
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Agent trust and stop semantics provide long-horizon observability.
|
|
13
|
-
|
|
14
|
-
LoopForge is currently available as `2.7.0`. It requires Node.js 18 or
|
|
15
|
-
newer and has no runtime dependencies.
|
|
7
|
+
The Agent remains the executor: it reads code, edits files, runs tools and
|
|
8
|
+
decides how to reason. LoopForge is the external round boundary that checks
|
|
9
|
+
claims, commits canonical facts and rebuilds the next prompt. It is not a model,
|
|
10
|
+
scheduler, unattended worker or second planning system.
|
|
16
11
|
|
|
17
12
|
## Install
|
|
18
13
|
|
|
19
14
|
```bash
|
|
20
|
-
npm install -g loopforge
|
|
15
|
+
npm install -g loopforge
|
|
21
16
|
loopforge init --client claude
|
|
22
17
|
claude mcp add loopforge -- npx loopforge mcp
|
|
23
18
|
```
|
|
24
19
|
|
|
25
|
-
For Codex
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
loopforge init --client codex
|
|
29
|
-
codex mcp add loopforge -- npx loopforge mcp
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
For another MCP client:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
loopforge init --client generic
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
The generic command prints an MCP configuration fragment and installs the
|
|
39
|
-
Perception skill under `.loopforge/skills/` unless `--target` is supplied.
|
|
20
|
+
For Codex use `loopforge init --client codex` and register the same MCP command.
|
|
21
|
+
The generic initializer prints an MCP configuration fragment. The package
|
|
22
|
+
requires Node.js 18 or newer and has zero runtime dependencies.
|
|
40
23
|
|
|
41
24
|
## How a loop works
|
|
42
25
|
|
|
43
|
-
1. The Agent calls `loopforge_start` with
|
|
44
|
-
2. LoopForge compiles
|
|
45
|
-
3. The Agent executes that prompt and submits a structured
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
boundary, and either commits, rejects, or stops the round.
|
|
49
|
-
5. If work remains, the next prompt is compiled from the committed state.
|
|
50
|
-
|
|
51
|
-
A rejected attempt keeps the same logical round ID, increments its attempt
|
|
52
|
-
number, and commits no feedback. A process restart can recover the current prompt
|
|
53
|
-
and the last committed decision without advancing the round twice.
|
|
54
|
-
|
|
55
|
-
## Prompt views
|
|
26
|
+
1. The Agent calls `loopforge_start` with one objective and its hard constraints.
|
|
27
|
+
2. LoopForge compiles a prompt from canonical state.
|
|
28
|
+
3. The Agent executes that prompt and submits a structured `RoundReport`.
|
|
29
|
+
4. Runtime evidence is checked, the round boundary is enforced, and the result is accepted, rejected, backtracked or stopped.
|
|
30
|
+
5. If work remains, the next prompt is compiled again from committed facts.
|
|
56
31
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| --- | --- |
|
|
63
|
-
| L0 | Same-round retry with the rejection reason and changed evidence |
|
|
64
|
-
| L1 | Normal continuation with a compact state capsule |
|
|
65
|
-
| L2 | First round, checkpoint, goal change, or full state rehydration |
|
|
66
|
-
|
|
67
|
-
Every artifact records its state hash, prompt hash, level reasons, included
|
|
68
|
-
sections, character budget, round ID, and attempt number.
|
|
69
|
-
|
|
70
|
-
## Durable storage
|
|
71
|
-
|
|
72
|
-
Typed JSON documents are stored by loop under:
|
|
73
|
-
|
|
74
|
-
```text
|
|
75
|
-
.loopforge/
|
|
76
|
-
loops/<sha256(loopId)>/
|
|
77
|
-
metadata.json
|
|
78
|
-
session.json
|
|
79
|
-
rounds/<round>.json
|
|
80
|
-
state/<loopId>-state.md
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
The JSON session and round documents are the durable transaction truth. The
|
|
84
|
-
Markdown state file is a human-readable derived view and may be disabled with
|
|
85
|
-
`state_file.enabled`.
|
|
86
|
-
|
|
87
|
-
To import a pre-2.0 vault without deleting it:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
loopforge migrate
|
|
91
|
-
loopforge migrate --from path/to/prompt_vault.json --json
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
The migration is idempotent and writes a marker under `.loopforge/migrations/`.
|
|
95
|
-
|
|
96
|
-
## MCP tools
|
|
97
|
-
|
|
98
|
-
The stdio server exposes synchronous Agent-driven tools:
|
|
99
|
-
|
|
100
|
-
- `loopforge_start`
|
|
101
|
-
- `loopforge_next`
|
|
102
|
-
- `loopforge_status`
|
|
103
|
-
- `loopforge_pause`
|
|
104
|
-
- `loopforge_resume`
|
|
105
|
-
- `loopforge_stop`
|
|
106
|
-
- `loopforge_list`
|
|
107
|
-
- `loopforge_replay`
|
|
108
|
-
- `loopforge_health`
|
|
109
|
-
|
|
110
|
-
Tool results include `structuredContent` and a serialized text block for older
|
|
111
|
-
clients. LoopForge intentionally does not implement MCP Tasks. Long-running work
|
|
112
|
-
belongs to the Agent that is already executing the user's task.
|
|
113
|
-
|
|
114
|
-
Running sessions use renewable leases so two MCP processes cannot advance the
|
|
115
|
-
same loop at once. Paused and running sessions can be reconstructed from the
|
|
116
|
-
typed store after a restart.
|
|
117
|
-
|
|
118
|
-
## Command evidence
|
|
119
|
-
|
|
120
|
-
Git evidence is enabled by default. Verification commands are explicit and
|
|
121
|
-
disabled unless added to `loop_policy.json`:
|
|
32
|
+
The report deliberately separates `outcome` (`success`, `partial`, `failed` or
|
|
33
|
+
`blocked`) from `continuation` (`continue` or `stop`). This prevents a local
|
|
34
|
+
round result from being mistaken for completion of the whole objective. The old
|
|
35
|
+
`status` report and free-form evaluation text are not accepted at the public
|
|
36
|
+
boundary.
|
|
122
37
|
|
|
123
38
|
```json
|
|
124
39
|
{
|
|
125
|
-
"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
"cwd": ".",
|
|
135
|
-
"phase": "after",
|
|
136
|
-
"required": true,
|
|
137
|
-
"timeout_ms": 120000,
|
|
138
|
-
"max_output_chars": 20000,
|
|
139
|
-
"success_exit_codes": [0]
|
|
140
|
-
}
|
|
141
|
-
]
|
|
142
|
-
}
|
|
40
|
+
"format": "loopforge.round-report",
|
|
41
|
+
"outcome": "partial",
|
|
42
|
+
"continuation": "continue",
|
|
43
|
+
"summary": "Implemented the parser and ran focused tests.",
|
|
44
|
+
"violations": [],
|
|
45
|
+
"agentIntent": "Add parser regression coverage next",
|
|
46
|
+
"evidence": {"files": ["src/parser.ts"], "checks": [], "claims": [], "provenance": {}},
|
|
47
|
+
"discoveries": {"wrongAssumptions": [], "emergedWork": [], "facts": [], "newConstraints": []},
|
|
48
|
+
"subGoalEvents": {"completed": [], "blocked": [], "canceled": []}
|
|
143
49
|
}
|
|
144
50
|
```
|
|
145
51
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
52
|
+
`blocked` reports include a blocker. User decisions and external side effects
|
|
53
|
+
must stop at a User Gate; an Agent-resolvable technical blocker may continue
|
|
54
|
+
only with concrete evidence requirements.
|
|
55
|
+
|
|
56
|
+
## State model and prompt artifact
|
|
57
|
+
|
|
58
|
+
The package follows a single-source rule: Goal, RoundEvent, EvidenceEnvelope,
|
|
59
|
+
Constraint, SubGoal and GateDecision are durable facts; phase history, Current
|
|
60
|
+
Focus, Todo, Handoff, progress, failed patterns and prompt diagnostics are
|
|
61
|
+
rebuildable projections, never a competing plan or persistence layer. Compiler
|
|
62
|
+
responses keep those derived views in one `projection` object. The full model,
|
|
63
|
+
the L0/L1/L2 prompt levels and the immutable prompt artifact are described in
|
|
64
|
+
the [project README](https://github.com/kyrielrving11/LoopForge/blob/main/README.md)
|
|
65
|
+
under “Canonical cognitive state” and “One prompt artifact per attempt”.
|
|
66
|
+
|
|
67
|
+
The default store is `.loopforge`. Events are written through the canonical
|
|
68
|
+
writer and receive a monotonic `event_sequence` within each loop. Prompt and
|
|
69
|
+
state hashes make a restart or replay auditable. Markdown state is only a
|
|
70
|
+
replaceable view, not the recovery truth.
|
|
71
|
+
|
|
72
|
+
Evidence collection and recovery are asynchronous. A malformed report is an
|
|
73
|
+
input error; corrupt JSON, an invalid event format, or a sequence gap is a
|
|
74
|
+
`storage_corruption` error and is never auto-repaired. The configured
|
|
75
|
+
`maxRounds` must be a positive integer no greater than the policy limit.
|
|
76
|
+
|
|
77
|
+
## Verification, enforcement and gates
|
|
78
|
+
|
|
79
|
+
Git evidence is enabled by default. Explicit command providers run with an
|
|
80
|
+
executable and argument array (`shell: false`), bounded output, a timeout and a
|
|
81
|
+
workspace path check; required command failures contradict an unsupported
|
|
82
|
+
success claim. Agent claims remain `claimed` until a provider observes them —
|
|
83
|
+
provenance is owned by the runtime, and criteria coverage comes from verified
|
|
84
|
+
claims, never from a progress estimate. A successful report that requests a
|
|
85
|
+
stop is accepted as completion only when a live evidence provider produced
|
|
86
|
+
verifiable evidence; with no provider, completion stays unresolved.
|
|
87
|
+
|
|
88
|
+
Gate checks use `GateActionDescriptor` to make risk explicit: production,
|
|
89
|
+
credentials, data migration, publishing, payment, external communication,
|
|
90
|
+
public API changes, irreversible actions and unknown authorization are User
|
|
91
|
+
Gates; bounded, reversible, explicitly agent-allowed workspace work is an
|
|
92
|
+
Agent Gate. Decisions bind to an `actionHash`, so changing an action
|
|
93
|
+
invalidates an earlier approval.
|
|
94
|
+
|
|
95
|
+
For the full decision semantics — what the gate detects, how enforcement
|
|
96
|
+
decides accept, reject, backtrack and stop — see the corresponding sections in
|
|
97
|
+
the [project README](https://github.com/kyrielrving11/LoopForge/blob/main/README.md).
|
|
98
|
+
|
|
99
|
+
## Recovery semantics
|
|
100
|
+
|
|
101
|
+
A rejected attempt is zero-commit: its report does not enter canonical state.
|
|
102
|
+
`loopforge_retry` keeps the same logical round ID and increments the attempt;
|
|
103
|
+
`loopforge_next` cannot bypass a rejection. `loopforge_backtrack` returns a
|
|
104
|
+
bounded restore point and verification requirements but never changes workspace
|
|
105
|
+
files. Pause and resume preserve the transaction across client or process
|
|
106
|
+
boundaries. Stop records termination without deleting the vault.
|
|
107
|
+
|
|
108
|
+
`agentIntent` is an optional, non-authoritative suggestion retained inside the
|
|
109
|
+
report for audit and drift comparison. It never overwrites Current Focus, Todo,
|
|
110
|
+
runtime progress, completion, or the compiler's Suggested Next Action.
|
|
111
|
+
|
|
112
|
+
Terminal enforcement escalation is tracked per rule. A rule can issue one
|
|
113
|
+
guided retry before a repeated hit terminates it; accepting progress or
|
|
114
|
+
backtracking clears the active rejection episode.
|
|
115
|
+
|
|
116
|
+
## MCP surface
|
|
117
|
+
|
|
118
|
+
The server exposes exactly twelve Agent-driven tools:
|
|
151
119
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
loopforge doctor [--json]
|
|
158
|
-
loopforge inspect LOOP_ID [--round N] [--prompt] [--json]
|
|
159
|
-
loopforge migrate [--from PATH] [--json]
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
`inspect` hides full prompts unless `--prompt` is present. `doctor` checks the
|
|
163
|
-
Node version, store permissions, Git availability, and configured command
|
|
164
|
-
evidence paths without running verification commands.
|
|
165
|
-
|
|
166
|
-
## Library API
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
import { run } from "loopforge";
|
|
170
|
-
|
|
171
|
-
const result = await run({
|
|
172
|
-
task: "Audit this repository and fix confirmed defects",
|
|
173
|
-
constraintsFromPlan: ["Do not change the public API"],
|
|
174
|
-
execute: async (prompt, context) => {
|
|
175
|
-
return agent.execute(prompt, { signal: context.signal });
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
For an embedding that already owns long-term context or telemetry, use explicit
|
|
181
|
-
hooks instead of auto-discovery:
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
const result = await run({
|
|
185
|
-
task,
|
|
186
|
-
execute,
|
|
187
|
-
contextProvider: async ({ loopId, round, lastEvaluation }) => {
|
|
188
|
-
return myContextStore.read({ loopId, round, lastEvaluation });
|
|
189
|
-
},
|
|
190
|
-
terminalSinks: [async (event) => myTelemetry.record(event)],
|
|
191
|
-
});
|
|
120
|
+
```text
|
|
121
|
+
loopforge_start loopforge_next loopforge_retry
|
|
122
|
+
loopforge_backtrack loopforge_resume loopforge_pause
|
|
123
|
+
loopforge_stop loopforge_status loopforge_history
|
|
124
|
+
loopforge_gate_check loopforge_gate_resolve loopforge_audit
|
|
192
125
|
```
|
|
193
126
|
|
|
194
|
-
|
|
195
|
-
|
|
127
|
+
`status` combines session, loop and persisted-loop views. `history` combines
|
|
128
|
+
single-round inspection and event-timeline replay with sequence pagination. The
|
|
129
|
+
old `list`, `health`, `inspect` and `replay` MCP names are not aliases. MCP
|
|
130
|
+
Tasks and background Agent execution are intentionally outside this package.
|
|
131
|
+
|
|
132
|
+
## Long-horizon use
|
|
133
|
+
|
|
134
|
+
LoopForge can support dozens or hundreds of explicit rounds, context
|
|
135
|
+
compression, restart, rejection and backtrack because every prompt is compiled
|
|
136
|
+
from durable facts rather than from a summary of the previous prompt. It does
|
|
137
|
+
not keep an Agent running after the client task ends. Use one loop per clear
|
|
138
|
+
objective; for a large roadmap, let the Agent carry explicit outcomes between
|
|
139
|
+
independent loops instead of creating an endless session.
|
|
140
|
+
|
|
141
|
+
## Reliability boundaries
|
|
142
|
+
|
|
143
|
+
Malformed canonical reports are rejected without changing the current session,
|
|
144
|
+
round or vault, so callers can retry the same logical round. Persistence
|
|
145
|
+
failures are returned as errors and never become a successful or partial
|
|
146
|
+
commit. Drift clarification requires an explanation and a runtime-verifiable
|
|
147
|
+
anchor; agent waiver flags are not accepted. `loopforge doctor` diagnoses
|
|
148
|
+
unknown, deprecated, type-invalid and removed policy keys. Automatic Git
|
|
149
|
+
restore is intentionally not implemented. Persistence and evidence collection
|
|
150
|
+
are asynchronous; corrupt JSON, invalid event formats and sequence gaps return
|
|
151
|
+
`storage_corruption` instead of a partial status. `maxRounds` must be a positive
|
|
152
|
+
integer within the configured policy limit.
|
|
196
153
|
|
|
197
154
|
## Development
|
|
198
155
|
|
|
199
156
|
```bash
|
|
200
157
|
npm run check
|
|
201
158
|
npm test
|
|
202
|
-
npm
|
|
159
|
+
npm run build
|
|
160
|
+
npm run verify:artifacts
|
|
161
|
+
npm pack --dry-run --json --ignore-scripts
|
|
203
162
|
```
|
|
204
163
|
|
|
205
|
-
The schema in
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
The 2.0 release removes the prompt-technique catalog, strategy heuristics, MCP
|
|
211
|
-
Tasks, automatic memory discovery, global PromptCraft vaults, Markdown lineage
|
|
212
|
-
fallbacks, `Technique` and `Analysis` wire fields, and the `loopforge-mcp`
|
|
213
|
-
binary. Use `loopforge mcp`, the typed `FileLoopStore`, and explicit providers.
|
|
164
|
+
The schema in the repository's `loopforge-protocol.json` is generated from
|
|
165
|
+
`src/protocol.ts`.
|
|
166
|
+
Do not edit it by hand. `init` installs `skills/perception/SKILL.md` for the
|
|
167
|
+
selected client and prints its MCP registration command.
|
|
214
168
|
|
|
215
169
|
## License
|
|
216
170
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LoopStore } from "./loop-store.js";
|
|
2
|
+
import type { VaultEntry } from "./vault-types.js";
|
|
3
|
+
/** Single write boundary for canonical loop events.
|
|
4
|
+
*
|
|
5
|
+
* Stores may provide their own atomic sequence allocator (FileLoopStore does),
|
|
6
|
+
* but the writer also assigns and validates sequences for lightweight test or
|
|
7
|
+
* embedding backends. Every runtime event therefore enters the Vault through
|
|
8
|
+
* one deterministic path.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CanonicalEventWriter {
|
|
11
|
+
private readonly backend;
|
|
12
|
+
constructor(backend: LoopStore);
|
|
13
|
+
private allEntries;
|
|
14
|
+
private replaceExisting;
|
|
15
|
+
private existing;
|
|
16
|
+
append(entry: VaultEntry): VaultEntry;
|
|
17
|
+
appendBatch(entries: VaultEntry[]): VaultEntry[];
|
|
18
|
+
private assertSequence;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=canonical-event-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-event-writer.d.ts","sourceRoot":"","sources":["../src/canonical-event-writer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;;GAMG;AACH,qBAAa,oBAAoB;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,SAAS;IAE/C,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,QAAQ;IAMhB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;IAkCrC,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,UAAU,EAAE;IAuChD,OAAO,CAAC,cAAc;CAiBvB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { StorageCorruptionError } from "./loop-store.js";
|
|
2
|
+
/** Single write boundary for canonical loop events.
|
|
3
|
+
*
|
|
4
|
+
* Stores may provide their own atomic sequence allocator (FileLoopStore does),
|
|
5
|
+
* but the writer also assigns and validates sequences for lightweight test or
|
|
6
|
+
* embedding backends. Every runtime event therefore enters the Vault through
|
|
7
|
+
* one deterministic path.
|
|
8
|
+
*/
|
|
9
|
+
export class CanonicalEventWriter {
|
|
10
|
+
backend;
|
|
11
|
+
constructor(backend) {
|
|
12
|
+
this.backend = backend;
|
|
13
|
+
}
|
|
14
|
+
allEntries() {
|
|
15
|
+
// Global replacement needs entries from every loop, including backends
|
|
16
|
+
// whose default query excludes canonical `:event` records. Keep this
|
|
17
|
+
// fallback centralized here; per-loop runtime reads use queryLoopEvents.
|
|
18
|
+
const entries = this.backend.listLoopIds()
|
|
19
|
+
.flatMap((loopId) => this.backend.queryLoopEvents(loopId));
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
return entries.filter((entry) => {
|
|
22
|
+
const key = `${entry.task_id ?? entry.id ?? ""}:${entry.event_sequence ?? ""}`;
|
|
23
|
+
if (seen.has(key))
|
|
24
|
+
return false;
|
|
25
|
+
seen.add(key);
|
|
26
|
+
return true;
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
replaceExisting(loopId, updated) {
|
|
30
|
+
const all = this.allEntries();
|
|
31
|
+
const filtered = all.filter((candidate) => !(candidate.loop_id === loopId && candidate.task_id === updated.task_id));
|
|
32
|
+
const replacement = [...filtered, updated];
|
|
33
|
+
this.backend.replaceEntries(replacement);
|
|
34
|
+
}
|
|
35
|
+
existing(loopId) {
|
|
36
|
+
const entries = this.backend.queryLoopEvents(loopId);
|
|
37
|
+
this.assertSequence(loopId, entries);
|
|
38
|
+
return entries;
|
|
39
|
+
}
|
|
40
|
+
append(entry) {
|
|
41
|
+
const write = () => {
|
|
42
|
+
const loopId = entry.loop_id;
|
|
43
|
+
if (!loopId)
|
|
44
|
+
throw new Error("Canonical events require loop_id");
|
|
45
|
+
if (entry.loop_id !== loopId)
|
|
46
|
+
throw new StorageCorruptionError("sequence_cross_loop", loopId, loopId);
|
|
47
|
+
const existing = this.existing(loopId);
|
|
48
|
+
const prior = existing.find((candidate) => candidate.task_id === entry.task_id);
|
|
49
|
+
if (prior && Number.isInteger(prior.event_sequence)) {
|
|
50
|
+
const updated = { ...entry, event_sequence: prior.event_sequence };
|
|
51
|
+
const priorComparable = { ...prior, id: undefined, timestamp: undefined };
|
|
52
|
+
const updatedComparable = { ...updated, id: undefined, timestamp: undefined };
|
|
53
|
+
if (JSON.stringify(priorComparable) === JSON.stringify(updatedComparable))
|
|
54
|
+
return prior;
|
|
55
|
+
this.replaceExisting(loopId, updated);
|
|
56
|
+
return updated;
|
|
57
|
+
}
|
|
58
|
+
const max = existing.reduce((value, candidate) => typeof candidate.event_sequence === "number" && Number.isInteger(candidate.event_sequence)
|
|
59
|
+
? Math.max(value, candidate.event_sequence)
|
|
60
|
+
: value, 0);
|
|
61
|
+
const next = max + 1;
|
|
62
|
+
const sequenced = { ...entry, event_sequence: entry.event_sequence ?? next };
|
|
63
|
+
if (!Number.isInteger(sequenced.event_sequence) || sequenced.event_sequence < 1 ||
|
|
64
|
+
existing.some((candidate) => candidate.event_sequence === sequenced.event_sequence)) {
|
|
65
|
+
throw new StorageCorruptionError("sequence_duplicate", loopId, loopId, sequenced.event_sequence);
|
|
66
|
+
}
|
|
67
|
+
if (sequenced.event_sequence !== next) {
|
|
68
|
+
throw new StorageCorruptionError("sequence_gap", loopId, loopId, sequenced.event_sequence);
|
|
69
|
+
}
|
|
70
|
+
this.backend.appendEntry(sequenced);
|
|
71
|
+
return sequenced;
|
|
72
|
+
};
|
|
73
|
+
return this.backend.withLock(write);
|
|
74
|
+
}
|
|
75
|
+
appendBatch(entries) {
|
|
76
|
+
if (entries.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
const write = () => {
|
|
79
|
+
const loopIds = new Set(entries.map((entry) => entry.loop_id));
|
|
80
|
+
if (loopIds.size !== 1 || loopIds.has(undefined)) {
|
|
81
|
+
throw new Error("A canonical event batch must belong to one loop");
|
|
82
|
+
}
|
|
83
|
+
const loopId = [...loopIds][0];
|
|
84
|
+
const existing = this.existing(loopId);
|
|
85
|
+
const existingTaskIds = new Set(existing.map((candidate) => candidate.task_id).filter(Boolean));
|
|
86
|
+
const batchTaskIds = entries.map((entry) => entry.task_id).filter(Boolean);
|
|
87
|
+
if (batchTaskIds.length !== new Set(batchTaskIds).size ||
|
|
88
|
+
batchTaskIds.some((taskId) => existingTaskIds.has(taskId))) {
|
|
89
|
+
throw new Error(`Duplicate canonical task id in event batch for loop ${loopId}`);
|
|
90
|
+
}
|
|
91
|
+
const used = new Set(existing
|
|
92
|
+
.map((candidate) => candidate.event_sequence)
|
|
93
|
+
.filter((value) => typeof value === "number" && Number.isInteger(value)));
|
|
94
|
+
let next = existing.reduce((value, candidate) => typeof candidate.event_sequence === "number" && Number.isInteger(candidate.event_sequence)
|
|
95
|
+
? Math.max(value, candidate.event_sequence)
|
|
96
|
+
: value, 0) + 1;
|
|
97
|
+
const sequenced = entries.map((entry) => ({ ...entry, event_sequence: entry.event_sequence ?? next++ }));
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
for (const entry of sequenced) {
|
|
100
|
+
if (!Number.isInteger(entry.event_sequence) || used.has(entry.event_sequence) || seen.has(entry.event_sequence)) {
|
|
101
|
+
throw new StorageCorruptionError("sequence_duplicate", loopId, loopId, entry.event_sequence);
|
|
102
|
+
}
|
|
103
|
+
if (entry.event_sequence !== (existing.length ? Math.max(...used) : 0) + 1 + seen.size) {
|
|
104
|
+
throw new StorageCorruptionError("sequence_gap", loopId, loopId, entry.event_sequence);
|
|
105
|
+
}
|
|
106
|
+
seen.add(entry.event_sequence);
|
|
107
|
+
}
|
|
108
|
+
this.backend.appendEntries(sequenced);
|
|
109
|
+
return sequenced;
|
|
110
|
+
};
|
|
111
|
+
return this.backend.withLock(write);
|
|
112
|
+
}
|
|
113
|
+
assertSequence(loopId, entries) {
|
|
114
|
+
const sorted = [...entries].sort((a, b) => (a.event_sequence ?? 0) - (b.event_sequence ?? 0));
|
|
115
|
+
const seen = new Set();
|
|
116
|
+
for (let index = 0; index < sorted.length; index++) {
|
|
117
|
+
const sequence = sorted[index].event_sequence;
|
|
118
|
+
if (!Number.isInteger(sequence) || sequence < 1) {
|
|
119
|
+
throw new StorageCorruptionError("sequence_invalid", loopId, loopId, sequence);
|
|
120
|
+
}
|
|
121
|
+
if (seen.has(sequence)) {
|
|
122
|
+
throw new StorageCorruptionError("sequence_duplicate", loopId, loopId, sequence);
|
|
123
|
+
}
|
|
124
|
+
seen.add(sequence);
|
|
125
|
+
if (sequence !== index + 1) {
|
|
126
|
+
throw new StorageCorruptionError("sequence_gap", loopId, loopId, sequence);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=canonical-event-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-event-writer.js","sourceRoot":"","sources":["../src/canonical-event-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,17 +3,16 @@
|
|
|
3
3
|
* The canonical state is data, not Markdown. Prompt and state-file renderers
|
|
4
4
|
* consume the same value so they cannot silently drift apart.
|
|
5
5
|
*/
|
|
6
|
-
import type { ConstraintMeta, LoopCompileRequest, LoopCompileResponse,
|
|
7
|
-
export declare const
|
|
6
|
+
import type { ConstraintMeta, LoopCompileRequest, LoopCompileResponse, SubGoal, VerificationFlag, DerivedGate, HandoffProjection, PhaseHistoryEntry, TodoProjection } from "./protocol.js";
|
|
7
|
+
export declare const CANONICAL_STATE_FORMAT: "loopforge.state";
|
|
8
8
|
export interface CanonicalLoopState {
|
|
9
|
-
|
|
9
|
+
format: "loopforge.state";
|
|
10
10
|
loopId: string;
|
|
11
11
|
round: number;
|
|
12
12
|
maxRounds: number;
|
|
13
13
|
goalId: string;
|
|
14
14
|
objective: string;
|
|
15
15
|
objectiveVersion: number;
|
|
16
|
-
currentTask: string;
|
|
17
16
|
successCriteria: string[];
|
|
18
17
|
hardConstraints: string[];
|
|
19
18
|
activeConstraints: string[];
|
|
@@ -27,20 +26,9 @@ export interface CanonicalLoopState {
|
|
|
27
26
|
blockers: string[];
|
|
28
27
|
verificationFlags: VerificationFlag[];
|
|
29
28
|
discoveries: string[];
|
|
30
|
-
nextAction: string;
|
|
31
29
|
rollingOutcomes: string[];
|
|
32
30
|
recurringIssues: string[];
|
|
33
31
|
failedPatterns: string[];
|
|
34
|
-
/** v2.6: derived from agent_declared milestones. */
|
|
35
|
-
checkpoints: Array<{
|
|
36
|
-
label: string;
|
|
37
|
-
declared_at_round: number;
|
|
38
|
-
outcome: string;
|
|
39
|
-
carried_constraints: string[];
|
|
40
|
-
resolved_constraints: string[];
|
|
41
|
-
}>;
|
|
42
|
-
/** v2.1: Phase-boundary milestone summaries that survive window eviction. */
|
|
43
|
-
milestones: MilestoneSummary[];
|
|
44
32
|
/** v2.1: Single-paragraph loop-level synthesis (formulaic). */
|
|
45
33
|
loopSynthesis: string;
|
|
46
34
|
/** v2.2: Structured sub-goals with compiler-managed lifecycle. */
|
|
@@ -49,7 +37,6 @@ export interface CanonicalLoopState {
|
|
|
49
37
|
agentTrustScore: number | undefined;
|
|
50
38
|
/** v2.5: Trust trend over last 10 rounds. */
|
|
51
39
|
agentTrustTrend: number[];
|
|
52
|
-
suggestedNextTask: string;
|
|
53
40
|
externalContext: string;
|
|
54
41
|
stateFilePath: string;
|
|
55
42
|
progress: {
|
|
@@ -63,6 +50,23 @@ export interface CanonicalLoopState {
|
|
|
63
50
|
skipped: number;
|
|
64
51
|
} | null;
|
|
65
52
|
};
|
|
53
|
+
/** Derived obligations that must be rechecked after a change. */
|
|
54
|
+
regressionObligations: Array<{
|
|
55
|
+
id: string;
|
|
56
|
+
description: string;
|
|
57
|
+
sourceRound: number;
|
|
58
|
+
evidenceRefs: string[];
|
|
59
|
+
status: "open" | "satisfied" | "contradicted";
|
|
60
|
+
}>;
|
|
61
|
+
/** Derived views. They are rebuilt on every compile and are not a
|
|
62
|
+
* second transaction truth. */
|
|
63
|
+
phaseHistory: PhaseHistoryEntry[];
|
|
64
|
+
currentFocus: string;
|
|
65
|
+
todoProjection: TodoProjection | null;
|
|
66
|
+
suggestedNextAction: string;
|
|
67
|
+
activeGate: DerivedGate | null;
|
|
68
|
+
stateFreshness: "fresh" | "stale" | "missing";
|
|
69
|
+
handoff: HandoffProjection;
|
|
66
70
|
}
|
|
67
71
|
/** Deterministic JSON serialization used by state and prompt hashes. */
|
|
68
72
|
export declare function stableStringify(value: unknown): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canonical-state.d.ts","sourceRoot":"","sources":["../src/canonical-state.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"canonical-state.d.ts","sourceRoot":"","sources":["../src/canonical-state.ts"],"names":[],"mappings":"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"}
|