@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
package/dist/src/cli/codex-sm.js
CHANGED
|
@@ -11,9 +11,26 @@ import { program } from "commander";
|
|
|
11
11
|
import { v4 as uuidv4 } from "uuid";
|
|
12
12
|
import chalk from "chalk";
|
|
13
13
|
import { initializeTracing, trace } from "../core/trace/index.js";
|
|
14
|
+
import {
|
|
15
|
+
resolveRealCliBin,
|
|
16
|
+
resolveNativeCodexBin,
|
|
17
|
+
resolveNvmBin
|
|
18
|
+
} from "./utils/real-cli-bin.js";
|
|
19
|
+
import {
|
|
20
|
+
startDeterminismWatcher,
|
|
21
|
+
stopDeterminismWatcher
|
|
22
|
+
} from "./utils/determinism-watcher.js";
|
|
23
|
+
import {
|
|
24
|
+
canonicalStateStore,
|
|
25
|
+
projectIdFromIdentifier
|
|
26
|
+
} from "../core/shared-state/canonical-store.js";
|
|
14
27
|
class CodexSM {
|
|
15
28
|
config;
|
|
16
29
|
stackmemoryPath;
|
|
30
|
+
sessionId;
|
|
31
|
+
ownsSession;
|
|
32
|
+
sessionEnded;
|
|
33
|
+
determinismWatcher;
|
|
17
34
|
constructor() {
|
|
18
35
|
this.config = {
|
|
19
36
|
instanceId: this.generateInstanceId(),
|
|
@@ -23,6 +40,10 @@ class CodexSM {
|
|
|
23
40
|
verboseTracing: false
|
|
24
41
|
};
|
|
25
42
|
this.stackmemoryPath = this.findStackMemory();
|
|
43
|
+
this.sessionId = process.env["STACKMEMORY_SESSION"] || uuidv4();
|
|
44
|
+
this.ownsSession = !process.env["STACKMEMORY_SESSION"];
|
|
45
|
+
this.sessionEnded = false;
|
|
46
|
+
this.determinismWatcher = null;
|
|
26
47
|
}
|
|
27
48
|
getRepoRoot() {
|
|
28
49
|
try {
|
|
@@ -87,6 +108,20 @@ class CodexSM {
|
|
|
87
108
|
return "main";
|
|
88
109
|
}
|
|
89
110
|
}
|
|
111
|
+
getProjectId() {
|
|
112
|
+
const root = this.getRepoRoot() || process.cwd();
|
|
113
|
+
try {
|
|
114
|
+
const remote = execSync("git config --get remote.origin.url", {
|
|
115
|
+
cwd: root,
|
|
116
|
+
encoding: "utf8"
|
|
117
|
+
}).trim();
|
|
118
|
+
if (remote) {
|
|
119
|
+
return projectIdFromIdentifier(remote);
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
return projectIdFromIdentifier(root);
|
|
124
|
+
}
|
|
90
125
|
hasUncommittedChanges() {
|
|
91
126
|
try {
|
|
92
127
|
const status = execSync("git status --porcelain", { encoding: "utf8" });
|
|
@@ -96,24 +131,21 @@ class CodexSM {
|
|
|
96
131
|
}
|
|
97
132
|
}
|
|
98
133
|
resolveCodexBin() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
} catch {
|
|
115
|
-
}
|
|
116
|
-
return null;
|
|
134
|
+
return resolveRealCliBin({
|
|
135
|
+
explicitBin: this.config.codexBin,
|
|
136
|
+
envBin: process.env["CODEX_BIN"],
|
|
137
|
+
preferredPaths: [
|
|
138
|
+
...resolveNativeCodexBin(),
|
|
139
|
+
// native binary first (no wrapper overhead)
|
|
140
|
+
resolveNvmBin("codex"),
|
|
141
|
+
// dynamic nvm/fnm/volta path
|
|
142
|
+
"/usr/local/bin/codex",
|
|
143
|
+
"/opt/homebrew/bin/codex",
|
|
144
|
+
"/usr/local/bin/codex-cli",
|
|
145
|
+
"/opt/homebrew/bin/codex-cli"
|
|
146
|
+
].filter((p) => !!p),
|
|
147
|
+
pathCommands: ["codex", "codex-cli"]
|
|
148
|
+
});
|
|
117
149
|
}
|
|
118
150
|
setupWorktree() {
|
|
119
151
|
if (!this.config.useWorktree || !this.isGitRepo()) return null;
|
|
@@ -169,22 +201,58 @@ class CodexSM {
|
|
|
169
201
|
} catch {
|
|
170
202
|
}
|
|
171
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Emit context budget advice based on tool-call count from checkpoint state.
|
|
206
|
+
* Mirrors the Claude Code context-budget.js hook.
|
|
207
|
+
*/
|
|
208
|
+
emitContextBudgetAdvice() {
|
|
209
|
+
const COMPACT_SUGGEST = 50;
|
|
210
|
+
const COMPACT_STRONG = 65;
|
|
211
|
+
const RESTART_RECOMMEND = 80;
|
|
212
|
+
try {
|
|
213
|
+
const stateFile = path.join(
|
|
214
|
+
os.homedir(),
|
|
215
|
+
".stackmemory",
|
|
216
|
+
`checkpoint-state-${this.config.instanceId}.json`
|
|
217
|
+
);
|
|
218
|
+
if (!fs.existsSync(stateFile)) return;
|
|
219
|
+
const state = JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
220
|
+
const cwd = process.cwd();
|
|
221
|
+
const toolCount = state.projects?.[cwd]?.toolCount || 0;
|
|
222
|
+
if (toolCount >= RESTART_RECOMMEND) {
|
|
223
|
+
console.log(
|
|
224
|
+
chalk.yellow(
|
|
225
|
+
`[CONTEXT_BUDGET] ${toolCount} tool calls (~150K+ tokens). Recommend: save context then start fresh session.`
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
} else if (toolCount >= COMPACT_STRONG) {
|
|
229
|
+
console.log(
|
|
230
|
+
chalk.yellow(
|
|
231
|
+
`[CONTEXT_BUDGET] ${toolCount} tool calls (~100-130K tokens). Context heavy \u2014 consider compacting or restarting.`
|
|
232
|
+
)
|
|
233
|
+
);
|
|
234
|
+
} else if (toolCount >= COMPACT_SUGGEST) {
|
|
235
|
+
console.log(
|
|
236
|
+
chalk.gray(
|
|
237
|
+
`[CONTEXT_BUDGET] ${toolCount} tool calls (~80-100K tokens). Context getting heavy.`
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
}
|
|
172
244
|
loadContext() {
|
|
173
245
|
if (!this.config.contextEnabled) return;
|
|
174
246
|
try {
|
|
175
247
|
console.log(chalk.blue("\u{1F4DA} Loading previous context..."));
|
|
176
|
-
const cmd = `${this.stackmemoryPath} context
|
|
177
|
-
const output = execSync(cmd, {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
chalk.gray(` - ${ctx.message} (${ctx.metadata?.timestamp})`)
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
);
|
|
248
|
+
const cmd = `${this.stackmemoryPath} context show`;
|
|
249
|
+
const output = execSync(cmd, {
|
|
250
|
+
encoding: "utf8",
|
|
251
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
252
|
+
});
|
|
253
|
+
const lines = output.trim().split("\n").filter((line) => line.trim());
|
|
254
|
+
if (lines.length > 3) {
|
|
255
|
+
console.log(chalk.gray("Context stack loaded"));
|
|
188
256
|
}
|
|
189
257
|
} catch {
|
|
190
258
|
}
|
|
@@ -197,6 +265,117 @@ class CodexSM {
|
|
|
197
265
|
);
|
|
198
266
|
}
|
|
199
267
|
}
|
|
268
|
+
async publishSessionStart() {
|
|
269
|
+
const projectPath = process.cwd();
|
|
270
|
+
const projectId = this.getProjectId();
|
|
271
|
+
const branch = this.isGitRepo() ? this.getCurrentBranch() : void 0;
|
|
272
|
+
await canonicalStateStore.upsertSession({
|
|
273
|
+
sessionId: this.sessionId,
|
|
274
|
+
tool: "codex",
|
|
275
|
+
projectId,
|
|
276
|
+
projectPath,
|
|
277
|
+
branch,
|
|
278
|
+
instanceId: this.config.instanceId,
|
|
279
|
+
metadata: {
|
|
280
|
+
task: this.config.task
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
await canonicalStateStore.upsertInstance({
|
|
284
|
+
instanceId: this.config.instanceId,
|
|
285
|
+
tool: "codex",
|
|
286
|
+
sessionId: this.sessionId,
|
|
287
|
+
projectId,
|
|
288
|
+
projectPath,
|
|
289
|
+
branch,
|
|
290
|
+
worktreePath: this.config.worktreePath,
|
|
291
|
+
pid: process.pid,
|
|
292
|
+
status: "active",
|
|
293
|
+
metadata: {
|
|
294
|
+
task: this.config.task
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
await canonicalStateStore.appendEvent({
|
|
298
|
+
type: "session_start",
|
|
299
|
+
tool: "codex",
|
|
300
|
+
sessionId: this.sessionId,
|
|
301
|
+
instanceId: this.config.instanceId,
|
|
302
|
+
projectId,
|
|
303
|
+
projectPath,
|
|
304
|
+
branch,
|
|
305
|
+
payload: {
|
|
306
|
+
task: this.config.task,
|
|
307
|
+
worktreePath: this.config.worktreePath
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
const claimResult = await canonicalStateStore.claimPaths({
|
|
311
|
+
tool: "codex",
|
|
312
|
+
sessionId: this.sessionId,
|
|
313
|
+
instanceId: this.config.instanceId,
|
|
314
|
+
projectId,
|
|
315
|
+
projectPath,
|
|
316
|
+
branch,
|
|
317
|
+
paths: [],
|
|
318
|
+
metadata: {
|
|
319
|
+
task: this.config.task,
|
|
320
|
+
scope: "branch"
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
if (claimResult.conflicts.length > 0) {
|
|
324
|
+
console.log(chalk.yellow("\u26A0\uFE0F Shared state conflict detected"));
|
|
325
|
+
for (const conflict of claimResult.conflicts.slice(0, 3)) {
|
|
326
|
+
console.log(
|
|
327
|
+
chalk.gray(
|
|
328
|
+
` Claim ${conflict.claimId.slice(0, 8)} already owns ${conflict.branch || "overlapping work"}`
|
|
329
|
+
)
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
async publishSessionEnd(eventType, payload = {}) {
|
|
335
|
+
if (this.sessionEnded) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
this.sessionEnded = true;
|
|
339
|
+
const projectPath = process.cwd();
|
|
340
|
+
const projectId = this.getProjectId();
|
|
341
|
+
const branch = this.isGitRepo() ? this.getCurrentBranch() : void 0;
|
|
342
|
+
await canonicalStateStore.appendEvent({
|
|
343
|
+
type: eventType,
|
|
344
|
+
tool: "codex",
|
|
345
|
+
sessionId: this.sessionId,
|
|
346
|
+
instanceId: this.config.instanceId,
|
|
347
|
+
projectId,
|
|
348
|
+
projectPath,
|
|
349
|
+
branch,
|
|
350
|
+
payload
|
|
351
|
+
});
|
|
352
|
+
await canonicalStateStore.releaseClaims({
|
|
353
|
+
instanceId: this.config.instanceId,
|
|
354
|
+
reason: eventType
|
|
355
|
+
});
|
|
356
|
+
await canonicalStateStore.endInstance(this.config.instanceId);
|
|
357
|
+
if (this.ownsSession) {
|
|
358
|
+
await canonicalStateStore.endSession(this.sessionId);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
startDeterminismWatcher() {
|
|
362
|
+
this.determinismWatcher = startDeterminismWatcher({
|
|
363
|
+
stackmemoryBin: this.stackmemoryPath,
|
|
364
|
+
cwd: process.cwd(),
|
|
365
|
+
task: this.config.task,
|
|
366
|
+
instanceId: this.config.instanceId,
|
|
367
|
+
sessionId: this.sessionId,
|
|
368
|
+
tool: "codex"
|
|
369
|
+
});
|
|
370
|
+
if (this.determinismWatcher) {
|
|
371
|
+
const modeLabel = this.determinismWatcher.mode === "targeted" ? "targeted" : "repo-root fallback";
|
|
372
|
+
console.log(chalk.gray(`\u{1F9EA} Determinism: ${modeLabel}`));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
stopDeterminismWatcher() {
|
|
376
|
+
stopDeterminismWatcher(this.determinismWatcher);
|
|
377
|
+
this.determinismWatcher = null;
|
|
378
|
+
}
|
|
200
379
|
async run(args) {
|
|
201
380
|
const codexArgs = [];
|
|
202
381
|
let i = 0;
|
|
@@ -293,9 +472,13 @@ class CodexSM {
|
|
|
293
472
|
}
|
|
294
473
|
this.loadContext();
|
|
295
474
|
process.env["CODEX_INSTANCE_ID"] = this.config.instanceId;
|
|
475
|
+
process.env["STACKMEMORY_SESSION"] = this.sessionId;
|
|
296
476
|
if (this.config.worktreePath)
|
|
297
477
|
process.env["CODEX_WORKTREE_PATH"] = this.config.worktreePath;
|
|
478
|
+
await this.publishSessionStart();
|
|
479
|
+
this.startDeterminismWatcher();
|
|
298
480
|
console.log(chalk.gray(`\u{1F916} Instance ID: ${this.config.instanceId}`));
|
|
481
|
+
console.log(chalk.gray(`\u{1F9E0} Session ID: ${this.sessionId.slice(0, 8)}`));
|
|
299
482
|
console.log(chalk.gray(`\u{1F4C1} Working in: ${process.cwd()}`));
|
|
300
483
|
console.log();
|
|
301
484
|
console.log(chalk.gray("Starting Codex..."));
|
|
@@ -334,11 +517,15 @@ class CodexSM {
|
|
|
334
517
|
}
|
|
335
518
|
process.exit(1);
|
|
336
519
|
});
|
|
337
|
-
child.on("exit", (code) => {
|
|
520
|
+
child.on("exit", async (code) => {
|
|
521
|
+
this.stopDeterminismWatcher();
|
|
338
522
|
this.saveContext("Codex session ended", {
|
|
339
523
|
action: "session_end",
|
|
340
524
|
exitCode: code
|
|
341
525
|
});
|
|
526
|
+
await this.publishSessionEnd("session_end", {
|
|
527
|
+
exitCode: code
|
|
528
|
+
});
|
|
342
529
|
try {
|
|
343
530
|
execSync("stackmemory linear sync", {
|
|
344
531
|
stdio: "ignore",
|
|
@@ -346,6 +533,7 @@ class CodexSM {
|
|
|
346
533
|
});
|
|
347
534
|
} catch {
|
|
348
535
|
}
|
|
536
|
+
this.emitContextBudgetAdvice();
|
|
349
537
|
if (this.config.tracingEnabled) {
|
|
350
538
|
const summary = trace.getExecutionSummary();
|
|
351
539
|
console.log();
|
|
@@ -361,16 +549,20 @@ class CodexSM {
|
|
|
361
549
|
}
|
|
362
550
|
process.exit(code || 0);
|
|
363
551
|
});
|
|
364
|
-
process.on("SIGINT", () => {
|
|
552
|
+
process.on("SIGINT", async () => {
|
|
553
|
+
this.stopDeterminismWatcher();
|
|
365
554
|
this.saveContext("Codex session interrupted", {
|
|
366
555
|
action: "session_interrupt"
|
|
367
556
|
});
|
|
557
|
+
await this.publishSessionEnd("session_interrupt");
|
|
368
558
|
child.kill("SIGINT");
|
|
369
559
|
});
|
|
370
|
-
process.on("SIGTERM", () => {
|
|
560
|
+
process.on("SIGTERM", async () => {
|
|
561
|
+
this.stopDeterminismWatcher();
|
|
371
562
|
this.saveContext("Codex session terminated", {
|
|
372
563
|
action: "session_terminate"
|
|
373
564
|
});
|
|
565
|
+
await this.publishSessionEnd("session_terminate");
|
|
374
566
|
child.kill("SIGTERM");
|
|
375
567
|
});
|
|
376
568
|
}
|
|
@@ -3,6 +3,7 @@ import { dirname as __pathDirname } from 'path';
|
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
5
|
import { Command } from "commander";
|
|
6
|
+
import chalk from "chalk";
|
|
6
7
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
7
8
|
import { join } from "path";
|
|
8
9
|
import {
|
|
@@ -13,6 +14,13 @@ import {
|
|
|
13
14
|
import {
|
|
14
15
|
feedbackLoops
|
|
15
16
|
} from "../../core/monitoring/feedback-loops.js";
|
|
17
|
+
import {
|
|
18
|
+
DETERMINISM_WATCH_IGNORE,
|
|
19
|
+
getDeterminismWatchTargets,
|
|
20
|
+
persistDeterminismReport,
|
|
21
|
+
readLatestDeterminismReport,
|
|
22
|
+
runDeterminismSmoke
|
|
23
|
+
} from "../../orchestrators/multimodal/determinism.js";
|
|
16
24
|
function loadRunMetrics(projectRoot) {
|
|
17
25
|
const metricsFile = join(
|
|
18
26
|
projectRoot,
|
|
@@ -45,6 +53,56 @@ function loadSpikeAudits(projectRoot) {
|
|
|
45
53
|
}
|
|
46
54
|
}).filter(Boolean);
|
|
47
55
|
}
|
|
56
|
+
function printDeterminismReport(task, requestedRuns, report) {
|
|
57
|
+
console.log("\nHarness Determinism Smoke");
|
|
58
|
+
console.log("\u2550".repeat(60));
|
|
59
|
+
console.log(`Task: ${task}`);
|
|
60
|
+
console.log(`Runs: ${report.runs}`);
|
|
61
|
+
console.log(`Determinism score: ${report.score.toFixed(2)}/100`);
|
|
62
|
+
console.log("\nDimension Scores:");
|
|
63
|
+
for (const dimension of report.dimensions) {
|
|
64
|
+
console.log(
|
|
65
|
+
` ${dimension.name.padEnd(14)} ${dimension.score.toFixed(2).padStart(6)}/100 ${dimension.details}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (report.recommendations.length > 0) {
|
|
69
|
+
console.log("\nRecommended Tightening:");
|
|
70
|
+
for (const recommendation of report.recommendations) {
|
|
71
|
+
console.log(` - ${recommendation}`);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
console.log("\nNo drift detected in deterministic fixture mode.");
|
|
75
|
+
}
|
|
76
|
+
const sample = report.snapshots[0];
|
|
77
|
+
if (sample) {
|
|
78
|
+
console.log("\nReference Snapshot:");
|
|
79
|
+
console.log(` resultHash: ${sample.resultHash.slice(0, 16)}`);
|
|
80
|
+
console.log(` planHash: ${sample.planHash.slice(0, 16)}`);
|
|
81
|
+
console.log(` critiqueHash: ${sample.critiqueHash.slice(0, 16)}`);
|
|
82
|
+
console.log(` commandsHash: ${sample.commandsHash.slice(0, 16)}`);
|
|
83
|
+
console.log(` iterations: ${sample.iterations}`);
|
|
84
|
+
console.log(` contextTokens: ${sample.contextTokens}`);
|
|
85
|
+
}
|
|
86
|
+
if (report.runs !== requestedRuns) {
|
|
87
|
+
console.log(
|
|
88
|
+
`
|
|
89
|
+
Note: requested ${requestedRuns} runs, completed ${report.runs}.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
console.log("");
|
|
93
|
+
}
|
|
94
|
+
function printStoredDeterminismReport(stored) {
|
|
95
|
+
console.log("\nCached Determinism Result");
|
|
96
|
+
console.log("\u2550".repeat(60));
|
|
97
|
+
console.log(`Task: ${stored.task}`);
|
|
98
|
+
console.log(`Trigger: ${stored.trigger}`);
|
|
99
|
+
console.log(`Timestamp: ${stored.timestamp}`);
|
|
100
|
+
console.log(`Determinism score: ${stored.report.score.toFixed(2)}/100`);
|
|
101
|
+
if (stored.changedPaths.length > 0) {
|
|
102
|
+
console.log(`Changed paths: ${stored.changedPaths.join(", ")}`);
|
|
103
|
+
}
|
|
104
|
+
console.log("");
|
|
105
|
+
}
|
|
48
106
|
function createBenchCommand() {
|
|
49
107
|
const bench = new Command("bench").description(
|
|
50
108
|
"Harness benchmarks \u2014 compare local runs against SWE-bench baselines"
|
|
@@ -197,11 +255,160 @@ Recent Spike Audits (${audits.length}):`);
|
|
|
197
255
|
}
|
|
198
256
|
console.log("");
|
|
199
257
|
});
|
|
200
|
-
bench.command("
|
|
258
|
+
bench.command("determinism").description(
|
|
259
|
+
"Run deterministic fixture smoke checks for the multimodal harness"
|
|
260
|
+
).option(
|
|
261
|
+
"-t, --task <desc>",
|
|
262
|
+
"Task description to run through the harness",
|
|
263
|
+
"Add a small auth guard"
|
|
264
|
+
).option("--runs <n>", "Number of repeated runs", "5").option(
|
|
265
|
+
"--planner-model <name>",
|
|
266
|
+
"Planner model label to include in the run config",
|
|
267
|
+
"claude-sonnet-4-20250514"
|
|
268
|
+
).option(
|
|
269
|
+
"--reviewer-model <name>",
|
|
270
|
+
"Reviewer model label to include in the run config",
|
|
271
|
+
"claude-sonnet-4-20250514"
|
|
272
|
+
).option("--implementer <name>", "codex|claude", "codex").option("--max-iters <n>", "Retry loop iterations", "2").option(
|
|
273
|
+
"--watch",
|
|
274
|
+
"Watch harness-critical files and rerun on changes",
|
|
275
|
+
false
|
|
276
|
+
).option(
|
|
277
|
+
"--debounce-ms <n>",
|
|
278
|
+
"Debounce window for write completion in watch mode",
|
|
279
|
+
"3000"
|
|
280
|
+
).option("--latest", "Show the latest cached determinism result", false).option("--json", "Output as JSON", false).action(async function() {
|
|
281
|
+
const command = this;
|
|
282
|
+
const options = command.opts();
|
|
283
|
+
const json = Boolean(options.json || command.parent?.opts().json);
|
|
284
|
+
const projectRoot = process.cwd();
|
|
285
|
+
const runs = Math.max(1, parseInt(options.runs, 10) || 5);
|
|
286
|
+
const debounceMs = Math.max(
|
|
287
|
+
250,
|
|
288
|
+
parseInt(options.debounceMs, 10) || 3e3
|
|
289
|
+
);
|
|
290
|
+
if (options.latest) {
|
|
291
|
+
const stored = readLatestDeterminismReport(projectRoot);
|
|
292
|
+
if (!stored) {
|
|
293
|
+
console.error("No cached determinism result found.");
|
|
294
|
+
process.exitCode = 1;
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (json) {
|
|
298
|
+
console.log(JSON.stringify(stored, null, 2));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
printStoredDeterminismReport(stored);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const runCheck = async (trigger, changedPaths = []) => {
|
|
305
|
+
const report = await runDeterminismSmoke(
|
|
306
|
+
{
|
|
307
|
+
task: options.task,
|
|
308
|
+
repoPath: projectRoot
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
runs,
|
|
312
|
+
plannerModel: options.plannerModel,
|
|
313
|
+
reviewerModel: options.reviewerModel,
|
|
314
|
+
implementer: options.implementer,
|
|
315
|
+
maxIters: parseInt(options.maxIters, 10) || 2
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
const stored = persistDeterminismReport(projectRoot, report, {
|
|
319
|
+
task: options.task,
|
|
320
|
+
trigger,
|
|
321
|
+
changedPaths
|
|
322
|
+
});
|
|
323
|
+
if (json) {
|
|
324
|
+
console.log(JSON.stringify(stored, null, 2));
|
|
325
|
+
} else {
|
|
326
|
+
printDeterminismReport(options.task, runs, report);
|
|
327
|
+
}
|
|
328
|
+
return stored;
|
|
329
|
+
};
|
|
330
|
+
if (options.watch) {
|
|
331
|
+
const chokidar = await import("chokidar");
|
|
332
|
+
const watchTargets = getDeterminismWatchTargets(projectRoot);
|
|
333
|
+
const watchPatterns = watchTargets.map(
|
|
334
|
+
(pattern) => join(projectRoot, pattern)
|
|
335
|
+
);
|
|
336
|
+
const watcher = chokidar.watch(watchPatterns, {
|
|
337
|
+
ignoreInitial: true,
|
|
338
|
+
ignored: DETERMINISM_WATCH_IGNORE.map(
|
|
339
|
+
(pattern) => join(projectRoot, pattern)
|
|
340
|
+
),
|
|
341
|
+
awaitWriteFinish: {
|
|
342
|
+
stabilityThreshold: debounceMs,
|
|
343
|
+
pollInterval: 100
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
let running = false;
|
|
347
|
+
let rerunRequested = false;
|
|
348
|
+
const pendingPaths = /* @__PURE__ */ new Set();
|
|
349
|
+
const maybeRun = async (trigger) => {
|
|
350
|
+
if (running) {
|
|
351
|
+
rerunRequested = true;
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
running = true;
|
|
355
|
+
const changedPaths = Array.from(pendingPaths).sort();
|
|
356
|
+
pendingPaths.clear();
|
|
357
|
+
try {
|
|
358
|
+
await runCheck(trigger, changedPaths);
|
|
359
|
+
} finally {
|
|
360
|
+
running = false;
|
|
361
|
+
if (rerunRequested) {
|
|
362
|
+
rerunRequested = false;
|
|
363
|
+
await maybeRun("watch:debounced-rerun");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const onFileEvent = async (trigger, filePath) => {
|
|
368
|
+
const relativePath = filePath.startsWith(projectRoot) ? filePath.slice(projectRoot.length + 1) : filePath;
|
|
369
|
+
pendingPaths.add(relativePath);
|
|
370
|
+
if (!json) {
|
|
371
|
+
console.log(
|
|
372
|
+
chalk.gray(`determinism watcher: ${trigger} ${relativePath}`)
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
await maybeRun(`watch:${trigger}`);
|
|
376
|
+
};
|
|
377
|
+
watcher.on("all", async (eventName, filePath) => {
|
|
378
|
+
if (eventName !== "add" && eventName !== "change") {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await onFileEvent(eventName, filePath);
|
|
382
|
+
});
|
|
383
|
+
if (!json) {
|
|
384
|
+
console.log("\nHarness Determinism Watch");
|
|
385
|
+
console.log("\u2550".repeat(60));
|
|
386
|
+
console.log(`Task: ${options.task}`);
|
|
387
|
+
console.log(`Watching: ${watchTargets.join(", ")}`);
|
|
388
|
+
console.log(`Debounce: ${debounceMs}ms`);
|
|
389
|
+
console.log(chalk.gray("Press Ctrl+C to stop.\n"));
|
|
390
|
+
}
|
|
391
|
+
await runCheck("watch:initial");
|
|
392
|
+
await new Promise((resolve) => {
|
|
393
|
+
const stop = () => {
|
|
394
|
+
void watcher.close();
|
|
395
|
+
resolve();
|
|
396
|
+
};
|
|
397
|
+
process.once("SIGINT", stop);
|
|
398
|
+
process.once("SIGTERM", stop);
|
|
399
|
+
});
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
await runCheck("manual");
|
|
403
|
+
});
|
|
404
|
+
bench.command("loops").description("Show feedback loop configuration, status, and recent events").option("--json", "Output as JSON", false).action(function() {
|
|
405
|
+
const command = this;
|
|
406
|
+
const options = command.opts();
|
|
407
|
+
const json = Boolean(options.json || command.parent?.opts().json);
|
|
201
408
|
const config = feedbackLoops.getConfig();
|
|
202
409
|
const stats = feedbackLoops.getStats();
|
|
203
410
|
const history = feedbackLoops.getHistory(void 0, 20);
|
|
204
|
-
if (
|
|
411
|
+
if (json) {
|
|
205
412
|
console.log(JSON.stringify({ config, stats, history }, null, 2));
|
|
206
413
|
return;
|
|
207
414
|
}
|