@stackmemoryai/stackmemory 1.10.5 → 1.14.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/LICENSE +131 -64
- package/README.md +107 -24
- package/bin/claude-sm +16 -1
- package/bin/claude-smd +16 -1
- package/bin/codex-smd +16 -1
- package/bin/gemini-sm +16 -1
- package/bin/hermes-sm +21 -0
- package/bin/hermes-smd +21 -0
- package/bin/opencode-sm +16 -1
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +225 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/brain.js +206 -0
- package/dist/src/cli/commands/cache.js +126 -0
- package/dist/src/cli/commands/company-os.js +184 -0
- package/dist/src/cli/commands/context.js +5 -0
- package/dist/src/cli/commands/daemon.js +41 -0
- package/dist/src/cli/commands/handoff.js +40 -9
- package/dist/src/cli/commands/onboard.js +70 -3
- package/dist/src/cli/commands/operator.js +127 -0
- package/dist/src/cli/commands/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +232 -5
- package/dist/src/cli/commands/orchestrator.js +315 -26
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/patterns.js +254 -0
- package/dist/src/cli/commands/portal.js +161 -0
- package/dist/src/cli/commands/scaffold.js +92 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +178 -11
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/commands/sync.js +253 -0
- package/dist/src/cli/commands/tasks.js +130 -1
- package/dist/src/cli/commands/vision.js +221 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/hermes-sm.js +224 -0
- package/dist/src/cli/index.js +105 -39
- package/dist/src/cli/opencode-sm.js +38 -21
- package/dist/src/cli/utils/determinism-watcher.js +66 -0
- package/dist/src/cli/utils/real-cli-bin.js +116 -0
- package/dist/src/core/brain/brain-store.js +187 -0
- package/dist/src/core/brain/brain-sync.js +193 -0
- package/dist/src/core/brain/index.js +78 -0
- package/dist/src/core/brain/types.js +10 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
- package/dist/src/core/cache/token-estimator.js +39 -0
- package/dist/src/core/config/feature-flags.js +2 -6
- package/dist/src/core/context/frame-database.js +79 -27
- package/dist/src/core/context/recursive-context-manager.js +1 -1
- package/dist/src/core/context/rehydration.js +2 -1
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/cross-search/index.js +10 -0
- package/dist/src/core/database/sqlite-adapter.js +14 -84
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +54 -2
- package/dist/src/core/models/provider-pricing.js +58 -4
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -0
- package/dist/src/core/patterns/index.js +22 -0
- package/dist/src/core/patterns/pattern-applier.js +39 -0
- package/dist/src/core/patterns/pattern-observer.js +157 -0
- package/dist/src/core/patterns/pattern-store.js +259 -0
- package/dist/src/core/patterns/types.js +19 -0
- package/dist/src/core/provenance/confidence-scorer.js +128 -0
- package/dist/src/core/provenance/index.js +40 -0
- package/dist/src/core/provenance/provenance-store.js +194 -0
- package/dist/src/core/provenance/types.js +82 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
- package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
- package/dist/src/core/session/project-handoff.js +64 -0
- package/dist/src/core/session/session-manager.js +28 -0
- package/dist/src/core/shared-state/canonical-store.js +564 -0
- package/dist/src/core/skill-packs/index.js +18 -0
- package/dist/src/core/skill-packs/parser.js +42 -0
- package/dist/src/core/skill-packs/registry.js +224 -0
- package/dist/src/core/skill-packs/types.js +79 -0
- package/dist/src/core/storage/cloud-sync-manager.js +116 -0
- package/dist/src/core/storage/cloud-sync.js +574 -0
- package/dist/src/core/storage/two-tier-storage.js +5 -1
- package/dist/src/core/tasks/master-tasks-template.js +43 -0
- package/dist/src/core/tasks/md-task-parser.js +138 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/vision/index.js +27 -0
- package/dist/src/core/vision/signals.js +79 -0
- package/dist/src/core/vision/types.js +22 -0
- package/dist/src/core/vision/vision-file.js +146 -0
- package/dist/src/core/vision/vision-loop.js +220 -0
- package/dist/src/core/wiki/wiki-compiler.js +103 -1
- package/dist/src/daemon/daemon-config.js +52 -0
- package/dist/src/daemon/services/desire-path-service.js +566 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/services/research-stream-service.js +320 -0
- package/dist/src/daemon/services/telemetry-service.js +192 -0
- package/dist/src/daemon/unified-daemon.js +58 -1
- package/dist/src/features/browser/cli-browser-agent.js +417 -0
- package/dist/src/features/browser/stagehand-workflows.js +578 -0
- package/dist/src/features/operator/adapter-factory.js +62 -0
- package/dist/src/features/operator/browser-adapter.js +109 -0
- package/dist/src/features/operator/desktop-adapter.js +125 -0
- package/dist/src/features/operator/index.js +39 -0
- package/dist/src/features/operator/llm-decision.js +137 -0
- package/dist/src/features/operator/operator-logger.js +92 -0
- package/dist/src/features/operator/overnight-runner.js +327 -0
- package/dist/src/features/operator/screen-adapter.js +91 -0
- package/dist/src/features/operator/session-manager.js +127 -0
- package/dist/src/features/operator/state-machine.js +227 -0
- package/dist/src/features/operator/task-queue.js +81 -0
- package/dist/src/features/portal/index.js +26 -0
- package/dist/src/features/portal/server.js +240 -0
- package/dist/src/features/portal/types.js +14 -0
- package/dist/src/features/portal/ui.js +195 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/features/tasks/task-aware-context.js +2 -1
- package/dist/src/features/tui/simple-monitor.js +0 -23
- package/dist/src/features/tui/swarm-monitor.js +8 -66
- package/dist/src/features/web/client/hooks/use-socket.js +12 -0
- package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
- package/dist/src/features/web/client/next-env.d.js +4 -0
- package/dist/src/features/web/client/stores/session-store.js +12 -0
- package/dist/src/features/web/server/gcp-billing.js +76 -0
- package/dist/src/features/web/server/index.js +10 -0
- package/dist/src/features/web/server/spend-calculator.js +228 -0
- package/dist/src/hooks/schemas.js +6 -1
- package/dist/src/integrations/anthropic/client.js +3 -2
- package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
- package/dist/src/integrations/claude-code/subagent-client.js +307 -11
- package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/linear/webhook-retry.js +196 -0
- package/dist/src/integrations/linear/webhook-server.js +18 -22
- package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -84
- package/dist/src/integrations/mcp/server.js +542 -641
- package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
- package/dist/src/integrations/mcp/tool-definitions.js +152 -682
- package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
- package/dist/src/orchestrators/multimodal/determinism.js +244 -0
- package/dist/src/orchestrators/multimodal/harness.js +149 -78
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
- package/dist/src/utils/hook-installer.js +0 -8
- package/dist/src/utils/process-cleanup.js +1 -7
- package/docs/README.md +42 -0
- package/docs/guides/README_INSTALL.md +208 -0
- package/package.json +27 -9
- package/packs/coding/python-fastapi/instructions.md +60 -0
- package/packs/coding/python-fastapi/pack.yaml +28 -0
- package/packs/coding/typescript-react/instructions.md +47 -0
- package/packs/coding/typescript-react/pack.yaml +28 -0
- package/packs/core/commands/capture.md +32 -0
- package/packs/core/commands/learn.md +73 -0
- package/packs/core/commands/next.md +36 -0
- package/packs/core/commands/restart.md +58 -0
- package/packs/core/commands/restore.md +29 -0
- package/packs/core/commands/start.md +57 -0
- package/packs/core/commands/stop.md +65 -0
- package/packs/core/commands/summary.md +40 -0
- package/packs/core/manifest.json +24 -0
- package/packs/ops/decision-recovery/instructions.md +65 -0
- package/packs/ops/decision-recovery/pack.yaml +89 -0
- package/scripts/claude-code-wrapper.sh +11 -0
- package/scripts/claude-sm-setup.sh +12 -1
- package/scripts/codex-wrapper.sh +11 -0
- package/scripts/git-hooks/branch-context-manager.sh +11 -0
- package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
- package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
- package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
- package/scripts/hooks/cleanup-shell.sh +12 -1
- package/scripts/hooks/task-complete.sh +12 -1
- package/scripts/install-code-execution-hooks.sh +12 -1
- package/scripts/install-sweep-hook.sh +12 -0
- package/scripts/install.sh +11 -0
- package/scripts/opencode-wrapper.sh +11 -0
- package/scripts/portal/cloud-init.yaml +69 -0
- package/scripts/portal/setup.sh +69 -0
- package/scripts/portal/stackmemory-portal.service +34 -0
- package/scripts/setup-claude-integration.sh +12 -1
- package/scripts/smoke-init-db.sh +23 -0
- package/scripts/stackmemory-daemon.sh +11 -0
- package/scripts/verify-dist.cjs +11 -4
- package/dist/src/cli/commands/ralph.js +0 -1053
- package/dist/src/cli/commands/team.js +0 -168
- package/dist/src/core/context/shared-context-layer.js +0 -620
- package/dist/src/core/context/stack-merge-resolver.js +0 -748
- package/dist/src/core/merge/conflict-detector.js +0 -430
- package/dist/src/core/merge/resolution-engine.js +0 -557
- package/dist/src/core/merge/stack-diff.js +0 -531
- package/dist/src/core/merge/unified-merge-resolver.js +0 -302
- package/dist/src/hooks/diffmem-hooks.js +0 -376
- package/dist/src/integrations/diffmem/client.js +0 -208
- package/dist/src/integrations/diffmem/config.js +0 -14
- package/dist/src/integrations/greptile/client.js +0 -101
- package/dist/src/integrations/greptile/config.js +0 -14
- package/dist/src/integrations/greptile/index.js +0 -11
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
- package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
- package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
- package/dist/src/integrations/ralph/index.js +0 -17
- package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
- package/dist/src/integrations/ralph/loopmax.js +0 -488
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
- package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
- package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
- package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
- package/scripts/ralph-loop-implementation.js +0 -404
- /package/dist/src/core/{merge → cache}/types.js +0 -0
- /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
- /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
- /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
6
|
+
const HEADER_RE = /^\|\s*id\s*\|/i;
|
|
7
|
+
const SEPARATOR_RE = /^\|[\s-|]+\|$/;
|
|
8
|
+
const PRIORITIES = ["P0", "P1", "P2", "P3"];
|
|
9
|
+
const STATUSES = ["todo", "active", "done", "blocked", "cut"];
|
|
10
|
+
const SYNCS = ["local", "linear", "gh"];
|
|
11
|
+
function parseMasterTasks(content) {
|
|
12
|
+
const lines = content.split("\n");
|
|
13
|
+
const tasks = [];
|
|
14
|
+
let inTable = false;
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const trimmed = line.trim();
|
|
17
|
+
if (!trimmed.startsWith("|")) {
|
|
18
|
+
if (inTable) break;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (HEADER_RE.test(trimmed)) {
|
|
22
|
+
inTable = true;
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (SEPARATOR_RE.test(trimmed)) continue;
|
|
26
|
+
if (!inTable) continue;
|
|
27
|
+
const cells = trimmed.split("|").slice(1, -1).map((c) => c.trim());
|
|
28
|
+
if (cells.length < 8) continue;
|
|
29
|
+
const [id, priority, status, owner, sync, task, branchPr, notes] = cells;
|
|
30
|
+
if (!id || !PRIORITIES.includes(priority)) continue;
|
|
31
|
+
tasks.push({
|
|
32
|
+
id,
|
|
33
|
+
priority,
|
|
34
|
+
status: STATUSES.includes(status) ? status : "todo",
|
|
35
|
+
owner: owner || "@me",
|
|
36
|
+
sync: SYNCS.includes(sync) ? sync : "local",
|
|
37
|
+
task: task || "",
|
|
38
|
+
branchPr: branchPr || "",
|
|
39
|
+
notes: notes || ""
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return tasks;
|
|
43
|
+
}
|
|
44
|
+
function serializeTaskRows(tasks) {
|
|
45
|
+
return tasks.map(
|
|
46
|
+
(t) => `| ${t.id} | ${t.priority} | ${t.status} | ${t.owner} | ${t.sync} | ${t.task} | ${t.branchPr} | ${t.notes} |`
|
|
47
|
+
).join("\n");
|
|
48
|
+
}
|
|
49
|
+
function updateTaskInFile(filePath, taskId, updates) {
|
|
50
|
+
const content = readFileSync(filePath, "utf-8");
|
|
51
|
+
const lines = content.split("\n");
|
|
52
|
+
let found = false;
|
|
53
|
+
const updated = lines.map((line) => {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed.startsWith("|") || HEADER_RE.test(trimmed) || SEPARATOR_RE.test(trimmed)) {
|
|
56
|
+
return line;
|
|
57
|
+
}
|
|
58
|
+
const cells = trimmed.split("|").slice(1, -1).map((c) => c.trim());
|
|
59
|
+
if (cells.length < 8 || cells[0] !== taskId) return line;
|
|
60
|
+
found = true;
|
|
61
|
+
const task = {
|
|
62
|
+
id: cells[0],
|
|
63
|
+
priority: updates.priority ?? cells[1],
|
|
64
|
+
status: updates.status ?? cells[2],
|
|
65
|
+
owner: updates.owner ?? cells[3],
|
|
66
|
+
sync: updates.sync ?? cells[4],
|
|
67
|
+
task: updates.task ?? cells[5],
|
|
68
|
+
branchPr: updates.branchPr ?? cells[6],
|
|
69
|
+
notes: updates.notes ?? cells[7]
|
|
70
|
+
};
|
|
71
|
+
return `| ${task.id} | ${task.priority} | ${task.status} | ${task.owner} | ${task.sync} | ${task.task} | ${task.branchPr} | ${task.notes} |`;
|
|
72
|
+
});
|
|
73
|
+
if (!found) throw new Error(`Task ${taskId} not found in ${filePath}`);
|
|
74
|
+
writeFileSync(filePath, updated.join("\n"), "utf-8");
|
|
75
|
+
}
|
|
76
|
+
function addTaskToFile(filePath, task) {
|
|
77
|
+
const content = readFileSync(filePath, "utf-8");
|
|
78
|
+
const existing = parseMasterTasks(content);
|
|
79
|
+
const maxNum = existing.reduce((max, t) => {
|
|
80
|
+
const n = parseInt(t.id.replace(/^T/, ""), 10);
|
|
81
|
+
return isNaN(n) ? max : Math.max(max, n);
|
|
82
|
+
}, 0);
|
|
83
|
+
const id = `T${String(maxNum + 1).padStart(2, "0")}`;
|
|
84
|
+
const newRow = `| ${id} | ${task.priority} | ${task.status} | ${task.owner} | ${task.sync} | ${task.task} | ${task.branchPr} | ${task.notes} |`;
|
|
85
|
+
const lines = content.split("\n");
|
|
86
|
+
let insertIdx = -1;
|
|
87
|
+
let inTable = false;
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
const trimmed = lines[i].trim();
|
|
90
|
+
if (HEADER_RE.test(trimmed)) {
|
|
91
|
+
inTable = true;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (inTable && trimmed.startsWith("|") && !SEPARATOR_RE.test(trimmed)) {
|
|
95
|
+
insertIdx = i;
|
|
96
|
+
}
|
|
97
|
+
if (inTable && !trimmed.startsWith("|") && trimmed !== "") {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (insertIdx === -1) {
|
|
102
|
+
for (let i = 0; i < lines.length; i++) {
|
|
103
|
+
if (SEPARATOR_RE.test(lines[i].trim())) {
|
|
104
|
+
insertIdx = i;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
lines.splice(insertIdx + 1, 0, newRow);
|
|
110
|
+
writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
111
|
+
return id;
|
|
112
|
+
}
|
|
113
|
+
function getNextTask(tasks) {
|
|
114
|
+
const actionable = tasks.filter(
|
|
115
|
+
(t) => t.status === "todo" || t.status === "active"
|
|
116
|
+
);
|
|
117
|
+
if (actionable.length === 0) return void 0;
|
|
118
|
+
const ownerRank = {
|
|
119
|
+
"@agent": 0,
|
|
120
|
+
"@me": 1,
|
|
121
|
+
"@defer": 2
|
|
122
|
+
};
|
|
123
|
+
actionable.sort((a, b) => {
|
|
124
|
+
const pDiff = PRIORITIES.indexOf(a.priority) - PRIORITIES.indexOf(b.priority);
|
|
125
|
+
if (pDiff !== 0) return pDiff;
|
|
126
|
+
const aRank = ownerRank[a.owner] ?? 1;
|
|
127
|
+
const bRank = ownerRank[b.owner] ?? 1;
|
|
128
|
+
return aRank - bRank;
|
|
129
|
+
});
|
|
130
|
+
return actionable[0];
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
addTaskToFile,
|
|
134
|
+
getNextTask,
|
|
135
|
+
parseMasterTasks,
|
|
136
|
+
serializeTaskRows,
|
|
137
|
+
updateTaskInFile
|
|
138
|
+
};
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { v4 as uuidv4 } from "uuid";
|
|
6
|
+
import { logger } from "../monitoring/logger.js";
|
|
7
|
+
class TraceEventStore {
|
|
8
|
+
db;
|
|
9
|
+
constructor(db) {
|
|
10
|
+
this.db = db;
|
|
11
|
+
this.initSchema();
|
|
12
|
+
}
|
|
13
|
+
initSchema() {
|
|
14
|
+
this.db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS trace_events (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
timestamp TEXT NOT NULL,
|
|
18
|
+
session_id TEXT NOT NULL,
|
|
19
|
+
trace_id TEXT NOT NULL,
|
|
20
|
+
parent_trace_id TEXT,
|
|
21
|
+
tenant_id TEXT NOT NULL DEFAULT 'local',
|
|
22
|
+
actor_host TEXT NOT NULL DEFAULT 'unknown',
|
|
23
|
+
actor_agent TEXT NOT NULL DEFAULT 'stackmemory-mcp',
|
|
24
|
+
actor_user TEXT NOT NULL DEFAULT 'anonymous',
|
|
25
|
+
operation TEXT NOT NULL,
|
|
26
|
+
inputs TEXT NOT NULL DEFAULT '{}',
|
|
27
|
+
outputs TEXT NOT NULL DEFAULT '{}',
|
|
28
|
+
tokens_in INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
tokens_out INTEGER NOT NULL DEFAULT 0,
|
|
30
|
+
cost_usd REAL NOT NULL DEFAULT 0,
|
|
31
|
+
duration_ms INTEGER NOT NULL DEFAULT 0,
|
|
32
|
+
score REAL,
|
|
33
|
+
feedback TEXT,
|
|
34
|
+
provenance TEXT NOT NULL DEFAULT '{"sources":[],"derivation":[],"confidence":1}',
|
|
35
|
+
error TEXT,
|
|
36
|
+
tags TEXT
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_te_session ON trace_events(session_id);
|
|
40
|
+
CREATE INDEX IF NOT EXISTS idx_te_trace ON trace_events(trace_id);
|
|
41
|
+
CREATE INDEX IF NOT EXISTS idx_te_operation ON trace_events(operation);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_te_timestamp ON trace_events(timestamp);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_te_score ON trace_events(score) WHERE score IS NOT NULL;
|
|
44
|
+
`);
|
|
45
|
+
}
|
|
46
|
+
// ------------------------------------------------------------------
|
|
47
|
+
// Write
|
|
48
|
+
// ------------------------------------------------------------------
|
|
49
|
+
/**
|
|
50
|
+
* Record a trace event. Generates ID if not present in trace_id.
|
|
51
|
+
*/
|
|
52
|
+
record(event) {
|
|
53
|
+
const id = uuidv4();
|
|
54
|
+
this.db.prepare(
|
|
55
|
+
`INSERT INTO trace_events (
|
|
56
|
+
id, timestamp, session_id, trace_id, parent_trace_id, tenant_id,
|
|
57
|
+
actor_host, actor_agent, actor_user,
|
|
58
|
+
operation, inputs, outputs,
|
|
59
|
+
tokens_in, tokens_out, cost_usd, duration_ms,
|
|
60
|
+
score, feedback, provenance, error, tags
|
|
61
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
62
|
+
).run(
|
|
63
|
+
id,
|
|
64
|
+
event.timestamp,
|
|
65
|
+
event.session_id,
|
|
66
|
+
event.trace_id,
|
|
67
|
+
event.parent_trace_id ?? null,
|
|
68
|
+
event.tenant_id,
|
|
69
|
+
event.actor.host,
|
|
70
|
+
event.actor.agent,
|
|
71
|
+
event.actor.user,
|
|
72
|
+
event.operation,
|
|
73
|
+
JSON.stringify(event.inputs),
|
|
74
|
+
JSON.stringify(event.outputs),
|
|
75
|
+
event.tokens_in,
|
|
76
|
+
event.tokens_out,
|
|
77
|
+
event.cost_usd,
|
|
78
|
+
event.duration_ms,
|
|
79
|
+
event.score ?? null,
|
|
80
|
+
event.feedback ?? null,
|
|
81
|
+
JSON.stringify(event.provenance),
|
|
82
|
+
event.error ?? null,
|
|
83
|
+
event.tags ? JSON.stringify(event.tags) : null
|
|
84
|
+
);
|
|
85
|
+
logger.debug(`TraceEvent recorded: ${event.operation} [${id}]`);
|
|
86
|
+
return id;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Record multiple events in a single transaction.
|
|
90
|
+
*/
|
|
91
|
+
recordBatch(events) {
|
|
92
|
+
const ids = [];
|
|
93
|
+
this.db.transaction(() => {
|
|
94
|
+
for (const event of events) {
|
|
95
|
+
ids.push(this.record(event));
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
return ids;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Add score and/or feedback to an existing event.
|
|
102
|
+
*/
|
|
103
|
+
annotate(id, annotation) {
|
|
104
|
+
const sets = [];
|
|
105
|
+
const params = [];
|
|
106
|
+
if (annotation.score !== void 0) {
|
|
107
|
+
sets.push("score = ?");
|
|
108
|
+
params.push(annotation.score);
|
|
109
|
+
}
|
|
110
|
+
if (annotation.feedback !== void 0) {
|
|
111
|
+
sets.push("feedback = ?");
|
|
112
|
+
params.push(annotation.feedback);
|
|
113
|
+
}
|
|
114
|
+
if (sets.length === 0) return false;
|
|
115
|
+
params.push(id);
|
|
116
|
+
const result = this.db.prepare(`UPDATE trace_events SET ${sets.join(", ")} WHERE id = ?`).run(...params);
|
|
117
|
+
return result.changes > 0;
|
|
118
|
+
}
|
|
119
|
+
// ------------------------------------------------------------------
|
|
120
|
+
// Read
|
|
121
|
+
// ------------------------------------------------------------------
|
|
122
|
+
/**
|
|
123
|
+
* Get a single event by ID.
|
|
124
|
+
*/
|
|
125
|
+
get(id) {
|
|
126
|
+
const row = this.db.prepare("SELECT * FROM trace_events WHERE id = ?").get(id);
|
|
127
|
+
return row ? this.rowToEvent(row) : void 0;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Query events with filters.
|
|
131
|
+
*/
|
|
132
|
+
query(filter = {}) {
|
|
133
|
+
const conditions = [];
|
|
134
|
+
const params = [];
|
|
135
|
+
if (filter.session_id) {
|
|
136
|
+
conditions.push("session_id = ?");
|
|
137
|
+
params.push(filter.session_id);
|
|
138
|
+
}
|
|
139
|
+
if (filter.operation) {
|
|
140
|
+
conditions.push("operation = ?");
|
|
141
|
+
params.push(filter.operation);
|
|
142
|
+
}
|
|
143
|
+
if (filter.min_score !== void 0) {
|
|
144
|
+
conditions.push("score >= ?");
|
|
145
|
+
params.push(filter.min_score);
|
|
146
|
+
}
|
|
147
|
+
if (filter.has_feedback) {
|
|
148
|
+
conditions.push("feedback IS NOT NULL");
|
|
149
|
+
}
|
|
150
|
+
if (filter.since) {
|
|
151
|
+
conditions.push("timestamp >= ?");
|
|
152
|
+
params.push(filter.since);
|
|
153
|
+
}
|
|
154
|
+
if (filter.until) {
|
|
155
|
+
conditions.push("timestamp <= ?");
|
|
156
|
+
params.push(filter.until);
|
|
157
|
+
}
|
|
158
|
+
const where = conditions.length ? "WHERE " + conditions.join(" AND ") : "";
|
|
159
|
+
const limit = filter.limit ?? 100;
|
|
160
|
+
const offset = filter.offset ?? 0;
|
|
161
|
+
const rows = this.db.prepare(
|
|
162
|
+
`SELECT * FROM trace_events ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`
|
|
163
|
+
).all(...params, limit, offset);
|
|
164
|
+
return rows.map((r) => this.rowToEvent(r));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get events for a specific session.
|
|
168
|
+
*/
|
|
169
|
+
getBySession(sessionId) {
|
|
170
|
+
return this.query({ session_id: sessionId, limit: 1e3 });
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Get events with scores (for GEPA consumption).
|
|
174
|
+
*/
|
|
175
|
+
getScoredEvents(minScore) {
|
|
176
|
+
return this.query({
|
|
177
|
+
min_score: minScore ?? 0,
|
|
178
|
+
limit: 500
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get events with feedback (for GEPA ASI consumption).
|
|
183
|
+
*/
|
|
184
|
+
getFeedbackEvents() {
|
|
185
|
+
return this.query({ has_feedback: true, limit: 500 });
|
|
186
|
+
}
|
|
187
|
+
// ------------------------------------------------------------------
|
|
188
|
+
// Stats
|
|
189
|
+
// ------------------------------------------------------------------
|
|
190
|
+
/**
|
|
191
|
+
* Aggregate statistics across all events.
|
|
192
|
+
*/
|
|
193
|
+
getStats(filter) {
|
|
194
|
+
const conditions = [];
|
|
195
|
+
const params = [];
|
|
196
|
+
if (filter?.session_id) {
|
|
197
|
+
conditions.push("session_id = ?");
|
|
198
|
+
params.push(filter.session_id);
|
|
199
|
+
}
|
|
200
|
+
if (filter?.since) {
|
|
201
|
+
conditions.push("timestamp >= ?");
|
|
202
|
+
params.push(filter.since);
|
|
203
|
+
}
|
|
204
|
+
const where = conditions.length ? "WHERE " + conditions.join(" AND ") : "";
|
|
205
|
+
const agg = this.db.prepare(
|
|
206
|
+
`SELECT
|
|
207
|
+
COUNT(*) as total_events,
|
|
208
|
+
COALESCE(SUM(tokens_in), 0) as total_tokens_in,
|
|
209
|
+
COALESCE(SUM(tokens_out), 0) as total_tokens_out,
|
|
210
|
+
COALESCE(SUM(cost_usd), 0) as total_cost_usd,
|
|
211
|
+
AVG(score) as avg_score,
|
|
212
|
+
SUM(CASE WHEN feedback IS NOT NULL THEN 1 ELSE 0 END) as events_with_feedback,
|
|
213
|
+
SUM(CASE WHEN error IS NOT NULL THEN 1 ELSE 0 END) as events_with_errors
|
|
214
|
+
FROM trace_events ${where}`
|
|
215
|
+
).get(...params);
|
|
216
|
+
const opRows = this.db.prepare(
|
|
217
|
+
`SELECT operation, COUNT(*) as cnt FROM trace_events ${where} GROUP BY operation ORDER BY cnt DESC`
|
|
218
|
+
).all(...params);
|
|
219
|
+
const hostRows = this.db.prepare(
|
|
220
|
+
`SELECT actor_host, COUNT(*) as cnt FROM trace_events ${where} GROUP BY actor_host ORDER BY cnt DESC`
|
|
221
|
+
).all(...params);
|
|
222
|
+
const operations = {};
|
|
223
|
+
for (const r of opRows) operations[r.operation] = r.cnt;
|
|
224
|
+
const hosts = {};
|
|
225
|
+
for (const r of hostRows) hosts[r.actor_host] = r.cnt;
|
|
226
|
+
return {
|
|
227
|
+
total_events: agg["total_events"] || 0,
|
|
228
|
+
total_tokens_in: agg["total_tokens_in"] || 0,
|
|
229
|
+
total_tokens_out: agg["total_tokens_out"] || 0,
|
|
230
|
+
total_cost_usd: agg["total_cost_usd"] || 0,
|
|
231
|
+
avg_score: agg["avg_score"],
|
|
232
|
+
events_with_feedback: agg["events_with_feedback"] || 0,
|
|
233
|
+
events_with_errors: agg["events_with_errors"] || 0,
|
|
234
|
+
operations,
|
|
235
|
+
hosts
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// ------------------------------------------------------------------
|
|
239
|
+
// Lifecycle
|
|
240
|
+
// ------------------------------------------------------------------
|
|
241
|
+
/**
|
|
242
|
+
* Delete events older than the given ISO timestamp.
|
|
243
|
+
*/
|
|
244
|
+
evict(olderThan) {
|
|
245
|
+
const result = this.db.prepare("DELETE FROM trace_events WHERE timestamp < ?").run(olderThan);
|
|
246
|
+
return result.changes;
|
|
247
|
+
}
|
|
248
|
+
// ------------------------------------------------------------------
|
|
249
|
+
// Helpers
|
|
250
|
+
// ------------------------------------------------------------------
|
|
251
|
+
rowToEvent(row) {
|
|
252
|
+
const provenance = JSON.parse(row.provenance);
|
|
253
|
+
const actor = {
|
|
254
|
+
host: row.actor_host,
|
|
255
|
+
agent: row.actor_agent,
|
|
256
|
+
user: row.actor_user
|
|
257
|
+
};
|
|
258
|
+
return {
|
|
259
|
+
timestamp: row.timestamp,
|
|
260
|
+
session_id: row.session_id,
|
|
261
|
+
trace_id: row.trace_id,
|
|
262
|
+
parent_trace_id: row.parent_trace_id ?? void 0,
|
|
263
|
+
tenant_id: row.tenant_id,
|
|
264
|
+
actor,
|
|
265
|
+
operation: row.operation,
|
|
266
|
+
inputs: JSON.parse(row.inputs),
|
|
267
|
+
outputs: JSON.parse(row.outputs),
|
|
268
|
+
tokens_in: row.tokens_in,
|
|
269
|
+
tokens_out: row.tokens_out,
|
|
270
|
+
cost_usd: row.cost_usd,
|
|
271
|
+
duration_ms: row.duration_ms,
|
|
272
|
+
score: row.score ?? void 0,
|
|
273
|
+
feedback: row.feedback ?? void 0,
|
|
274
|
+
provenance,
|
|
275
|
+
error: row.error ?? void 0,
|
|
276
|
+
tags: row.tags ? JSON.parse(row.tags) : void 0
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
export {
|
|
281
|
+
TraceEventStore
|
|
282
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
import {
|
|
7
|
+
parseVision,
|
|
8
|
+
loadVision,
|
|
9
|
+
setObjectiveDone,
|
|
10
|
+
scaffoldVision,
|
|
11
|
+
objectiveId,
|
|
12
|
+
VISION_TEMPLATE
|
|
13
|
+
} from "./vision-file.js";
|
|
14
|
+
import { SignalInbox } from "./signals.js";
|
|
15
|
+
import {
|
|
16
|
+
VisionLoop
|
|
17
|
+
} from "./vision-loop.js";
|
|
18
|
+
export {
|
|
19
|
+
SignalInbox,
|
|
20
|
+
VISION_TEMPLATE,
|
|
21
|
+
VisionLoop,
|
|
22
|
+
loadVision,
|
|
23
|
+
objectiveId,
|
|
24
|
+
parseVision,
|
|
25
|
+
scaffoldVision,
|
|
26
|
+
setObjectiveDone
|
|
27
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import {
|
|
6
|
+
existsSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
appendFileSync
|
|
11
|
+
} from "fs";
|
|
12
|
+
import { dirname } from "path";
|
|
13
|
+
import { randomUUID } from "crypto";
|
|
14
|
+
class SignalInbox {
|
|
15
|
+
constructor(path) {
|
|
16
|
+
this.path = path;
|
|
17
|
+
}
|
|
18
|
+
ensureDir() {
|
|
19
|
+
const dir = dirname(this.path);
|
|
20
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
add(input) {
|
|
23
|
+
this.ensureDir();
|
|
24
|
+
const signal = {
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
source: input.source ?? "manual",
|
|
27
|
+
severity: input.severity ?? "medium",
|
|
28
|
+
text: input.text,
|
|
29
|
+
createdAt: Date.now(),
|
|
30
|
+
...input.refs ? { refs: input.refs } : {}
|
|
31
|
+
};
|
|
32
|
+
appendFileSync(this.path, JSON.stringify(signal) + "\n");
|
|
33
|
+
return signal;
|
|
34
|
+
}
|
|
35
|
+
all() {
|
|
36
|
+
if (!existsSync(this.path)) return [];
|
|
37
|
+
return readFileSync(this.path, "utf-8").split(/\r?\n/).filter(Boolean).map((l) => {
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(l);
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}).filter((s) => !!s);
|
|
44
|
+
}
|
|
45
|
+
/** Unresolved signals, most severe + oldest first. */
|
|
46
|
+
pending() {
|
|
47
|
+
const rank = {
|
|
48
|
+
low: 1,
|
|
49
|
+
medium: 2,
|
|
50
|
+
high: 3,
|
|
51
|
+
critical: 4
|
|
52
|
+
};
|
|
53
|
+
return this.all().filter((s) => !s.resolvedAt).sort(
|
|
54
|
+
(a, b) => rank[b.severity] - rank[a.severity] || a.createdAt - b.createdAt
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
/** Rewrite the file marking a signal resolved (compacts the log). */
|
|
58
|
+
resolve(id) {
|
|
59
|
+
const signals = this.all();
|
|
60
|
+
let changed = false;
|
|
61
|
+
for (const s of signals) {
|
|
62
|
+
if (s.id === id && !s.resolvedAt) {
|
|
63
|
+
s.resolvedAt = Date.now();
|
|
64
|
+
changed = true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (changed) {
|
|
68
|
+
this.ensureDir();
|
|
69
|
+
writeFileSync(
|
|
70
|
+
this.path,
|
|
71
|
+
signals.map((s) => JSON.stringify(s)).join("\n") + "\n"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return changed;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
SignalInbox
|
|
79
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
const DEFAULT_LIMITS = {
|
|
6
|
+
maxIterations: 10,
|
|
7
|
+
maxIterationsPerDay: 50,
|
|
8
|
+
maxConsecutiveFailures: 3,
|
|
9
|
+
tickIntervalSec: 60,
|
|
10
|
+
requireApproval: false,
|
|
11
|
+
stopWhenComplete: true
|
|
12
|
+
};
|
|
13
|
+
const SEVERITY_RANK = {
|
|
14
|
+
low: 1,
|
|
15
|
+
medium: 2,
|
|
16
|
+
high: 3,
|
|
17
|
+
critical: 4
|
|
18
|
+
};
|
|
19
|
+
export {
|
|
20
|
+
DEFAULT_LIMITS,
|
|
21
|
+
SEVERITY_RANK
|
|
22
|
+
};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { createHash } from "crypto";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_LIMITS
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
function objectiveId(text) {
|
|
11
|
+
return createHash("sha1").update(text.trim()).digest("hex").slice(0, 10);
|
|
12
|
+
}
|
|
13
|
+
function splitSections(text) {
|
|
14
|
+
const lines = text.split(/\r?\n/);
|
|
15
|
+
const sections = /* @__PURE__ */ new Map();
|
|
16
|
+
const preamble = [];
|
|
17
|
+
let current = null;
|
|
18
|
+
for (const line of lines) {
|
|
19
|
+
const h2 = line.match(/^##\s+(.+?)\s*$/);
|
|
20
|
+
if (h2?.[1]) {
|
|
21
|
+
current = { body: [] };
|
|
22
|
+
sections.set(h2[1].toLowerCase(), current);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (/^#\s+/.test(line)) continue;
|
|
26
|
+
if (current) {
|
|
27
|
+
current.body.push(line);
|
|
28
|
+
} else {
|
|
29
|
+
preamble.push(line);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { preamble, sections };
|
|
33
|
+
}
|
|
34
|
+
function bulletLines(body) {
|
|
35
|
+
return body.map((l) => l.match(/^\s*[-*]\s+(.*\S)\s*$/)?.[1]?.trim()).filter((s) => !!s).filter((s) => !/^\[[ xX]\]/.test(s));
|
|
36
|
+
}
|
|
37
|
+
function parseObjectives(body) {
|
|
38
|
+
const objectives = [];
|
|
39
|
+
for (const line of body) {
|
|
40
|
+
const m = line.match(/^\s*[-*]\s+\[([ xX])\]\s+(.*\S)\s*$/);
|
|
41
|
+
if (!m?.[2]) continue;
|
|
42
|
+
const text = m[2].trim();
|
|
43
|
+
objectives.push({
|
|
44
|
+
id: objectiveId(text),
|
|
45
|
+
text,
|
|
46
|
+
done: (m[1] ?? "").toLowerCase() === "x"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return objectives;
|
|
50
|
+
}
|
|
51
|
+
function parseLimits(body) {
|
|
52
|
+
const limits = { ...DEFAULT_LIMITS };
|
|
53
|
+
for (const line of body) {
|
|
54
|
+
const m = line.match(/^\s*([a-zA-Z]+)\s*:\s*(.+?)\s*$/);
|
|
55
|
+
if (!m?.[1] || m[2] === void 0) continue;
|
|
56
|
+
const key = m[1];
|
|
57
|
+
const raw = m[2];
|
|
58
|
+
if (!(key in limits)) continue;
|
|
59
|
+
if (key === "requireApproval" || key === "stopWhenComplete") {
|
|
60
|
+
limits[key] = /^(true|yes|1)$/i.test(raw);
|
|
61
|
+
} else {
|
|
62
|
+
const n = parseInt(raw, 10);
|
|
63
|
+
if (Number.isFinite(n)) limits[key] = n;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return limits;
|
|
67
|
+
}
|
|
68
|
+
function parseVision(text) {
|
|
69
|
+
const { preamble, sections } = splitSections(text);
|
|
70
|
+
const body = (name) => sections.get(name)?.body ?? [];
|
|
71
|
+
const mission = preamble.map((l) => l.trim()).filter(Boolean).join(" ").trim();
|
|
72
|
+
return {
|
|
73
|
+
mission,
|
|
74
|
+
guardrails: bulletLines(body("guardrails")),
|
|
75
|
+
scope: bulletLines(body("scope")),
|
|
76
|
+
objectives: parseObjectives(body("objectives")),
|
|
77
|
+
limits: sections.has("limits") ? parseLimits(body("limits")) : { ...DEFAULT_LIMITS }
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function loadVision(path) {
|
|
81
|
+
if (!existsSync(path)) return null;
|
|
82
|
+
return parseVision(readFileSync(path, "utf-8"));
|
|
83
|
+
}
|
|
84
|
+
function setObjectiveDone(path, objId, done) {
|
|
85
|
+
if (!existsSync(path)) return false;
|
|
86
|
+
const lines = readFileSync(path, "utf-8").split(/\r?\n/);
|
|
87
|
+
let changed = false;
|
|
88
|
+
for (let i = 0; i < lines.length; i++) {
|
|
89
|
+
const line = lines[i];
|
|
90
|
+
if (line === void 0) continue;
|
|
91
|
+
const m = line.match(/^(\s*[-*]\s+)\[([ xX])\]\s+(.*\S)\s*$/);
|
|
92
|
+
if (!m?.[3]) continue;
|
|
93
|
+
if (objectiveId(m[3].trim()) === objId) {
|
|
94
|
+
lines[i] = `${m[1] ?? ""}[${done ? "x" : " "}] ${m[3].trim()}`;
|
|
95
|
+
changed = true;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (changed) writeFileSync(path, lines.join("\n"));
|
|
100
|
+
return changed;
|
|
101
|
+
}
|
|
102
|
+
const VISION_TEMPLATE = `# Vision
|
|
103
|
+
|
|
104
|
+
State the single north-star mission this autonomous loop serves. Keep it to a
|
|
105
|
+
sentence or two \u2014 concrete enough to judge whether a piece of work belongs.
|
|
106
|
+
|
|
107
|
+
## Guardrails
|
|
108
|
+
|
|
109
|
+
- Stay within the scope below; do not touch anything outside it.
|
|
110
|
+
- Never modify secrets, production credentials, or deploy/publish.
|
|
111
|
+
- Open a PR for review; never merge to the default branch autonomously.
|
|
112
|
+
- If an objective is ambiguous or risky, stop and ask a human.
|
|
113
|
+
|
|
114
|
+
## Scope
|
|
115
|
+
|
|
116
|
+
- src/**
|
|
117
|
+
- docs/**
|
|
118
|
+
|
|
119
|
+
## Objectives
|
|
120
|
+
|
|
121
|
+
- [ ] First concrete objective the loop should pursue
|
|
122
|
+
- [ ] Second objective
|
|
123
|
+
- [ ] Third objective
|
|
124
|
+
|
|
125
|
+
## Limits
|
|
126
|
+
|
|
127
|
+
maxIterations: 10
|
|
128
|
+
maxIterationsPerDay: 50
|
|
129
|
+
maxConsecutiveFailures: 3
|
|
130
|
+
tickIntervalSec: 60
|
|
131
|
+
requireApproval: false
|
|
132
|
+
stopWhenComplete: true
|
|
133
|
+
`;
|
|
134
|
+
function scaffoldVision(path, force = false) {
|
|
135
|
+
if (existsSync(path) && !force) return false;
|
|
136
|
+
writeFileSync(path, VISION_TEMPLATE);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
VISION_TEMPLATE,
|
|
141
|
+
loadVision,
|
|
142
|
+
objectiveId,
|
|
143
|
+
parseVision,
|
|
144
|
+
scaffoldVision,
|
|
145
|
+
setObjectiveDone
|
|
146
|
+
};
|