loopforge 1.15.1 → 2.0.1
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 +172 -95
- package/dist/backends/interface.d.ts +5 -10
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/backends/interface.js +3 -4
- package/dist/backends/interface.js.map +1 -1
- package/dist/canonical-state.d.ts +53 -0
- package/dist/canonical-state.d.ts.map +1 -0
- package/dist/canonical-state.js +161 -0
- package/dist/canonical-state.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +227 -0
- package/dist/cli.js.map +1 -0
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +28 -10
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +9 -28
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +135 -361
- package/dist/engine.js.map +1 -1
- package/dist/evidence-provider.d.ts +146 -0
- package/dist/evidence-provider.d.ts.map +1 -0
- package/dist/evidence-provider.js +512 -0
- package/dist/evidence-provider.js.map +1 -0
- package/dist/index.d.ts +19 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -12
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +15 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +49 -0
- package/dist/init.js.map +1 -0
- package/dist/interop.d.ts +35 -0
- package/dist/interop.d.ts.map +1 -0
- package/dist/interop.js +34 -0
- package/dist/interop.js.map +1 -0
- package/dist/loop-compiler.d.ts +14 -64
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +305 -2206
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-extras-parser.d.ts +82 -0
- package/dist/loop-extras-parser.d.ts.map +1 -0
- package/dist/loop-extras-parser.js +217 -0
- package/dist/loop-extras-parser.js.map +1 -0
- package/dist/loop-store.d.ts +104 -0
- package/dist/loop-store.d.ts.map +1 -0
- package/dist/loop-store.js +421 -0
- package/dist/loop-store.js.map +1 -0
- package/dist/mcp/server.d.ts +9 -6
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +109 -73
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +102 -21
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +795 -400
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.d.ts +18 -268
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +226 -12
- package/dist/mcp/tools.js.map +1 -1
- package/dist/observability.d.ts +36 -16
- package/dist/observability.d.ts.map +1 -1
- package/dist/observability.js +82 -23
- package/dist/observability.js.map +1 -1
- package/dist/policy-metrics.d.ts +46 -0
- package/dist/policy-metrics.d.ts.map +1 -0
- package/dist/policy-metrics.js +146 -0
- package/dist/policy-metrics.js.map +1 -0
- package/dist/policy.d.ts +52 -147
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +118 -191
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts +30 -0
- package/dist/prompt-assembler.d.ts.map +1 -0
- package/dist/prompt-assembler.js +278 -0
- package/dist/prompt-assembler.js.map +1 -0
- package/dist/prompt-policy.d.ts +29 -0
- package/dist/prompt-policy.d.ts.map +1 -0
- package/dist/prompt-policy.js +51 -0
- package/dist/prompt-policy.js.map +1 -0
- package/dist/protocol.d.ts +75 -152
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +11 -53
- package/dist/protocol.js.map +1 -1
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +1 -27
- package/dist/replay.js.map +1 -1
- package/dist/round-coordinator.d.ts +90 -0
- package/dist/round-coordinator.d.ts.map +1 -0
- package/dist/round-coordinator.js +186 -0
- package/dist/round-coordinator.js.map +1 -0
- package/dist/round-driver.d.ts +53 -0
- package/dist/round-driver.d.ts.map +1 -0
- package/dist/round-driver.js +101 -0
- package/dist/round-driver.js.map +1 -0
- package/dist/round-transaction.d.ts +70 -0
- package/dist/round-transaction.d.ts.map +1 -0
- package/dist/round-transaction.js +290 -0
- package/dist/round-transaction.js.map +1 -0
- package/dist/self-eval.d.ts +26 -0
- package/dist/self-eval.d.ts.map +1 -0
- package/dist/self-eval.js +154 -0
- package/dist/self-eval.js.map +1 -0
- package/dist/storage.d.ts +39 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +193 -0
- package/dist/storage.js.map +1 -0
- package/dist/verification-gate.d.ts +31 -7
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +250 -7
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +19 -53
- package/package.json +13 -10
- package/skills/perception/SKILL.md +80 -267
- package/dist/backends/fs.d.ts +0 -36
- package/dist/backends/fs.d.ts.map +0 -1
- package/dist/backends/fs.js +0 -362
- package/dist/backends/fs.js.map +0 -1
- package/dist/builder.d.ts +0 -19
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js +0 -215
- package/dist/builder.js.map +0 -1
- package/dist/mcp-server.d.ts +0 -12
- package/dist/mcp-server.d.ts.map +0 -1
- package/dist/mcp-server.js +0 -14
- package/dist/mcp-server.js.map +0 -1
- package/dist/memory-bridge.d.ts +0 -47
- package/dist/memory-bridge.d.ts.map +0 -1
- package/dist/memory-bridge.js +0 -290
- package/dist/memory-bridge.js.map +0 -1
- package/dist/runtime.d.ts +0 -80
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -699
- package/dist/runtime.js.map +0 -1
- package/dist/tests/_helpers.d.ts +0 -25
- package/dist/tests/_helpers.d.ts.map +0 -1
- package/dist/tests/_helpers.js +0 -47
- package/dist/tests/_helpers.js.map +0 -1
- package/dist/tests/builder.test.d.ts +0 -2
- package/dist/tests/builder.test.d.ts.map +0 -1
- package/dist/tests/builder.test.js +0 -112
- package/dist/tests/builder.test.js.map +0 -1
- package/dist/tests/enforcement-gate.test.d.ts +0 -2
- package/dist/tests/enforcement-gate.test.d.ts.map +0 -1
- package/dist/tests/enforcement-gate.test.js +0 -328
- package/dist/tests/enforcement-gate.test.js.map +0 -1
- package/dist/tests/engine.test.d.ts +0 -2
- package/dist/tests/engine.test.d.ts.map +0 -1
- package/dist/tests/engine.test.js +0 -375
- package/dist/tests/engine.test.js.map +0 -1
- package/dist/tests/generate-schema.test.d.ts +0 -8
- package/dist/tests/generate-schema.test.d.ts.map +0 -1
- package/dist/tests/generate-schema.test.js +0 -199
- package/dist/tests/generate-schema.test.js.map +0 -1
- package/dist/tests/loop-compiler.test.d.ts +0 -2
- package/dist/tests/loop-compiler.test.d.ts.map +0 -1
- package/dist/tests/loop-compiler.test.js +0 -1059
- package/dist/tests/loop-compiler.test.js.map +0 -1
- package/dist/tests/mcp.test.d.ts +0 -7
- package/dist/tests/mcp.test.d.ts.map +0 -1
- package/dist/tests/mcp.test.js +0 -505
- package/dist/tests/mcp.test.js.map +0 -1
- package/dist/tests/memory-bridge.test.d.ts +0 -8
- package/dist/tests/memory-bridge.test.d.ts.map +0 -1
- package/dist/tests/memory-bridge.test.js +0 -383
- package/dist/tests/memory-bridge.test.js.map +0 -1
- package/dist/tests/policy.test.d.ts +0 -2
- package/dist/tests/policy.test.d.ts.map +0 -1
- package/dist/tests/policy.test.js +0 -49
- package/dist/tests/policy.test.js.map +0 -1
- package/dist/tests/protocol.test.d.ts +0 -2
- package/dist/tests/protocol.test.d.ts.map +0 -1
- package/dist/tests/protocol.test.js +0 -73
- package/dist/tests/protocol.test.js.map +0 -1
- package/dist/tests/replay.test.d.ts +0 -2
- package/dist/tests/replay.test.d.ts.map +0 -1
- package/dist/tests/replay.test.js +0 -222
- package/dist/tests/replay.test.js.map +0 -1
- package/dist/tests/runtime.test.d.ts +0 -7
- package/dist/tests/runtime.test.d.ts.map +0 -1
- package/dist/tests/runtime.test.js +0 -337
- package/dist/tests/runtime.test.js.map +0 -1
- package/dist/tests/verification-gate.test.d.ts +0 -2
- package/dist/tests/verification-gate.test.d.ts.map +0 -1
- package/dist/tests/verification-gate.test.js +0 -407
- package/dist/tests/verification-gate.test.js.map +0 -1
- package/skills/prompt-techniques/SKILL.md +0 -52
- package/skills/prompt-techniques/references/chain-of-thought.md +0 -207
- package/skills/prompt-techniques/references/few-shot.md +0 -174
- package/skills/prompt-techniques/references/least-to-most.md +0 -182
- package/skills/prompt-techniques/references/step-back.md +0 -160
- package/skills/prompt-techniques/references/tree-of-thought.md +0 -191
- package/skills/prompt-techniques/references/zero-shot.md +0 -97
package/dist/policy.js
CHANGED
|
@@ -1,141 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
import { resolve } from "node:path";
|
|
7
|
-
/** Resolve which injection phases are allowed for a given maxRounds.
|
|
8
|
-
* Uses the tiered strategy: finds the first tier where maxRounds ≤ tier.max_rounds,
|
|
9
|
-
* and returns its allowed_phases. If maxRounds exceeds all tiers, returns the
|
|
10
|
-
* last tier's phases. */
|
|
11
|
-
export function resolveAllowedPhases(maxRounds, tiers) {
|
|
12
|
-
if (tiers.length === 0)
|
|
13
|
-
return [1]; // safety fallback
|
|
14
|
-
for (const tier of tiers) {
|
|
15
|
-
if (maxRounds <= tier.max_rounds)
|
|
16
|
-
return tier.allowed_phases;
|
|
17
|
-
}
|
|
18
|
-
return tiers[tiers.length - 1].allowed_phases;
|
|
19
|
-
}
|
|
20
|
-
/** Resolve which memory injection phase (1/2/3) should fire this round.
|
|
21
|
-
* Shared between LoopRuntime (runtime.ts) and SessionManager (mcp/session.ts).
|
|
22
|
-
* Returns the phase number, or 0 if no injection should occur this round.
|
|
23
|
-
*
|
|
24
|
-
* @param currentRound Current round number (1-based).
|
|
25
|
-
* @param injectionCount How many injections have already occurred.
|
|
26
|
-
* @param allowedPhases Phases allowed by the round-tier policy.
|
|
27
|
-
* @param progress Current progress estimate (-1 if unavailable).
|
|
28
|
-
* @param phase2Triggered Whether phase 2 has already fired in this loop.
|
|
29
|
-
* @param phase3Triggered Whether phase 3 has already fired in this loop.
|
|
30
|
-
* @param thresholds Policy threshold config for phase2/phase3. */
|
|
31
|
-
export function resolveInjectionPhase(currentRound, injectionCount, allowedPhases, progress, phase2Triggered, phase3Triggered, thresholds) {
|
|
32
|
-
const maxInjections = allowedPhases.size;
|
|
33
|
-
if (injectionCount >= maxInjections)
|
|
34
|
-
return 0;
|
|
35
|
-
// Phase 1: round 1 only
|
|
36
|
-
if (currentRound === 1 && injectionCount === 0 && allowedPhases.has(1)) {
|
|
37
|
-
return 1;
|
|
38
|
-
}
|
|
39
|
-
const hasProgress = progress >= 0;
|
|
40
|
-
// Phase 2: progress threshold (only if allowed by tier and not yet triggered)
|
|
41
|
-
if (allowedPhases.has(2) &&
|
|
42
|
-
!phase2Triggered &&
|
|
43
|
-
hasProgress &&
|
|
44
|
-
progress >= thresholds.phase2) {
|
|
45
|
-
return 2;
|
|
46
|
-
}
|
|
47
|
-
// Phase 3: progress threshold (only if allowed by tier and not yet triggered)
|
|
48
|
-
if (allowedPhases.has(3) &&
|
|
49
|
-
!phase3Triggered &&
|
|
50
|
-
hasProgress &&
|
|
51
|
-
progress >= thresholds.phase3) {
|
|
52
|
-
return 3;
|
|
53
|
-
}
|
|
54
|
-
return 0;
|
|
55
|
-
}
|
|
56
|
-
/** Build accumulated context for a targeted memory query from a SelfEvaluation.
|
|
57
|
-
* Extracts recurring issues, key lessons, and remaining criteria.
|
|
58
|
-
* Shared between LoopRuntime (runtime.ts) and SessionManager (mcp/session.ts). */
|
|
59
|
-
export function buildAccumulatedMemoryContext(selfEval) {
|
|
60
|
-
const recurringIssues = [];
|
|
61
|
-
const failedPatterns = [];
|
|
62
|
-
const keyLessons = [];
|
|
63
|
-
const remainingCriteria = [];
|
|
64
|
-
if (selfEval.constraint_violations.length) {
|
|
65
|
-
recurringIssues.push(...selfEval.constraint_violations);
|
|
66
|
-
}
|
|
67
|
-
if (selfEval.execution_evidence?.success_criteria_remaining?.length) {
|
|
68
|
-
remainingCriteria.push(...selfEval.execution_evidence.success_criteria_remaining);
|
|
69
|
-
}
|
|
70
|
-
if (selfEval.emerged_subtasks?.length) {
|
|
71
|
-
keyLessons.push(...selfEval.emerged_subtasks);
|
|
72
|
-
}
|
|
73
|
-
if (selfEval.wrong_assumptions?.length) {
|
|
74
|
-
keyLessons.push(...selfEval.wrong_assumptions.map((a) => `Wrong: ${a}`));
|
|
75
|
-
}
|
|
76
|
-
return { recurringIssues, failedPatterns, keyLessons, remainingCriteria };
|
|
77
|
-
}
|
|
78
|
-
/** Build a base LoopMemoryWriteback payload from loop terminal state.
|
|
79
|
-
* Shared between LoopRuntime (runtime.ts) and SessionManager (mcp/session.ts).
|
|
80
|
-
* Callers may layer additional feedback entries on top of the returned payload. */
|
|
81
|
-
export function buildBaseMemoryWriteback(params) {
|
|
82
|
-
const wp = getPolicy().memory_writeback;
|
|
83
|
-
const outcome = (params.stopReason === "task_complete" ? "completed" :
|
|
84
|
-
params.stopReason === "circuit_breaker" ? "circuit_breaker" :
|
|
85
|
-
params.stopReason === "stalled" ? "stalled" :
|
|
86
|
-
params.stopReason === "max_rounds" ? "max_rounds" :
|
|
87
|
-
"stopped");
|
|
88
|
-
return {
|
|
89
|
-
loopId: params.loopId,
|
|
90
|
-
task: params.task,
|
|
91
|
-
outcome,
|
|
92
|
-
roundsCompleted: params.roundsCompleted,
|
|
93
|
-
projectEntry: {
|
|
94
|
-
title: `${params.task.slice(0, 80)} — ${outcome}`,
|
|
95
|
-
objective: params.task.slice(0, 200),
|
|
96
|
-
keyOutcome: params.stopReason === "task_complete"
|
|
97
|
-
? `Completed successfully in ${params.roundsCompleted} rounds.`
|
|
98
|
-
: `Terminated with reason '${params.stopReason}' after ${params.roundsCompleted} rounds.`,
|
|
99
|
-
keyDiscoveries: params.discoveries.slice(0, wp.max_discoveries_in_project),
|
|
100
|
-
date: new Date().toISOString().split("T")[0],
|
|
101
|
-
},
|
|
102
|
-
referenceEntry: {
|
|
103
|
-
description: `LoopForge vault data for "${params.task.slice(0, 80)}"`,
|
|
104
|
-
vaultLocation: `.promptcraft/prompt_vault.json → loop:${params.loopId}:*`,
|
|
105
|
-
},
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
109
|
-
// Default policy
|
|
110
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
1
|
+
/** Externalized LoopForge runtime policy. */
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from "node:fs";
|
|
4
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
111
5
|
export const DEFAULT_POLICY = {
|
|
112
|
-
version: "
|
|
113
|
-
constraints: {
|
|
114
|
-
|
|
115
|
-
},
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
},
|
|
124
|
-
engine: {
|
|
125
|
-
feedback_flush_interval: 5,
|
|
126
|
-
max_circuit_breaker: 3,
|
|
127
|
-
},
|
|
128
|
-
runtime: {
|
|
129
|
-
max_rounds: 20,
|
|
130
|
-
round_timeout_ms: 600_000,
|
|
131
|
-
heartbeat_interval_ms: 30_000,
|
|
132
|
-
stall_grace_ms: 300_000,
|
|
133
|
-
max_consecutive_errors: 3,
|
|
134
|
-
},
|
|
135
|
-
backend: {
|
|
136
|
-
vault_path: ".promptcraft/prompt_vault.json",
|
|
137
|
-
global_vault_path: "~/.promptcraft/global_vault.json",
|
|
6
|
+
version: "2",
|
|
7
|
+
constraints: { retire_window: 3 },
|
|
8
|
+
summary: { window: 5, health_check_interval: 1 },
|
|
9
|
+
engine: { feedback_flush_interval: 5, max_circuit_breaker: 3, max_rounds: 20 },
|
|
10
|
+
prompt: {
|
|
11
|
+
injection_mode: "adaptive",
|
|
12
|
+
full_refresh_interval: 5,
|
|
13
|
+
l0_max_chars: 3000,
|
|
14
|
+
l1_max_chars: 7000,
|
|
15
|
+
l2_max_chars: 18000,
|
|
16
|
+
base_prompt_version: "2.0.0",
|
|
138
17
|
},
|
|
18
|
+
backend: { root_dir: ".loopforge" },
|
|
139
19
|
evolution: {
|
|
140
20
|
max_discovered_constraints_per_round: 5,
|
|
141
21
|
max_active_constraints: 15,
|
|
@@ -144,90 +24,137 @@ export const DEFAULT_POLICY = {
|
|
|
144
24
|
progress_stall_rounds: 2,
|
|
145
25
|
progress_mismatch_threshold: 0.3,
|
|
146
26
|
},
|
|
147
|
-
|
|
148
|
-
enabled: true,
|
|
149
|
-
min_rounds_between_injections: 1,
|
|
150
|
-
phase_thresholds: {
|
|
151
|
-
phase1: { trigger: "round_1" },
|
|
152
|
-
phase2: { trigger: "progress", threshold: 0.40 },
|
|
153
|
-
phase3: { trigger: "progress", threshold: 0.70 },
|
|
154
|
-
},
|
|
155
|
-
round_tiers: [
|
|
156
|
-
{ max_rounds: 10, allowed_phases: [1] },
|
|
157
|
-
{ max_rounds: 20, allowed_phases: [1, 3] },
|
|
158
|
-
{ max_rounds: 30, allowed_phases: [1, 2, 3] },
|
|
159
|
-
],
|
|
160
|
-
dedup_threshold: 0.6,
|
|
161
|
-
max_context_length: 2000,
|
|
162
|
-
section_title: "### Background Context (Long-Term Memory)",
|
|
163
|
-
},
|
|
164
|
-
memory_writeback: {
|
|
165
|
-
enabled: true,
|
|
166
|
-
max_feedback_entries: 5,
|
|
167
|
-
max_discoveries_in_project: 3,
|
|
168
|
-
write_on_outcomes: ["task_complete", "circuit_breaker", "max_rounds"],
|
|
169
|
-
},
|
|
170
|
-
checkpoint: {
|
|
171
|
-
max_carried_constraints: 10,
|
|
172
|
-
outcome_max_chars: 200,
|
|
173
|
-
},
|
|
27
|
+
checkpoint: { max_carried_constraints: 10, outcome_max_chars: 200 },
|
|
174
28
|
state_file: {
|
|
175
29
|
enabled: true,
|
|
176
30
|
directory: ".loopforge/state",
|
|
177
31
|
max_checkpoints: 5,
|
|
178
32
|
max_summary_rounds: 5,
|
|
179
33
|
},
|
|
34
|
+
evidence: { providers: ["git"], timeout_ms: 120_000, commands: [] },
|
|
35
|
+
mcp: {
|
|
36
|
+
session_lease_ms: 30_000,
|
|
37
|
+
session_lease_renew_interval_ms: 10_000,
|
|
38
|
+
},
|
|
180
39
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
40
|
+
/** Write a full default `loop_policy.json` to the target directory.
|
|
41
|
+
*
|
|
42
|
+
* The written file contains every configurable key and its default value
|
|
43
|
+
* so users can discover and tune the system without reading source code.
|
|
44
|
+
* Skip creation when the file already exists unless `force` is true.
|
|
45
|
+
*
|
|
46
|
+
* @returns The resolved file path and whether it was freshly created.
|
|
47
|
+
*/
|
|
48
|
+
export function writeDefaultPolicy(targetDir, force = false) {
|
|
49
|
+
const target = resolve(targetDir, "loop_policy.json");
|
|
50
|
+
if (existsSync(target) && !force) {
|
|
51
|
+
return { path: target, created: false };
|
|
52
|
+
}
|
|
53
|
+
writeFileSync(target, JSON.stringify(DEFAULT_POLICY, null, 2) + "\n", "utf8");
|
|
54
|
+
return { path: target, created: true };
|
|
55
|
+
}
|
|
184
56
|
function deepMerge(defaults, overrides) {
|
|
185
57
|
const result = { ...defaults };
|
|
186
58
|
for (const key of Object.keys(overrides)) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
typeof
|
|
191
|
-
!Array.isArray(
|
|
192
|
-
|
|
193
|
-
result[key] = deepMerge(result[key], overrides[key]);
|
|
59
|
+
const current = result[key];
|
|
60
|
+
const incoming = overrides[key];
|
|
61
|
+
if (key in result && current !== null && incoming !== null &&
|
|
62
|
+
typeof current === "object" && !Array.isArray(current) &&
|
|
63
|
+
typeof incoming === "object" && !Array.isArray(incoming)) {
|
|
64
|
+
result[key] = deepMerge(current, incoming);
|
|
194
65
|
}
|
|
195
|
-
else {
|
|
196
|
-
|
|
66
|
+
else if (key in result) {
|
|
67
|
+
// Unknown legacy keys are deliberately ignored at the 2.0 boundary.
|
|
68
|
+
result[key] = incoming;
|
|
197
69
|
}
|
|
198
70
|
}
|
|
199
71
|
return result;
|
|
200
72
|
}
|
|
201
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
202
|
-
// Loader
|
|
203
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
73
|
export function loadPolicy(path) {
|
|
205
74
|
const candidates = [path, "loop_policy.json"].filter(Boolean);
|
|
206
75
|
for (const candidate of candidates) {
|
|
207
76
|
try {
|
|
208
|
-
const
|
|
209
|
-
const raw = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
77
|
+
const raw = JSON.parse(readFileSync(resolve(candidate), "utf8"));
|
|
210
78
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
211
79
|
return deepMerge(DEFAULT_POLICY, raw);
|
|
212
80
|
}
|
|
213
81
|
}
|
|
214
82
|
catch {
|
|
215
|
-
//
|
|
83
|
+
// Try the next candidate.
|
|
216
84
|
}
|
|
217
85
|
}
|
|
218
|
-
return
|
|
86
|
+
return structuredClone(DEFAULT_POLICY);
|
|
219
87
|
}
|
|
220
|
-
|
|
221
|
-
// Module-level singleton — loaded once per session
|
|
222
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
223
|
-
let _policy = null;
|
|
88
|
+
let policy = null;
|
|
224
89
|
export function getPolicy(path) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
return _policy;
|
|
90
|
+
policy ??= loadPolicy(path);
|
|
91
|
+
return policy;
|
|
229
92
|
}
|
|
230
93
|
export function resetPolicy() {
|
|
231
|
-
|
|
94
|
+
policy = null;
|
|
95
|
+
}
|
|
96
|
+
const LOOP_ID_RE = /^[a-zA-Z0-9][-a-zA-Z0-9_:.]{0,127}$/;
|
|
97
|
+
export function validateLoopId(loopId) {
|
|
98
|
+
if (typeof loopId !== "string" || !loopId) {
|
|
99
|
+
throw new Error("Invalid loopId: must be a non-empty string");
|
|
100
|
+
}
|
|
101
|
+
if (loopId.includes(".."))
|
|
102
|
+
throw new Error('Invalid loopId: ".." is not allowed');
|
|
103
|
+
if (loopId.includes("/") || loopId.includes("\\")) {
|
|
104
|
+
throw new Error("Invalid loopId: path separators are not allowed");
|
|
105
|
+
}
|
|
106
|
+
if (!LOOP_ID_RE.test(loopId)) {
|
|
107
|
+
throw new Error("Invalid loopId: use at most 128 alphanumeric, hyphen, underscore, colon, or dot characters");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function resolveStateDirectory(workspaceRoot, configuredDirectory) {
|
|
111
|
+
const lexicalRoot = resolve(workspaceRoot);
|
|
112
|
+
const lexicalTarget = resolve(lexicalRoot, configuredDirectory);
|
|
113
|
+
const lexicalRelative = relative(lexicalRoot, lexicalTarget);
|
|
114
|
+
if (lexicalRelative === ".." || lexicalRelative.startsWith(`..${sep}`) ||
|
|
115
|
+
isAbsolute(lexicalRelative)) {
|
|
116
|
+
throw new Error("State file directory must stay within the workspace");
|
|
117
|
+
}
|
|
118
|
+
const realRoot = realpathSync(lexicalRoot);
|
|
119
|
+
let ancestor = lexicalTarget;
|
|
120
|
+
while (!existsSync(ancestor)) {
|
|
121
|
+
const parent = resolve(ancestor, "..");
|
|
122
|
+
if (parent === ancestor)
|
|
123
|
+
break;
|
|
124
|
+
ancestor = parent;
|
|
125
|
+
}
|
|
126
|
+
const projected = resolve(realpathSync(ancestor), relative(ancestor, lexicalTarget));
|
|
127
|
+
const realRelative = relative(realRoot, projected);
|
|
128
|
+
if (realRelative === ".." || realRelative.startsWith(`..${sep}`) ||
|
|
129
|
+
isAbsolute(realRelative)) {
|
|
130
|
+
throw new Error("State file directory resolves outside the workspace");
|
|
131
|
+
}
|
|
132
|
+
return lexicalTarget;
|
|
133
|
+
}
|
|
134
|
+
export function writeStateFile(loopId, content) {
|
|
135
|
+
if (!content)
|
|
136
|
+
return;
|
|
137
|
+
validateLoopId(loopId);
|
|
138
|
+
const config = getPolicy().state_file;
|
|
139
|
+
if (!config.enabled)
|
|
140
|
+
return;
|
|
141
|
+
const directory = resolveStateDirectory(process.cwd(), config.directory);
|
|
142
|
+
mkdirSync(directory, { recursive: true });
|
|
143
|
+
const verifiedDirectory = resolveStateDirectory(process.cwd(), config.directory);
|
|
144
|
+
const target = resolve(verifiedDirectory, `${loopId}-state.md`);
|
|
145
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
146
|
+
throw new Error("State file target must not be a symbolic link");
|
|
147
|
+
}
|
|
148
|
+
const temporary = resolve(verifiedDirectory, `.${loopId}-state.${process.pid}.${randomUUID()}.tmp`);
|
|
149
|
+
try {
|
|
150
|
+
writeFileSync(temporary, content, "utf8");
|
|
151
|
+
renameSync(temporary, target);
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
try {
|
|
155
|
+
rmSync(temporary, { force: true });
|
|
156
|
+
}
|
|
157
|
+
catch { /* best effort */ }
|
|
158
|
+
}
|
|
232
159
|
}
|
|
233
160
|
//# sourceMappingURL=policy.js.map
|
package/dist/policy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,MAAM,EACN,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AA4F/D,MAAM,CAAC,MAAM,cAAc,GAAe;IACxC,OAAO,EAAE,GAAG;IACZ,WAAW,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE;IACjC,OAAO,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE;IAChD,MAAM,EAAE,EAAE,uBAAuB,EAAE,CAAC,EAAE,mBAAmB,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IAC9E,MAAM,EAAE;QACN,cAAc,EAAE,UAAU;QAC1B,qBAAqB,EAAE,CAAC;QACxB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,KAAK;QACnB,mBAAmB,EAAE,OAAO;KAC7B;IACD,OAAO,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE;IACnC,SAAS,EAAE;QACT,oCAAoC,EAAE,CAAC;QACvC,sBAAsB,EAAE,EAAE;QAC1B,sBAAsB,EAAE,EAAE;QAC1B,wBAAwB,EAAE,IAAI;QAC9B,qBAAqB,EAAE,CAAC;QACxB,2BAA2B,EAAE,GAAG;KACjC;IACD,UAAU,EAAE,EAAE,uBAAuB,EAAE,EAAE,EAAE,iBAAiB,EAAE,GAAG,EAAE;IACnE,UAAU,EAAE;QACV,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,kBAAkB;QAC7B,eAAe,EAAE,CAAC;QAClB,kBAAkB,EAAE,CAAC;KACtB;IACD,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE;IACnE,GAAG,EAAE;QACH,gBAAgB,EAAE,MAAM;QACxB,+BAA+B,EAAE,MAAM;KACxC;CACF,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAiB,EACjB,KAAK,GAAG,KAAK;IAEb,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC1C,CAAC;IACD,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9E,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,SAAS,CAAI,QAAW,EAAE,SAAkC;IACnE,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAA6B,CAAC;IAC1D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAChC,IACE,GAAG,IAAI,MAAM,IAAI,OAAO,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI;YACtD,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YACtD,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EACxD,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CACrB,OAAkC,EAClC,QAAmC,CACpC,CAAC;QACJ,CAAC;aAAM,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;YACzB,oEAAoE;YACpE,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,MAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAa,CAAC;IAC1E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YACjE,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC1D,OAAO,SAAS,CAAC,cAAc,EAAE,GAA8B,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,0BAA0B;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC,cAAc,CAAC,CAAC;AACzC,CAAC;AAED,IAAI,MAAM,GAAsB,IAAI,CAAC;AAErC,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,MAAM,KAAK,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,GAAG,qCAAqC,CAAC;AAEzD,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAClF,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,4FAA4F,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,aAAqB,EACrB,mBAA2B;IAE3B,MAAM,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,MAAM,aAAa,GAAG,OAAO,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;IAChE,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IAC7D,IACE,eAAe,KAAK,IAAI,IAAI,eAAe,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC;QAClE,UAAU,CAAC,eAAe,CAAC,EAC3B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,QAAQ,GAAG,aAAa,CAAC;IAC7B,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,QAAQ;YAAE,MAAM;QAC/B,QAAQ,GAAG,MAAM,CAAC;IACpB,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;IACrF,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnD,IACE,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC;QAC5D,UAAU,CAAC,YAAY,CAAC,EACxB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,OAA2B;IACxE,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,cAAc,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC,UAAU,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO;IAC5B,MAAM,SAAS,GAAG,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACzE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,iBAAiB,GAAG,qBAAqB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,GAAG,MAAM,WAAW,CAAC,CAAC;IAChE,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CACvB,iBAAiB,EACjB,IAAI,MAAM,UAAU,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,MAAM,CACtD,CAAC;IACF,IAAI,CAAC;QACH,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC;YAAS,CAAC;QACT,IAAI,CAAC;YAAC,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACzE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Single-pass prompt renderer for canonical LoopForge state.
|
|
2
|
+
*
|
|
3
|
+
* L0/L1/L2 control state density only. Reasoning strategy belongs to the
|
|
4
|
+
* external Agent. Mandatory task, hard-constraint, and verification sections
|
|
5
|
+
* are never truncated; budgets are soft and overflow is recorded.
|
|
6
|
+
*/
|
|
7
|
+
import type { CanonicalLoopState } from "./canonical-state.js";
|
|
8
|
+
import type { PromptArtifact } from "./protocol.js";
|
|
9
|
+
import type { PromptLevel, PromptLevelReason } from "./prompt-policy.js";
|
|
10
|
+
export declare const PROMPT_ARTIFACT_SCHEMA_VERSION: 1;
|
|
11
|
+
export declare const BASE_PROMPT_VERSION = "2.0.0";
|
|
12
|
+
export type InjectionMode = "adaptive" | "full" | "pointer";
|
|
13
|
+
export interface PromptBudgets {
|
|
14
|
+
l0: number;
|
|
15
|
+
l1: number;
|
|
16
|
+
l2: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const DEFAULT_PROMPT_BUDGETS: PromptBudgets;
|
|
19
|
+
export interface PromptAssemblyInput {
|
|
20
|
+
state: CanonicalLoopState;
|
|
21
|
+
level: PromptLevel;
|
|
22
|
+
reasons: PromptLevelReason[];
|
|
23
|
+
mode?: InjectionMode;
|
|
24
|
+
budgets?: Partial<PromptBudgets>;
|
|
25
|
+
attempt?: number;
|
|
26
|
+
selfEvaluationBlock: string;
|
|
27
|
+
fullStateMarkdown?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function assemblePromptArtifact(input: PromptAssemblyInput): PromptArtifact;
|
|
30
|
+
//# sourceMappingURL=prompt-assembler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-assembler.d.ts","sourceRoot":"","sources":["../src/prompt-assembler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,8BAA8B,EAAG,CAAU,CAAC;AACzD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAuND,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc,CAiFjF"}
|