loopforge 1.15.1 → 2.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 +172 -95
- package/dist/backends/interface.d.ts +3 -9
- package/dist/backends/interface.d.ts.map +1 -1
- package/dist/backends/interface.js +1 -5
- 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 +7 -4
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +78 -138
- package/dist/engine.js.map +1 -1
- package/dist/evidence-provider.d.ts +113 -0
- package/dist/evidence-provider.d.ts.map +1 -0
- package/dist/evidence-provider.js +419 -0
- package/dist/evidence-provider.js.map +1 -0
- package/dist/index.d.ts +18 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -9
- 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 +30 -0
- package/dist/interop.d.ts.map +1 -0
- package/dist/interop.js +29 -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 +303 -2206
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +82 -0
- package/dist/loop-store.d.ts.map +1 -0
- package/dist/loop-store.js +332 -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 +75 -19
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +707 -361
- 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 +225 -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 +53 -140
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +119 -184
- 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 +257 -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 +88 -93
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +23 -39
- 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/runtime.d.ts +33 -19
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +355 -336
- package/dist/runtime.js.map +1 -1
- package/dist/storage.d.ts +38 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +201 -0
- package/dist/storage.js.map +1 -0
- package/dist/verification-gate.d.ts +33 -7
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +195 -7
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +22 -48
- 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/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,29 @@
|
|
|
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
|
-
summary: {
|
|
117
|
-
window: 5,
|
|
118
|
-
health_check_interval: 1,
|
|
119
|
-
},
|
|
120
|
-
technique: {
|
|
121
|
-
/** @deprecated v1.15 — no longer consumed. Retained for config compatibility. */
|
|
122
|
-
tier2_escalation_failures: 3,
|
|
123
|
-
},
|
|
124
|
-
engine: {
|
|
125
|
-
feedback_flush_interval: 5,
|
|
126
|
-
max_circuit_breaker: 3,
|
|
127
|
-
},
|
|
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 },
|
|
128
10
|
runtime: {
|
|
129
11
|
max_rounds: 20,
|
|
130
12
|
round_timeout_ms: 600_000,
|
|
131
13
|
heartbeat_interval_ms: 30_000,
|
|
132
14
|
stall_grace_ms: 300_000,
|
|
133
15
|
max_consecutive_errors: 3,
|
|
16
|
+
pause_double_tap_ms: 3000,
|
|
134
17
|
},
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
18
|
+
prompt: {
|
|
19
|
+
injection_mode: "adaptive",
|
|
20
|
+
full_refresh_interval: 5,
|
|
21
|
+
l0_max_chars: 3000,
|
|
22
|
+
l1_max_chars: 7000,
|
|
23
|
+
l2_max_chars: 18000,
|
|
24
|
+
base_prompt_version: "2.0.0",
|
|
138
25
|
},
|
|
26
|
+
backend: { root_dir: ".loopforge" },
|
|
139
27
|
evolution: {
|
|
140
28
|
max_discovered_constraints_per_round: 5,
|
|
141
29
|
max_active_constraints: 15,
|
|
@@ -144,90 +32,137 @@ export const DEFAULT_POLICY = {
|
|
|
144
32
|
progress_stall_rounds: 2,
|
|
145
33
|
progress_mismatch_threshold: 0.3,
|
|
146
34
|
},
|
|
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
|
-
},
|
|
35
|
+
checkpoint: { max_carried_constraints: 10, outcome_max_chars: 200 },
|
|
174
36
|
state_file: {
|
|
175
37
|
enabled: true,
|
|
176
38
|
directory: ".loopforge/state",
|
|
177
39
|
max_checkpoints: 5,
|
|
178
40
|
max_summary_rounds: 5,
|
|
179
41
|
},
|
|
42
|
+
evidence: { providers: ["git"], timeout_ms: 120_000, commands: [] },
|
|
43
|
+
mcp: {
|
|
44
|
+
session_lease_ms: 30_000,
|
|
45
|
+
session_lease_renew_interval_ms: 10_000,
|
|
46
|
+
},
|
|
180
47
|
};
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
48
|
+
/** Write a full default `loop_policy.json` to the target directory.
|
|
49
|
+
*
|
|
50
|
+
* The written file contains every configurable key and its default value
|
|
51
|
+
* so users can discover and tune the system without reading source code.
|
|
52
|
+
* Skip creation when the file already exists unless `force` is true.
|
|
53
|
+
*
|
|
54
|
+
* @returns The resolved file path and whether it was freshly created.
|
|
55
|
+
*/
|
|
56
|
+
export function writeDefaultPolicy(targetDir, force = false) {
|
|
57
|
+
const target = resolve(targetDir, "loop_policy.json");
|
|
58
|
+
if (existsSync(target) && !force) {
|
|
59
|
+
return { path: target, created: false };
|
|
60
|
+
}
|
|
61
|
+
writeFileSync(target, JSON.stringify(DEFAULT_POLICY, null, 2) + "\n", "utf8");
|
|
62
|
+
return { path: target, created: true };
|
|
63
|
+
}
|
|
184
64
|
function deepMerge(defaults, overrides) {
|
|
185
65
|
const result = { ...defaults };
|
|
186
66
|
for (const key of Object.keys(overrides)) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
typeof
|
|
191
|
-
!Array.isArray(
|
|
192
|
-
|
|
193
|
-
result[key] = deepMerge(result[key], overrides[key]);
|
|
67
|
+
const current = result[key];
|
|
68
|
+
const incoming = overrides[key];
|
|
69
|
+
if (key in result && current !== null && incoming !== null &&
|
|
70
|
+
typeof current === "object" && !Array.isArray(current) &&
|
|
71
|
+
typeof incoming === "object" && !Array.isArray(incoming)) {
|
|
72
|
+
result[key] = deepMerge(current, incoming);
|
|
194
73
|
}
|
|
195
|
-
else {
|
|
196
|
-
|
|
74
|
+
else if (key in result) {
|
|
75
|
+
// Unknown legacy keys are deliberately ignored at the 2.0 boundary.
|
|
76
|
+
result[key] = incoming;
|
|
197
77
|
}
|
|
198
78
|
}
|
|
199
79
|
return result;
|
|
200
80
|
}
|
|
201
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
202
|
-
// Loader
|
|
203
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
204
81
|
export function loadPolicy(path) {
|
|
205
82
|
const candidates = [path, "loop_policy.json"].filter(Boolean);
|
|
206
83
|
for (const candidate of candidates) {
|
|
207
84
|
try {
|
|
208
|
-
const
|
|
209
|
-
const raw = JSON.parse(readFileSync(resolved, "utf-8"));
|
|
85
|
+
const raw = JSON.parse(readFileSync(resolve(candidate), "utf8"));
|
|
210
86
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
211
87
|
return deepMerge(DEFAULT_POLICY, raw);
|
|
212
88
|
}
|
|
213
89
|
}
|
|
214
90
|
catch {
|
|
215
|
-
//
|
|
91
|
+
// Try the next candidate.
|
|
216
92
|
}
|
|
217
93
|
}
|
|
218
|
-
return
|
|
94
|
+
return structuredClone(DEFAULT_POLICY);
|
|
219
95
|
}
|
|
220
|
-
|
|
221
|
-
// Module-level singleton — loaded once per session
|
|
222
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
223
|
-
let _policy = null;
|
|
96
|
+
let policy = null;
|
|
224
97
|
export function getPolicy(path) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
return _policy;
|
|
98
|
+
policy ??= loadPolicy(path);
|
|
99
|
+
return policy;
|
|
229
100
|
}
|
|
230
101
|
export function resetPolicy() {
|
|
231
|
-
|
|
102
|
+
policy = null;
|
|
103
|
+
}
|
|
104
|
+
const LOOP_ID_RE = /^[a-zA-Z0-9][-a-zA-Z0-9_:.]{0,127}$/;
|
|
105
|
+
export function validateLoopId(loopId) {
|
|
106
|
+
if (typeof loopId !== "string" || !loopId) {
|
|
107
|
+
throw new Error("Invalid loopId: must be a non-empty string");
|
|
108
|
+
}
|
|
109
|
+
if (loopId.includes(".."))
|
|
110
|
+
throw new Error('Invalid loopId: ".." is not allowed');
|
|
111
|
+
if (loopId.includes("/") || loopId.includes("\\")) {
|
|
112
|
+
throw new Error("Invalid loopId: path separators are not allowed");
|
|
113
|
+
}
|
|
114
|
+
if (!LOOP_ID_RE.test(loopId)) {
|
|
115
|
+
throw new Error("Invalid loopId: use at most 128 alphanumeric, hyphen, underscore, colon, or dot characters");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function resolveStateDirectory(workspaceRoot, configuredDirectory) {
|
|
119
|
+
const lexicalRoot = resolve(workspaceRoot);
|
|
120
|
+
const lexicalTarget = resolve(lexicalRoot, configuredDirectory);
|
|
121
|
+
const lexicalRelative = relative(lexicalRoot, lexicalTarget);
|
|
122
|
+
if (lexicalRelative === ".." || lexicalRelative.startsWith(`..${sep}`) ||
|
|
123
|
+
isAbsolute(lexicalRelative)) {
|
|
124
|
+
throw new Error("State file directory must stay within the workspace");
|
|
125
|
+
}
|
|
126
|
+
const realRoot = realpathSync(lexicalRoot);
|
|
127
|
+
let ancestor = lexicalTarget;
|
|
128
|
+
while (!existsSync(ancestor)) {
|
|
129
|
+
const parent = resolve(ancestor, "..");
|
|
130
|
+
if (parent === ancestor)
|
|
131
|
+
break;
|
|
132
|
+
ancestor = parent;
|
|
133
|
+
}
|
|
134
|
+
const projected = resolve(realpathSync(ancestor), relative(ancestor, lexicalTarget));
|
|
135
|
+
const realRelative = relative(realRoot, projected);
|
|
136
|
+
if (realRelative === ".." || realRelative.startsWith(`..${sep}`) ||
|
|
137
|
+
isAbsolute(realRelative)) {
|
|
138
|
+
throw new Error("State file directory resolves outside the workspace");
|
|
139
|
+
}
|
|
140
|
+
return lexicalTarget;
|
|
141
|
+
}
|
|
142
|
+
export function writeStateFile(loopId, content) {
|
|
143
|
+
if (!content)
|
|
144
|
+
return;
|
|
145
|
+
validateLoopId(loopId);
|
|
146
|
+
const config = getPolicy().state_file;
|
|
147
|
+
if (!config.enabled)
|
|
148
|
+
return;
|
|
149
|
+
const directory = resolveStateDirectory(process.cwd(), config.directory);
|
|
150
|
+
mkdirSync(directory, { recursive: true });
|
|
151
|
+
const verifiedDirectory = resolveStateDirectory(process.cwd(), config.directory);
|
|
152
|
+
const target = resolve(verifiedDirectory, `${loopId}-state.md`);
|
|
153
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
154
|
+
throw new Error("State file target must not be a symbolic link");
|
|
155
|
+
}
|
|
156
|
+
const temporary = resolve(verifiedDirectory, `.${loopId}-state.${process.pid}.${randomUUID()}.tmp`);
|
|
157
|
+
try {
|
|
158
|
+
writeFileSync(temporary, content, "utf8");
|
|
159
|
+
renameSync(temporary, target);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
try {
|
|
163
|
+
rmSync(temporary, { force: true });
|
|
164
|
+
}
|
|
165
|
+
catch { /* best effort */ }
|
|
166
|
+
}
|
|
232
167
|
}
|
|
233
168
|
//# 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":"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"}
|
|
@@ -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,CA2DjF"}
|