@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
|
@@ -23,26 +23,32 @@ import {
|
|
|
23
23
|
} from "fs";
|
|
24
24
|
import { homedir } from "os";
|
|
25
25
|
import { compactPlan } from "../../orchestrators/multimodal/utils.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
parseMasterTasks,
|
|
28
|
+
getNextTask,
|
|
29
|
+
addTaskToFile,
|
|
30
|
+
updateTaskInFile
|
|
31
|
+
} from "../../core/tasks/md-task-parser.js";
|
|
27
32
|
import { join, dirname } from "path";
|
|
28
33
|
import { execSync } from "child_process";
|
|
29
34
|
import { FrameManager } from "../../core/context/index.js";
|
|
30
35
|
import { logger } from "../../core/monitoring/logger.js";
|
|
31
36
|
import { isFeatureEnabled } from "../../core/config/feature-flags.js";
|
|
37
|
+
import { ContentCache } from "../../core/cache/index.js";
|
|
38
|
+
import { TraceEventStore } from "../../core/trace/trace-event-store.js";
|
|
32
39
|
import { BrowserMCPIntegration } from "../../features/browser/browser-mcp.js";
|
|
33
40
|
import { TraceDetector } from "../../core/trace/trace-detector.js";
|
|
34
41
|
import { LLMContextRetrieval } from "../../core/retrieval/index.js";
|
|
35
42
|
import { DiscoveryHandlers } from "./handlers/discovery-handlers.js";
|
|
36
|
-
import { DiffMemHandlers } from "./handlers/diffmem-handlers.js";
|
|
37
|
-
import { GreptileHandlers } from "./handlers/greptile-handlers.js";
|
|
38
|
-
import { CordHandlers } from "./handlers/cord-handlers.js";
|
|
39
|
-
import { TeamHandlers } from "./handlers/team-handlers.js";
|
|
40
|
-
import { SQLiteAdapter } from "../../core/database/sqlite-adapter.js";
|
|
41
43
|
import {
|
|
42
44
|
generateChronologicalDigest
|
|
43
45
|
} from "../../core/digest/chronological-digest.js";
|
|
44
46
|
import { fuzzyEdit } from "../../utils/fuzzy-edit.js";
|
|
45
47
|
import { v4 as uuidv4 } from "uuid";
|
|
48
|
+
import {
|
|
49
|
+
resolveToolAlias,
|
|
50
|
+
resolveParamAliases
|
|
51
|
+
} from "./tool-alias-registry.js";
|
|
46
52
|
import {
|
|
47
53
|
DEFAULT_PLANNER_MODEL,
|
|
48
54
|
DEFAULT_IMPLEMENTER,
|
|
@@ -59,6 +65,28 @@ function _getEnv(key, defaultValue) {
|
|
|
59
65
|
function _getOptionalEnv(key) {
|
|
60
66
|
return process.env[key];
|
|
61
67
|
}
|
|
68
|
+
const CACHEABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
69
|
+
"get_context",
|
|
70
|
+
"get_hot_stack",
|
|
71
|
+
"get_active_tasks",
|
|
72
|
+
"get_task_metrics",
|
|
73
|
+
"get_traces",
|
|
74
|
+
"get_trace_statistics",
|
|
75
|
+
"smart_context",
|
|
76
|
+
"get_summary",
|
|
77
|
+
"sm_discover",
|
|
78
|
+
"sm_related_files",
|
|
79
|
+
"sm_session_summary",
|
|
80
|
+
"sm_search",
|
|
81
|
+
"sm_cross_search",
|
|
82
|
+
"sm_cross_discover",
|
|
83
|
+
"sm_cross_list",
|
|
84
|
+
"linear_get_tasks",
|
|
85
|
+
"linear_status",
|
|
86
|
+
"provenant_search",
|
|
87
|
+
"provenant_status",
|
|
88
|
+
"provenant_contradictions"
|
|
89
|
+
]);
|
|
62
90
|
class LocalStackMemoryMCP {
|
|
63
91
|
server;
|
|
64
92
|
db;
|
|
@@ -73,12 +101,13 @@ class LocalStackMemoryMCP {
|
|
|
73
101
|
traceDetector;
|
|
74
102
|
contextRetrieval;
|
|
75
103
|
discoveryHandlers;
|
|
76
|
-
diffMemHandlers;
|
|
77
|
-
greptileHandlers;
|
|
78
104
|
providerHandlers = null;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
105
|
+
contentCache;
|
|
106
|
+
traceEventStore;
|
|
107
|
+
sessionId;
|
|
108
|
+
sessionTokensSaved = 0;
|
|
109
|
+
sessionCacheHits = 0;
|
|
110
|
+
sessionCacheMisses = 0;
|
|
82
111
|
constructor() {
|
|
83
112
|
this.projectRoot = this.findProjectRoot();
|
|
84
113
|
this.projectId = this.getProjectId();
|
|
@@ -120,6 +149,9 @@ class LocalStackMemoryMCP {
|
|
|
120
149
|
);
|
|
121
150
|
CREATE INDEX IF NOT EXISTS idx_edit_telemetry_ts ON edit_telemetry(timestamp);
|
|
122
151
|
`);
|
|
152
|
+
this.contentCache = new ContentCache(this.db);
|
|
153
|
+
this.traceEventStore = new TraceEventStore(this.db);
|
|
154
|
+
this.sessionId = uuidv4();
|
|
123
155
|
this.frameManager = new FrameManager(this.db, this.projectId);
|
|
124
156
|
this.initLinearIfEnabled();
|
|
125
157
|
this.server = new Server(
|
|
@@ -149,10 +181,6 @@ class LocalStackMemoryMCP {
|
|
|
149
181
|
db: this.db,
|
|
150
182
|
projectRoot: this.projectRoot
|
|
151
183
|
});
|
|
152
|
-
this.diffMemHandlers = new DiffMemHandlers();
|
|
153
|
-
this.greptileHandlers = new GreptileHandlers();
|
|
154
|
-
this.initCordTeamHandlers();
|
|
155
|
-
this.initProviderHandlers();
|
|
156
184
|
this.setupHandlers();
|
|
157
185
|
this.loadInitialContext();
|
|
158
186
|
this.loadPendingPlans();
|
|
@@ -166,6 +194,105 @@ class LocalStackMemoryMCP {
|
|
|
166
194
|
projectId: this.projectId
|
|
167
195
|
});
|
|
168
196
|
}
|
|
197
|
+
// ------------------------------------------------------------------
|
|
198
|
+
// Content-hash cache helpers
|
|
199
|
+
// ------------------------------------------------------------------
|
|
200
|
+
/**
|
|
201
|
+
* Build a deterministic cache key from tool name + sorted args.
|
|
202
|
+
* Returns null for non-cacheable tools or empty args.
|
|
203
|
+
*/
|
|
204
|
+
buildCacheKey(tool, args) {
|
|
205
|
+
if (!CACHEABLE_TOOLS.has(tool)) return null;
|
|
206
|
+
const sorted = JSON.stringify(args, Object.keys(args).sort());
|
|
207
|
+
return `${tool}:${sorted}`;
|
|
208
|
+
}
|
|
209
|
+
handleCacheStats() {
|
|
210
|
+
const stats = this.contentCache.getStats();
|
|
211
|
+
return {
|
|
212
|
+
content: [
|
|
213
|
+
{
|
|
214
|
+
type: "text",
|
|
215
|
+
text: JSON.stringify({
|
|
216
|
+
session: {
|
|
217
|
+
cacheHits: this.sessionCacheHits,
|
|
218
|
+
cacheMisses: this.sessionCacheMisses,
|
|
219
|
+
tokensSaved: this.sessionTokensSaved,
|
|
220
|
+
hitRate: this.sessionCacheHits + this.sessionCacheMisses > 0 ? this.sessionCacheHits / (this.sessionCacheHits + this.sessionCacheMisses) : 0
|
|
221
|
+
},
|
|
222
|
+
lifetime: stats
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
isError: false
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
handleCacheLookup(args) {
|
|
230
|
+
const content = String(args.content ?? "");
|
|
231
|
+
if (!content) {
|
|
232
|
+
return {
|
|
233
|
+
content: [
|
|
234
|
+
{
|
|
235
|
+
type: "text",
|
|
236
|
+
text: JSON.stringify({ error: "content is required" })
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
isError: true
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const result = this.contentCache.lookup(content, String(args.source ?? ""));
|
|
243
|
+
if (result.hit) {
|
|
244
|
+
this.sessionCacheHits++;
|
|
245
|
+
this.sessionTokensSaved += result.tokensSaved;
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
249
|
+
isError: false
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
// ------------------------------------------------------------------
|
|
253
|
+
// Trace event handlers
|
|
254
|
+
// ------------------------------------------------------------------
|
|
255
|
+
handleTraceEvents(args) {
|
|
256
|
+
const events = this.traceEventStore.query({
|
|
257
|
+
session_id: args.session_id,
|
|
258
|
+
operation: args.operation,
|
|
259
|
+
min_score: args.min_score,
|
|
260
|
+
has_feedback: args.has_feedback,
|
|
261
|
+
limit: args.limit ?? 50
|
|
262
|
+
});
|
|
263
|
+
return {
|
|
264
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
265
|
+
isError: false
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
handleTraceEventStats(args) {
|
|
269
|
+
const stats = this.traceEventStore.getStats({
|
|
270
|
+
session_id: args.session_id
|
|
271
|
+
});
|
|
272
|
+
return {
|
|
273
|
+
content: [{ type: "text", text: JSON.stringify(stats) }],
|
|
274
|
+
isError: false
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
handleTraceEventAnnotate(args) {
|
|
278
|
+
const id = String(args.id ?? "");
|
|
279
|
+
if (!id) {
|
|
280
|
+
return {
|
|
281
|
+
content: [
|
|
282
|
+
{ type: "text", text: JSON.stringify({ error: "id is required" }) }
|
|
283
|
+
],
|
|
284
|
+
isError: true
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const ok = this.traceEventStore.annotate(id, {
|
|
288
|
+
score: args.score,
|
|
289
|
+
feedback: args.feedback
|
|
290
|
+
});
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: JSON.stringify({ ok, id }) }],
|
|
293
|
+
isError: false
|
|
294
|
+
};
|
|
295
|
+
}
|
|
169
296
|
findProjectRoot() {
|
|
170
297
|
let dir = process.cwd();
|
|
171
298
|
while (dir !== "/") {
|
|
@@ -276,37 +403,6 @@ ${summary}...`, 0.8);
|
|
|
276
403
|
this.contexts.set(ctx.id, ctx);
|
|
277
404
|
});
|
|
278
405
|
}
|
|
279
|
-
async initCordTeamHandlers() {
|
|
280
|
-
try {
|
|
281
|
-
const dbPath = join(this.projectRoot, ".stackmemory", "context.db");
|
|
282
|
-
const adapter = new SQLiteAdapter(this.projectId, {
|
|
283
|
-
dbPath,
|
|
284
|
-
walMode: true
|
|
285
|
-
});
|
|
286
|
-
await adapter.connect();
|
|
287
|
-
this.cordHandlers = new CordHandlers({
|
|
288
|
-
frameManager: this.frameManager,
|
|
289
|
-
dbAdapter: adapter
|
|
290
|
-
});
|
|
291
|
-
this.teamHandlers = new TeamHandlers({
|
|
292
|
-
frameManager: this.frameManager,
|
|
293
|
-
dbAdapter: adapter
|
|
294
|
-
});
|
|
295
|
-
logger.info("Cord and Team handlers initialized");
|
|
296
|
-
} catch (error) {
|
|
297
|
-
logger.warn("Failed to initialize Cord/Team handlers", { error });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
async initProviderHandlers() {
|
|
301
|
-
if (!isFeatureEnabled("multiProvider")) return;
|
|
302
|
-
try {
|
|
303
|
-
const { ProviderHandlers } = await import("./handlers/provider-handlers.js");
|
|
304
|
-
this.providerHandlers = new ProviderHandlers();
|
|
305
|
-
logger.info("Provider handlers initialized (multiProvider enabled)");
|
|
306
|
-
} catch (error) {
|
|
307
|
-
logger.warn("Failed to initialize provider handlers", { error });
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
406
|
setupHandlers() {
|
|
311
407
|
this.server.setRequestHandler(
|
|
312
408
|
z.object({
|
|
@@ -335,7 +431,6 @@ ${summary}...`, 0.8);
|
|
|
335
431
|
// Planning tools (only when ANTHROPIC_API_KEY is set)
|
|
336
432
|
...process.env.ANTHROPIC_API_KEY ? [
|
|
337
433
|
{
|
|
338
|
-
name: "plan_gate",
|
|
339
434
|
description: "Phase 1: Generate a plan and return an approvalId for later execution",
|
|
340
435
|
inputSchema: {
|
|
341
436
|
type: "object",
|
|
@@ -353,14 +448,12 @@ ${summary}...`, 0.8);
|
|
|
353
448
|
}
|
|
354
449
|
},
|
|
355
450
|
{
|
|
356
|
-
name: "approve_plan",
|
|
357
451
|
description: "Phase 2: Execute a previously generated plan by approvalId",
|
|
358
452
|
inputSchema: {
|
|
359
453
|
type: "object",
|
|
360
454
|
properties: {
|
|
361
455
|
approvalId: {
|
|
362
|
-
type: "string"
|
|
363
|
-
description: "Id from plan_gate"
|
|
456
|
+
type: "string"
|
|
364
457
|
},
|
|
365
458
|
implementer: {
|
|
366
459
|
type: "string",
|
|
@@ -369,6 +462,11 @@ ${summary}...`, 0.8);
|
|
|
369
462
|
description: "Which agent implements code"
|
|
370
463
|
},
|
|
371
464
|
maxIters: { type: "number", default: 2 },
|
|
465
|
+
verificationCommands: {
|
|
466
|
+
type: "array",
|
|
467
|
+
items: { type: "string" },
|
|
468
|
+
description: "Optional repro/test commands that must pass after implementation"
|
|
469
|
+
},
|
|
372
470
|
recordFrame: { type: "boolean", default: true },
|
|
373
471
|
execute: { type: "boolean", default: true }
|
|
374
472
|
},
|
|
@@ -434,15 +532,13 @@ ${summary}...`, 0.8);
|
|
|
434
532
|
type: "object",
|
|
435
533
|
properties: {
|
|
436
534
|
approvalId: {
|
|
437
|
-
type: "string"
|
|
438
|
-
description: "Approval id from plan_gate"
|
|
535
|
+
type: "string"
|
|
439
536
|
}
|
|
440
537
|
},
|
|
441
538
|
required: ["approvalId"]
|
|
442
539
|
}
|
|
443
540
|
},
|
|
444
541
|
{
|
|
445
|
-
name: "plan_only",
|
|
446
542
|
description: "Generate an implementation plan (Claude) and return JSON only",
|
|
447
543
|
inputSchema: {
|
|
448
544
|
type: "object",
|
|
@@ -460,7 +556,6 @@ ${summary}...`, 0.8);
|
|
|
460
556
|
}
|
|
461
557
|
},
|
|
462
558
|
{
|
|
463
|
-
name: "call_codex",
|
|
464
559
|
description: "Invoke Codex via codex-sm with a prompt and args; dry-run by default",
|
|
465
560
|
inputSchema: {
|
|
466
561
|
type: "object",
|
|
@@ -484,7 +579,6 @@ ${summary}...`, 0.8);
|
|
|
484
579
|
}
|
|
485
580
|
},
|
|
486
581
|
{
|
|
487
|
-
name: "call_claude",
|
|
488
582
|
description: "Invoke Claude with a prompt (Anthropic SDK)",
|
|
489
583
|
inputSchema: {
|
|
490
584
|
type: "object",
|
|
@@ -970,194 +1064,8 @@ ${summary}...`, 0.8);
|
|
|
970
1064
|
required: ["query"]
|
|
971
1065
|
}
|
|
972
1066
|
},
|
|
973
|
-
// DiffMem tools (only when DIFFMEM_ENDPOINT or DIFFMEM_ENABLED is set)
|
|
974
1067
|
...process.env.DIFFMEM_ENDPOINT || process.env.DIFFMEM_ENABLED === "true" ? this.diffMemHandlers.getToolDefinitions() : [],
|
|
975
|
-
// Cord task orchestration tools
|
|
976
|
-
{
|
|
977
|
-
name: "cord_spawn",
|
|
978
|
-
description: "Create a subtask with clean context (spawn). Child sees only its prompt and completed blocker results.",
|
|
979
|
-
inputSchema: {
|
|
980
|
-
type: "object",
|
|
981
|
-
properties: {
|
|
982
|
-
goal: {
|
|
983
|
-
type: "string",
|
|
984
|
-
description: "What this task should accomplish"
|
|
985
|
-
},
|
|
986
|
-
prompt: {
|
|
987
|
-
type: "string",
|
|
988
|
-
description: "Detailed instructions for the task"
|
|
989
|
-
},
|
|
990
|
-
blocked_by: {
|
|
991
|
-
type: "array",
|
|
992
|
-
items: { type: "string" },
|
|
993
|
-
description: "Task IDs that must complete first"
|
|
994
|
-
},
|
|
995
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
996
|
-
},
|
|
997
|
-
required: ["goal"]
|
|
998
|
-
}
|
|
999
|
-
},
|
|
1000
|
-
{
|
|
1001
|
-
name: "cord_fork",
|
|
1002
|
-
description: "Create a subtask with full sibling context (fork). Child sees prompt, blocker results, AND completed sibling results.",
|
|
1003
|
-
inputSchema: {
|
|
1004
|
-
type: "object",
|
|
1005
|
-
properties: {
|
|
1006
|
-
goal: {
|
|
1007
|
-
type: "string",
|
|
1008
|
-
description: "What this task should accomplish"
|
|
1009
|
-
},
|
|
1010
|
-
prompt: {
|
|
1011
|
-
type: "string",
|
|
1012
|
-
description: "Detailed instructions for the task"
|
|
1013
|
-
},
|
|
1014
|
-
blocked_by: {
|
|
1015
|
-
type: "array",
|
|
1016
|
-
items: { type: "string" },
|
|
1017
|
-
description: "Task IDs that must complete first"
|
|
1018
|
-
},
|
|
1019
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
1020
|
-
},
|
|
1021
|
-
required: ["goal"]
|
|
1022
|
-
}
|
|
1023
|
-
},
|
|
1024
|
-
{
|
|
1025
|
-
name: "cord_complete",
|
|
1026
|
-
description: "Mark a cord task as completed with a result. Automatically unblocks dependent tasks.",
|
|
1027
|
-
inputSchema: {
|
|
1028
|
-
type: "object",
|
|
1029
|
-
properties: {
|
|
1030
|
-
task_id: {
|
|
1031
|
-
type: "string",
|
|
1032
|
-
description: "Task ID to complete"
|
|
1033
|
-
},
|
|
1034
|
-
result: {
|
|
1035
|
-
type: "string",
|
|
1036
|
-
description: "The result/output of this task"
|
|
1037
|
-
}
|
|
1038
|
-
},
|
|
1039
|
-
required: ["task_id", "result"]
|
|
1040
|
-
}
|
|
1041
|
-
},
|
|
1042
|
-
{
|
|
1043
|
-
name: "cord_ask",
|
|
1044
|
-
description: "Create an ask task \u2014 a question that needs an answer before dependent tasks can proceed.",
|
|
1045
|
-
inputSchema: {
|
|
1046
|
-
type: "object",
|
|
1047
|
-
properties: {
|
|
1048
|
-
question: {
|
|
1049
|
-
type: "string",
|
|
1050
|
-
description: "The question to ask"
|
|
1051
|
-
},
|
|
1052
|
-
options: {
|
|
1053
|
-
type: "array",
|
|
1054
|
-
items: { type: "string" },
|
|
1055
|
-
description: "Optional list of answer choices"
|
|
1056
|
-
},
|
|
1057
|
-
parent_id: { type: "string", description: "Parent task ID" }
|
|
1058
|
-
},
|
|
1059
|
-
required: ["question"]
|
|
1060
|
-
}
|
|
1061
|
-
},
|
|
1062
|
-
{
|
|
1063
|
-
name: "cord_tree",
|
|
1064
|
-
description: "View the cord task tree with context scoping. Shows active, blocked, or completed tasks.",
|
|
1065
|
-
inputSchema: {
|
|
1066
|
-
type: "object",
|
|
1067
|
-
properties: {
|
|
1068
|
-
task_id: {
|
|
1069
|
-
type: "string",
|
|
1070
|
-
description: "Root task ID to show subtree (omit for full tree)"
|
|
1071
|
-
},
|
|
1072
|
-
include_results: {
|
|
1073
|
-
type: "boolean",
|
|
1074
|
-
default: false,
|
|
1075
|
-
description: "Include task results in output"
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
},
|
|
1080
|
-
// Team collaboration tools
|
|
1081
|
-
{
|
|
1082
|
-
name: "team_context_get",
|
|
1083
|
-
description: "Get context from other agents working on the same project. Returns recent frames and shared anchors.",
|
|
1084
|
-
inputSchema: {
|
|
1085
|
-
type: "object",
|
|
1086
|
-
properties: {
|
|
1087
|
-
limit: {
|
|
1088
|
-
type: "number",
|
|
1089
|
-
default: 10,
|
|
1090
|
-
description: "Max frames to return"
|
|
1091
|
-
},
|
|
1092
|
-
types: {
|
|
1093
|
-
type: "array",
|
|
1094
|
-
items: { type: "string" },
|
|
1095
|
-
description: "Filter by frame types"
|
|
1096
|
-
},
|
|
1097
|
-
since: {
|
|
1098
|
-
type: "number",
|
|
1099
|
-
description: "Only frames created after this timestamp (epoch ms)"
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
},
|
|
1104
|
-
{
|
|
1105
|
-
name: "team_context_share",
|
|
1106
|
-
description: "Share a piece of context with other agents. Creates a high-priority anchor visible to team_context_get.",
|
|
1107
|
-
inputSchema: {
|
|
1108
|
-
type: "object",
|
|
1109
|
-
properties: {
|
|
1110
|
-
content: {
|
|
1111
|
-
type: "string",
|
|
1112
|
-
description: "The context to share"
|
|
1113
|
-
},
|
|
1114
|
-
type: {
|
|
1115
|
-
type: "string",
|
|
1116
|
-
enum: [
|
|
1117
|
-
"FACT",
|
|
1118
|
-
"DECISION",
|
|
1119
|
-
"CONSTRAINT",
|
|
1120
|
-
"INTERFACE_CONTRACT",
|
|
1121
|
-
"TODO",
|
|
1122
|
-
"RISK"
|
|
1123
|
-
],
|
|
1124
|
-
default: "FACT",
|
|
1125
|
-
description: "Type of context"
|
|
1126
|
-
},
|
|
1127
|
-
priority: {
|
|
1128
|
-
type: "number",
|
|
1129
|
-
minimum: 1,
|
|
1130
|
-
maximum: 10,
|
|
1131
|
-
default: 8,
|
|
1132
|
-
description: "Priority level (1-10)"
|
|
1133
|
-
}
|
|
1134
|
-
},
|
|
1135
|
-
required: ["content"]
|
|
1136
|
-
}
|
|
1137
|
-
},
|
|
1138
|
-
{
|
|
1139
|
-
name: "team_search",
|
|
1140
|
-
description: "Search across all agents' context in the project. Uses full-text search across all sessions.",
|
|
1141
|
-
inputSchema: {
|
|
1142
|
-
type: "object",
|
|
1143
|
-
properties: {
|
|
1144
|
-
query: { type: "string", description: "Search query" },
|
|
1145
|
-
limit: {
|
|
1146
|
-
type: "number",
|
|
1147
|
-
default: 20,
|
|
1148
|
-
description: "Maximum results to return"
|
|
1149
|
-
},
|
|
1150
|
-
include_events: {
|
|
1151
|
-
type: "boolean",
|
|
1152
|
-
default: false,
|
|
1153
|
-
description: "Include events in results"
|
|
1154
|
-
}
|
|
1155
|
-
},
|
|
1156
|
-
required: ["query"]
|
|
1157
|
-
}
|
|
1158
|
-
},
|
|
1159
1068
|
// Greptile tools (only active when GREPTILE_API_KEY is set)
|
|
1160
|
-
...process.env.GREPTILE_API_KEY ? this.greptileHandlers.getToolDefinitions() : [],
|
|
1161
1069
|
// Provider tools (only active when STACKMEMORY_MULTI_PROVIDER=true)
|
|
1162
1070
|
...isFeatureEnabled("multiProvider") ? [
|
|
1163
1071
|
{
|
|
@@ -1274,6 +1182,143 @@ ${summary}...`, 0.8);
|
|
|
1274
1182
|
},
|
|
1275
1183
|
required: ["period"]
|
|
1276
1184
|
}
|
|
1185
|
+
},
|
|
1186
|
+
// Cross-project search tools
|
|
1187
|
+
{
|
|
1188
|
+
name: "sm_cross_search",
|
|
1189
|
+
description: "Search frames across all registered project databases using FTS5/BM25. Returns results ranked by relevance with source project attribution.",
|
|
1190
|
+
inputSchema: {
|
|
1191
|
+
type: "object",
|
|
1192
|
+
properties: {
|
|
1193
|
+
query: {
|
|
1194
|
+
type: "string",
|
|
1195
|
+
description: "Search query (natural language or keywords)"
|
|
1196
|
+
},
|
|
1197
|
+
limit: {
|
|
1198
|
+
type: "number",
|
|
1199
|
+
default: 20,
|
|
1200
|
+
description: "Maximum results to return"
|
|
1201
|
+
},
|
|
1202
|
+
exclude_current: {
|
|
1203
|
+
type: "boolean",
|
|
1204
|
+
default: false,
|
|
1205
|
+
description: "Exclude the current project from results"
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
1208
|
+
required: ["query"]
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
name: "sm_cross_discover",
|
|
1213
|
+
description: "Auto-discover project databases by scanning common directories for .stackmemory/context.db files.",
|
|
1214
|
+
inputSchema: {
|
|
1215
|
+
type: "object",
|
|
1216
|
+
properties: {
|
|
1217
|
+
paths: {
|
|
1218
|
+
type: "array",
|
|
1219
|
+
items: { type: "string" },
|
|
1220
|
+
description: "Custom directory paths to scan"
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
},
|
|
1225
|
+
{
|
|
1226
|
+
name: "sm_cross_register",
|
|
1227
|
+
description: "Manually register a project database for cross-project search.",
|
|
1228
|
+
inputSchema: {
|
|
1229
|
+
type: "object",
|
|
1230
|
+
properties: {
|
|
1231
|
+
name: {
|
|
1232
|
+
type: "string",
|
|
1233
|
+
description: "Project display name"
|
|
1234
|
+
},
|
|
1235
|
+
path: {
|
|
1236
|
+
type: "string",
|
|
1237
|
+
description: "Project root directory path"
|
|
1238
|
+
},
|
|
1239
|
+
db_path: {
|
|
1240
|
+
type: "string",
|
|
1241
|
+
description: "Path to the SQLite context.db file"
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
required: ["name", "path", "db_path"]
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
name: "sm_cross_list",
|
|
1249
|
+
description: "List all project databases registered for cross-project search.",
|
|
1250
|
+
inputSchema: {
|
|
1251
|
+
type: "object",
|
|
1252
|
+
properties: {}
|
|
1253
|
+
}
|
|
1254
|
+
},
|
|
1255
|
+
// Cache tools
|
|
1256
|
+
{
|
|
1257
|
+
name: "cache_stats",
|
|
1258
|
+
description: "Get content-hash cache statistics: session token savings, hit rate, and lifetime totals. Call this to see how many tokens have been saved by deduplication.",
|
|
1259
|
+
inputSchema: {
|
|
1260
|
+
type: "object",
|
|
1261
|
+
properties: {}
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
name: "cache_lookup",
|
|
1266
|
+
description: "Check if content is already cached. Returns hit/miss and token savings. Use for explicit deduplication before sending large content.",
|
|
1267
|
+
inputSchema: {
|
|
1268
|
+
type: "object",
|
|
1269
|
+
properties: {
|
|
1270
|
+
content: {
|
|
1271
|
+
type: "string",
|
|
1272
|
+
description: "The content to check against the cache."
|
|
1273
|
+
},
|
|
1274
|
+
source: {
|
|
1275
|
+
type: "string",
|
|
1276
|
+
description: 'Optional source label (e.g., "file:src/index.ts").'
|
|
1277
|
+
}
|
|
1278
|
+
},
|
|
1279
|
+
required: ["content"]
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
// Trace event tools
|
|
1283
|
+
{
|
|
1284
|
+
name: "trace_events",
|
|
1285
|
+
description: "Query ASI-shaped trace events. Filter by session, operation, min score, or feedback presence. Returns events with provenance, cost, and token data.",
|
|
1286
|
+
inputSchema: {
|
|
1287
|
+
type: "object",
|
|
1288
|
+
properties: {
|
|
1289
|
+
session_id: { type: "string" },
|
|
1290
|
+
operation: { type: "string" },
|
|
1291
|
+
min_score: { type: "number" },
|
|
1292
|
+
has_feedback: { type: "boolean" },
|
|
1293
|
+
limit: { type: "number" }
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
name: "trace_event_stats",
|
|
1299
|
+
description: "Get aggregate trace event statistics: total tokens, cost, operation counts, host distribution.",
|
|
1300
|
+
inputSchema: {
|
|
1301
|
+
type: "object",
|
|
1302
|
+
properties: {
|
|
1303
|
+
session_id: { type: "string" }
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
name: "trace_event_annotate",
|
|
1309
|
+
description: "Add a numeric score and/or textual feedback to a trace event. Used by GEPA-class optimizers.",
|
|
1310
|
+
inputSchema: {
|
|
1311
|
+
type: "object",
|
|
1312
|
+
properties: {
|
|
1313
|
+
id: { type: "string", description: "Trace event ID" },
|
|
1314
|
+
score: { type: "number", description: "Numeric score (0-1)" },
|
|
1315
|
+
feedback: {
|
|
1316
|
+
type: "string",
|
|
1317
|
+
description: "Textual ASI feedback"
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
required: ["id"]
|
|
1321
|
+
}
|
|
1277
1322
|
}
|
|
1278
1323
|
]
|
|
1279
1324
|
};
|
|
@@ -1288,7 +1333,18 @@ ${summary}...`, 0.8);
|
|
|
1288
1333
|
})
|
|
1289
1334
|
}),
|
|
1290
1335
|
async (request) => {
|
|
1291
|
-
const { name, arguments:
|
|
1336
|
+
const { name: rawName, arguments: rawArgs } = request.params;
|
|
1337
|
+
const aliasResolution = resolveToolAlias(rawName);
|
|
1338
|
+
const name = aliasResolution.canonicalName;
|
|
1339
|
+
const paramResolution = resolveParamAliases(name, rawArgs);
|
|
1340
|
+
const args = paramResolution.resolvedParams;
|
|
1341
|
+
if (aliasResolution.wasAlias || Object.keys(paramResolution.renames).length > 0) {
|
|
1342
|
+
logger.debug("Tool alias resolved", {
|
|
1343
|
+
originalTool: rawName,
|
|
1344
|
+
canonicalTool: name,
|
|
1345
|
+
paramRenames: paramResolution.renames
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1292
1348
|
const callId = uuidv4();
|
|
1293
1349
|
const startTime = Date.now();
|
|
1294
1350
|
const currentFrameId = this.frameManager.getCurrentFrameId();
|
|
@@ -1296,7 +1352,8 @@ ${summary}...`, 0.8);
|
|
|
1296
1352
|
this.frameManager.addEvent("tool_call", {
|
|
1297
1353
|
tool_name: name,
|
|
1298
1354
|
arguments: args,
|
|
1299
|
-
timestamp: startTime
|
|
1355
|
+
timestamp: startTime,
|
|
1356
|
+
...aliasResolution.wasAlias ? { alias_from: rawName } : {}
|
|
1300
1357
|
});
|
|
1301
1358
|
}
|
|
1302
1359
|
const toolCall = {
|
|
@@ -1305,6 +1362,28 @@ ${summary}...`, 0.8);
|
|
|
1305
1362
|
arguments: args,
|
|
1306
1363
|
timestamp: startTime
|
|
1307
1364
|
};
|
|
1365
|
+
const cacheKey = this.buildCacheKey(name, args);
|
|
1366
|
+
if (cacheKey) {
|
|
1367
|
+
const cached = this.contentCache.lookupByKey(
|
|
1368
|
+
cacheKey,
|
|
1369
|
+
`tool:${name}`
|
|
1370
|
+
);
|
|
1371
|
+
if (cached.hit && cached.entry) {
|
|
1372
|
+
this.sessionCacheHits++;
|
|
1373
|
+
this.sessionTokensSaved += cached.tokensSaved;
|
|
1374
|
+
const total = this.sessionCacheHits + this.sessionCacheMisses;
|
|
1375
|
+
const rate = (this.sessionCacheHits / total * 100).toFixed(0);
|
|
1376
|
+
console.error(
|
|
1377
|
+
`[cache] HIT ${name} \u2014 saved ~${cached.tokensSaved} tokens (session: ${this.sessionTokensSaved.toLocaleString()} total, ${rate}% hit rate)`
|
|
1378
|
+
);
|
|
1379
|
+
const cachedResult = JSON.parse(cached.entry.content);
|
|
1380
|
+
toolCall.result = cachedResult;
|
|
1381
|
+
toolCall.duration = Date.now() - startTime;
|
|
1382
|
+
this.traceDetector.addToolCall(toolCall);
|
|
1383
|
+
return cachedResult;
|
|
1384
|
+
}
|
|
1385
|
+
this.sessionCacheMisses++;
|
|
1386
|
+
}
|
|
1308
1387
|
let result;
|
|
1309
1388
|
let error;
|
|
1310
1389
|
try {
|
|
@@ -1372,20 +1451,10 @@ ${summary}...`, 0.8);
|
|
|
1372
1451
|
case "compress_old_traces":
|
|
1373
1452
|
result = await this.handleCompressOldTraces(args);
|
|
1374
1453
|
break;
|
|
1375
|
-
case "plan_only":
|
|
1376
|
-
result = await this.handlePlanOnly(args);
|
|
1377
1454
|
break;
|
|
1378
|
-
case "call_codex":
|
|
1379
|
-
result = await this.handleCallCodex(args);
|
|
1380
1455
|
break;
|
|
1381
|
-
case "call_claude":
|
|
1382
|
-
result = await this.handleCallClaude(args);
|
|
1383
1456
|
break;
|
|
1384
|
-
case "plan_gate":
|
|
1385
|
-
result = await this.handlePlanGate(args);
|
|
1386
1457
|
break;
|
|
1387
|
-
case "approve_plan":
|
|
1388
|
-
result = await this.handleApprovePlan(args);
|
|
1389
1458
|
break;
|
|
1390
1459
|
case "pending_list":
|
|
1391
1460
|
result = await this.handlePendingList();
|
|
@@ -1415,141 +1484,17 @@ ${summary}...`, 0.8);
|
|
|
1415
1484
|
case "sm_search":
|
|
1416
1485
|
result = await this.handleSmSearch(args);
|
|
1417
1486
|
break;
|
|
1418
|
-
|
|
1419
|
-
case "diffmem_get_user_context":
|
|
1420
|
-
result = await this.diffMemHandlers.handleGetUserContext(args);
|
|
1421
|
-
break;
|
|
1422
|
-
case "diffmem_store_learning":
|
|
1423
|
-
result = await this.diffMemHandlers.handleStoreLearning(args);
|
|
1424
|
-
break;
|
|
1425
|
-
case "diffmem_search":
|
|
1426
|
-
result = await this.diffMemHandlers.handleSearch(args);
|
|
1427
|
-
break;
|
|
1428
|
-
case "diffmem_status":
|
|
1429
|
-
result = await this.diffMemHandlers.handleStatus();
|
|
1430
|
-
break;
|
|
1431
|
-
// Greptile handlers
|
|
1432
|
-
case "greptile_pr_comments":
|
|
1433
|
-
result = await this.greptileHandlers.handleListPRComments(args);
|
|
1434
|
-
break;
|
|
1435
|
-
case "greptile_pr_details":
|
|
1436
|
-
result = await this.greptileHandlers.handleGetMergeRequest(args);
|
|
1437
|
-
break;
|
|
1438
|
-
case "greptile_list_prs":
|
|
1439
|
-
result = await this.greptileHandlers.handleListPullRequests(args);
|
|
1440
|
-
break;
|
|
1441
|
-
case "greptile_trigger_review":
|
|
1442
|
-
result = await this.greptileHandlers.handleTriggerCodeReview(args);
|
|
1443
|
-
break;
|
|
1444
|
-
case "greptile_search_patterns":
|
|
1445
|
-
result = await this.greptileHandlers.handleSearchPatterns(args);
|
|
1446
|
-
break;
|
|
1447
|
-
case "greptile_create_pattern":
|
|
1448
|
-
result = await this.greptileHandlers.handleCreatePattern(args);
|
|
1449
|
-
break;
|
|
1450
|
-
case "greptile_status":
|
|
1451
|
-
result = await this.greptileHandlers.handleStatus();
|
|
1452
|
-
break;
|
|
1453
|
-
case "sm_edit":
|
|
1454
|
-
result = await this.handleSmEdit(args);
|
|
1455
|
-
break;
|
|
1456
|
-
// Cord task orchestration
|
|
1457
|
-
case "cord_spawn":
|
|
1458
|
-
if (!this.cordHandlers) {
|
|
1459
|
-
result = {
|
|
1460
|
-
content: [
|
|
1461
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1462
|
-
],
|
|
1463
|
-
is_error: true
|
|
1464
|
-
};
|
|
1465
|
-
} else {
|
|
1466
|
-
result = await this.cordHandlers.handleCordSpawn(args);
|
|
1467
|
-
}
|
|
1468
|
-
break;
|
|
1469
|
-
case "cord_fork":
|
|
1470
|
-
if (!this.cordHandlers) {
|
|
1471
|
-
result = {
|
|
1472
|
-
content: [
|
|
1473
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1474
|
-
],
|
|
1475
|
-
is_error: true
|
|
1476
|
-
};
|
|
1477
|
-
} else {
|
|
1478
|
-
result = await this.cordHandlers.handleCordFork(args);
|
|
1479
|
-
}
|
|
1480
|
-
break;
|
|
1481
|
-
case "cord_complete":
|
|
1482
|
-
if (!this.cordHandlers) {
|
|
1483
|
-
result = {
|
|
1484
|
-
content: [
|
|
1485
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1486
|
-
],
|
|
1487
|
-
is_error: true
|
|
1488
|
-
};
|
|
1489
|
-
} else {
|
|
1490
|
-
result = await this.cordHandlers.handleCordComplete(args);
|
|
1491
|
-
}
|
|
1492
|
-
break;
|
|
1493
|
-
case "cord_ask":
|
|
1494
|
-
if (!this.cordHandlers) {
|
|
1495
|
-
result = {
|
|
1496
|
-
content: [
|
|
1497
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1498
|
-
],
|
|
1499
|
-
is_error: true
|
|
1500
|
-
};
|
|
1501
|
-
} else {
|
|
1502
|
-
result = await this.cordHandlers.handleCordAsk(args);
|
|
1503
|
-
}
|
|
1487
|
+
result = await this.diffMemHandlers.handleGetUserContext(args);
|
|
1504
1488
|
break;
|
|
1505
|
-
|
|
1506
|
-
if (!this.cordHandlers) {
|
|
1507
|
-
result = {
|
|
1508
|
-
content: [
|
|
1509
|
-
{ type: "text", text: "Cord handlers not initialized." }
|
|
1510
|
-
],
|
|
1511
|
-
is_error: true
|
|
1512
|
-
};
|
|
1513
|
-
} else {
|
|
1514
|
-
result = await this.cordHandlers.handleCordTree(args);
|
|
1515
|
-
}
|
|
1489
|
+
result = await this.diffMemHandlers.handleStoreLearning(args);
|
|
1516
1490
|
break;
|
|
1517
|
-
|
|
1518
|
-
case "team_context_get":
|
|
1519
|
-
if (!this.teamHandlers) {
|
|
1520
|
-
result = {
|
|
1521
|
-
content: [
|
|
1522
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1523
|
-
],
|
|
1524
|
-
is_error: true
|
|
1525
|
-
};
|
|
1526
|
-
} else {
|
|
1527
|
-
result = await this.teamHandlers.handleTeamContextGet(args);
|
|
1528
|
-
}
|
|
1491
|
+
result = await this.diffMemHandlers.handleSearch(args);
|
|
1529
1492
|
break;
|
|
1530
|
-
|
|
1531
|
-
if (!this.teamHandlers) {
|
|
1532
|
-
result = {
|
|
1533
|
-
content: [
|
|
1534
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1535
|
-
],
|
|
1536
|
-
is_error: true
|
|
1537
|
-
};
|
|
1538
|
-
} else {
|
|
1539
|
-
result = await this.teamHandlers.handleTeamContextShare(args);
|
|
1540
|
-
}
|
|
1493
|
+
result = await this.diffMemHandlers.handleStatus();
|
|
1541
1494
|
break;
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
content: [
|
|
1546
|
-
{ type: "text", text: "Team handlers not initialized." }
|
|
1547
|
-
],
|
|
1548
|
-
is_error: true
|
|
1549
|
-
};
|
|
1550
|
-
} else {
|
|
1551
|
-
result = await this.teamHandlers.handleTeamSearch(args);
|
|
1552
|
-
}
|
|
1495
|
+
// Greptile handlers
|
|
1496
|
+
case "sm_edit":
|
|
1497
|
+
result = await this.handleSmEdit(args);
|
|
1553
1498
|
break;
|
|
1554
1499
|
// Provider tools
|
|
1555
1500
|
case "delegate_to_model":
|
|
@@ -1609,6 +1554,44 @@ ${summary}...`, 0.8);
|
|
|
1609
1554
|
case "sm_desire_paths":
|
|
1610
1555
|
result = this.handleDesirePaths(args);
|
|
1611
1556
|
break;
|
|
1557
|
+
case "sm_cross_search":
|
|
1558
|
+
result = await this.crossSearchHandlers.handleCrossSearch(args);
|
|
1559
|
+
break;
|
|
1560
|
+
case "sm_cross_discover":
|
|
1561
|
+
result = await this.crossSearchHandlers.handleCrossDiscover(args);
|
|
1562
|
+
break;
|
|
1563
|
+
case "sm_cross_register":
|
|
1564
|
+
result = await this.crossSearchHandlers.handleCrossRegister(args);
|
|
1565
|
+
break;
|
|
1566
|
+
case "sm_cross_list":
|
|
1567
|
+
result = await this.crossSearchHandlers.handleCrossList();
|
|
1568
|
+
break;
|
|
1569
|
+
// Cache tools
|
|
1570
|
+
case "cache_stats":
|
|
1571
|
+
result = this.handleCacheStats();
|
|
1572
|
+
break;
|
|
1573
|
+
case "cache_lookup":
|
|
1574
|
+
result = this.handleCacheLookup(args);
|
|
1575
|
+
break;
|
|
1576
|
+
// Trace event tools
|
|
1577
|
+
case "trace_events":
|
|
1578
|
+
result = this.handleTraceEvents(args);
|
|
1579
|
+
break;
|
|
1580
|
+
case "trace_event_stats":
|
|
1581
|
+
result = this.handleTraceEventStats(args);
|
|
1582
|
+
break;
|
|
1583
|
+
case "trace_event_annotate":
|
|
1584
|
+
result = this.handleTraceEventAnnotate(args);
|
|
1585
|
+
break;
|
|
1586
|
+
case "get_next_master_task":
|
|
1587
|
+
result = this.handleGetNextMasterTask(args);
|
|
1588
|
+
break;
|
|
1589
|
+
case "update_master_task":
|
|
1590
|
+
result = this.handleUpdateMasterTask(args);
|
|
1591
|
+
break;
|
|
1592
|
+
case "create_master_task":
|
|
1593
|
+
result = this.handleCreateMasterTask(args);
|
|
1594
|
+
break;
|
|
1612
1595
|
default:
|
|
1613
1596
|
throw new Error(`Unknown tool: ${name}`);
|
|
1614
1597
|
}
|
|
@@ -1619,6 +1602,22 @@ ${summary}...`, 0.8);
|
|
|
1619
1602
|
throw err;
|
|
1620
1603
|
} finally {
|
|
1621
1604
|
const endTime = Date.now();
|
|
1605
|
+
if (!error && result && cacheKey) {
|
|
1606
|
+
try {
|
|
1607
|
+
const serialized = JSON.stringify(result);
|
|
1608
|
+
const entry = this.contentCache.putByKey(
|
|
1609
|
+
cacheKey,
|
|
1610
|
+
serialized,
|
|
1611
|
+
`tool:${name}`
|
|
1612
|
+
);
|
|
1613
|
+
if (entry.hitCount === 0) {
|
|
1614
|
+
console.error(
|
|
1615
|
+
`[cache] STORE ${name} \u2014 ~${entry.tokenCount} tokens cached for future dedup`
|
|
1616
|
+
);
|
|
1617
|
+
}
|
|
1618
|
+
} catch {
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1622
1621
|
if (currentFrameId && name !== "close_frame") {
|
|
1623
1622
|
try {
|
|
1624
1623
|
this.frameManager.addEvent("tool_result", {
|
|
@@ -1641,11 +1640,50 @@ ${summary}...`, 0.8);
|
|
|
1641
1640
|
toolCall.filesAffected = Array.isArray(files) ? files : [files];
|
|
1642
1641
|
}
|
|
1643
1642
|
this.traceDetector.addToolCall(toolCall);
|
|
1643
|
+
try {
|
|
1644
|
+
const traceEvent = {
|
|
1645
|
+
timestamp: new Date(startTime).toISOString(),
|
|
1646
|
+
session_id: this.sessionId,
|
|
1647
|
+
trace_id: callId,
|
|
1648
|
+
tenant_id: "local",
|
|
1649
|
+
actor: {
|
|
1650
|
+
host: process.env["STACKMEMORY_HOST"] || "claude-code",
|
|
1651
|
+
agent: "stackmemory-mcp",
|
|
1652
|
+
user: process.env["USER"] || "anonymous"
|
|
1653
|
+
},
|
|
1654
|
+
operation: name,
|
|
1655
|
+
inputs: args,
|
|
1656
|
+
outputs: error ? { error: error.message } : result ?? {},
|
|
1657
|
+
tokens_in: 0,
|
|
1658
|
+
tokens_out: 0,
|
|
1659
|
+
cost_usd: 0,
|
|
1660
|
+
duration_ms: endTime - startTime,
|
|
1661
|
+
error: error?.message,
|
|
1662
|
+
provenance: {
|
|
1663
|
+
sources: [{ type: "tool", id: name }],
|
|
1664
|
+
derivation: ["mcp-call"],
|
|
1665
|
+
confidence: 1
|
|
1666
|
+
}
|
|
1667
|
+
};
|
|
1668
|
+
this.traceEventStore.record(traceEvent);
|
|
1669
|
+
} catch {
|
|
1670
|
+
}
|
|
1644
1671
|
}
|
|
1645
1672
|
return result;
|
|
1646
1673
|
}
|
|
1647
1674
|
);
|
|
1648
1675
|
}
|
|
1676
|
+
getVerificationCommands(args) {
|
|
1677
|
+
const commands = args.verificationCommands ?? args.verifyCommands;
|
|
1678
|
+
if (Array.isArray(commands)) {
|
|
1679
|
+
return commands.map((command) => String(command).trim()).filter(Boolean);
|
|
1680
|
+
}
|
|
1681
|
+
const single = args.verificationCommand ?? args.verifyCommand;
|
|
1682
|
+
if (typeof single === "string" && single.trim()) {
|
|
1683
|
+
return [single.trim()];
|
|
1684
|
+
}
|
|
1685
|
+
return [];
|
|
1686
|
+
}
|
|
1649
1687
|
// Handle plan_and_code tool by invoking the mm harness
|
|
1650
1688
|
async handlePlanAndCode(args) {
|
|
1651
1689
|
const { runSpike } = await import("../../orchestrators/multimodal/harness.js");
|
|
@@ -1660,6 +1698,7 @@ ${summary}...`, 0.8);
|
|
|
1660
1698
|
const record = Boolean(args.record);
|
|
1661
1699
|
const recordFrame = Boolean(args.recordFrame);
|
|
1662
1700
|
const compact = Boolean(args.compact);
|
|
1701
|
+
const verificationCommands = this.getVerificationCommands(args);
|
|
1663
1702
|
const task = String(args.task || "Plan and implement change");
|
|
1664
1703
|
const result = await runSpike(
|
|
1665
1704
|
{
|
|
@@ -1673,6 +1712,7 @@ ${summary}...`, 0.8);
|
|
|
1673
1712
|
maxIters: isFinite(maxIters) ? Math.max(1, maxIters) : 2,
|
|
1674
1713
|
dryRun: !execute,
|
|
1675
1714
|
auditDir: void 0,
|
|
1715
|
+
verificationCommands,
|
|
1676
1716
|
recordFrame
|
|
1677
1717
|
}
|
|
1678
1718
|
);
|
|
@@ -1696,60 +1736,6 @@ ${summary}...`, 0.8);
|
|
|
1696
1736
|
isError: false
|
|
1697
1737
|
};
|
|
1698
1738
|
}
|
|
1699
|
-
async handlePlanOnly(args) {
|
|
1700
|
-
const { runPlanOnly } = await import("../../orchestrators/multimodal/harness.js");
|
|
1701
|
-
const task = String(args.task || "Plan change");
|
|
1702
|
-
const plannerModel = args.plannerModel || process.env["STACKMEMORY_MM_PLANNER_MODEL"] || DEFAULT_PLANNER_MODEL;
|
|
1703
|
-
const plan = await runPlanOnly(
|
|
1704
|
-
{ task, repoPath: this.projectRoot },
|
|
1705
|
-
{ plannerModel }
|
|
1706
|
-
);
|
|
1707
|
-
return {
|
|
1708
|
-
content: [
|
|
1709
|
-
{
|
|
1710
|
-
type: "text",
|
|
1711
|
-
text: JSON.stringify({ ok: true, plan })
|
|
1712
|
-
}
|
|
1713
|
-
],
|
|
1714
|
-
isError: false
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
async handleCallCodex(args) {
|
|
1718
|
-
const { callCodexCLI } = await import("../../orchestrators/multimodal/providers.js");
|
|
1719
|
-
const prompt = String(args.prompt || "");
|
|
1720
|
-
const extraArgs = Array.isArray(args.args) ? args.args : [];
|
|
1721
|
-
const execute = Boolean(args.execute);
|
|
1722
|
-
const resp = callCodexCLI(prompt, extraArgs, !execute);
|
|
1723
|
-
return {
|
|
1724
|
-
content: [
|
|
1725
|
-
{
|
|
1726
|
-
type: "text",
|
|
1727
|
-
text: JSON.stringify({
|
|
1728
|
-
ok: resp.ok,
|
|
1729
|
-
command: resp.command,
|
|
1730
|
-
output: resp.output
|
|
1731
|
-
})
|
|
1732
|
-
}
|
|
1733
|
-
],
|
|
1734
|
-
isError: false
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
async handleCallClaude(args) {
|
|
1738
|
-
const { callClaude } = await import("../../orchestrators/multimodal/providers.js");
|
|
1739
|
-
const prompt = String(args.prompt || "");
|
|
1740
|
-
const model = args.model || process.env["STACKMEMORY_MM_PLANNER_MODEL"] || DEFAULT_PLANNER_MODEL;
|
|
1741
|
-
const system = args.system || "You are a precise assistant. Return plain text unless asked for JSON.";
|
|
1742
|
-
const text = await callClaude(prompt, { model, system });
|
|
1743
|
-
return {
|
|
1744
|
-
content: [
|
|
1745
|
-
{
|
|
1746
|
-
type: "text",
|
|
1747
|
-
text: JSON.stringify({ ok: true, text })
|
|
1748
|
-
}
|
|
1749
|
-
],
|
|
1750
|
-
isError: false
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
1739
|
// Pending plan persistence (best-effort)
|
|
1754
1740
|
getPendingStoreDir() {
|
|
1755
1741
|
return join(this.projectRoot, ".stackmemory", "build");
|
|
@@ -1773,7 +1759,6 @@ ${summary}...`, 0.8);
|
|
|
1773
1759
|
}
|
|
1774
1760
|
const data = JSON.parse(readFileSync(sourceFile, "utf-8"));
|
|
1775
1761
|
if (data && typeof data === "object") {
|
|
1776
|
-
this.pendingPlans = new Map(Object.entries(data));
|
|
1777
1762
|
if (sourceFile !== file) this.savePendingPlans();
|
|
1778
1763
|
}
|
|
1779
1764
|
} catch {
|
|
@@ -1784,212 +1769,11 @@ ${summary}...`, 0.8);
|
|
|
1784
1769
|
const dir = this.getPendingStoreDir();
|
|
1785
1770
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
1786
1771
|
const file = this.getPendingStorePath();
|
|
1787
|
-
const obj = Object.fromEntries(this.pendingPlans);
|
|
1788
1772
|
writeFileSync(file, JSON.stringify(obj, null, 2));
|
|
1789
1773
|
} catch {
|
|
1790
1774
|
}
|
|
1791
1775
|
}
|
|
1792
|
-
|
|
1793
|
-
const { runPlanOnly } = await import("../../orchestrators/multimodal/harness.js");
|
|
1794
|
-
const task = String(args.task || "Plan change");
|
|
1795
|
-
const plannerModel = args.plannerModel || process.env["STACKMEMORY_MM_PLANNER_MODEL"] || DEFAULT_PLANNER_MODEL;
|
|
1796
|
-
const plan = await runPlanOnly(
|
|
1797
|
-
{ task, repoPath: this.projectRoot },
|
|
1798
|
-
{ plannerModel }
|
|
1799
|
-
);
|
|
1800
|
-
const approvalId = `appr_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1801
|
-
this.pendingPlans.set(approvalId, { task, plan, createdAt: Date.now() });
|
|
1802
|
-
this.savePendingPlans();
|
|
1803
|
-
const compact = Boolean(args.compact);
|
|
1804
|
-
const planOut = compact ? compactPlan(plan) : plan;
|
|
1805
|
-
return {
|
|
1806
|
-
content: [
|
|
1807
|
-
{
|
|
1808
|
-
type: "text",
|
|
1809
|
-
text: JSON.stringify({ ok: true, approvalId, plan: planOut })
|
|
1810
|
-
}
|
|
1811
|
-
],
|
|
1812
|
-
isError: false
|
|
1813
|
-
};
|
|
1814
|
-
}
|
|
1815
|
-
async handleApprovePlan(args) {
|
|
1816
|
-
const { runSpike } = await import("../../orchestrators/multimodal/harness.js");
|
|
1817
|
-
const approvalId = String(args.approvalId || "");
|
|
1818
|
-
const pending = this.pendingPlans.get(approvalId);
|
|
1819
|
-
if (!pending) {
|
|
1820
|
-
return {
|
|
1821
|
-
content: [
|
|
1822
|
-
{
|
|
1823
|
-
type: "text",
|
|
1824
|
-
text: JSON.stringify({ ok: false, error: "Invalid approvalId" })
|
|
1825
|
-
}
|
|
1826
|
-
],
|
|
1827
|
-
isError: false
|
|
1828
|
-
};
|
|
1829
|
-
}
|
|
1830
|
-
const implementer = args.implementer || process.env["STACKMEMORY_MM_IMPLEMENTER"] || DEFAULT_IMPLEMENTER;
|
|
1831
|
-
const maxIters = Number(
|
|
1832
|
-
args.maxIters ?? process.env["STACKMEMORY_MM_MAX_ITERS"] ?? DEFAULT_MAX_ITERS
|
|
1833
|
-
);
|
|
1834
|
-
const recordFrame = args.recordFrame !== false;
|
|
1835
|
-
const execute = args.execute !== false;
|
|
1836
|
-
const result = await runSpike(
|
|
1837
|
-
{ task: pending.task, repoPath: this.projectRoot },
|
|
1838
|
-
{
|
|
1839
|
-
plannerModel: process.env["STACKMEMORY_MM_PLANNER_MODEL"] || DEFAULT_PLANNER_MODEL,
|
|
1840
|
-
reviewerModel: process.env["STACKMEMORY_MM_REVIEWER_MODEL"] || process.env["STACKMEMORY_MM_PLANNER_MODEL"] || DEFAULT_PLANNER_MODEL,
|
|
1841
|
-
implementer: implementer === "claude" ? "claude" : "codex",
|
|
1842
|
-
maxIters: isFinite(maxIters) ? Math.max(1, maxIters) : 2,
|
|
1843
|
-
dryRun: !execute,
|
|
1844
|
-
recordFrame
|
|
1845
|
-
}
|
|
1846
|
-
);
|
|
1847
|
-
this.pendingPlans.delete(approvalId);
|
|
1848
|
-
this.savePendingPlans();
|
|
1849
|
-
const compact = Boolean(args.compact);
|
|
1850
|
-
const payload = compact ? { ...result, plan: compactPlan(result.plan) } : result;
|
|
1851
|
-
return {
|
|
1852
|
-
content: [
|
|
1853
|
-
{
|
|
1854
|
-
type: "text",
|
|
1855
|
-
text: JSON.stringify({ ok: true, approvalId, result: payload })
|
|
1856
|
-
}
|
|
1857
|
-
],
|
|
1858
|
-
isError: false
|
|
1859
|
-
};
|
|
1860
|
-
}
|
|
1861
|
-
async handlePendingList(args) {
|
|
1862
|
-
const schema = z.object({
|
|
1863
|
-
taskContains: z.string().optional(),
|
|
1864
|
-
olderThanMs: z.number().optional(),
|
|
1865
|
-
newerThanMs: z.number().optional(),
|
|
1866
|
-
sort: z.enum(["asc", "desc"]).optional(),
|
|
1867
|
-
limit: z.number().int().positive().optional()
|
|
1868
|
-
}).optional();
|
|
1869
|
-
const parsed = schema.safeParse(args);
|
|
1870
|
-
if (args && !parsed.success) {
|
|
1871
|
-
return {
|
|
1872
|
-
content: [
|
|
1873
|
-
{
|
|
1874
|
-
type: "text",
|
|
1875
|
-
text: JSON.stringify({
|
|
1876
|
-
ok: false,
|
|
1877
|
-
error: "Invalid arguments",
|
|
1878
|
-
details: parsed.error.issues
|
|
1879
|
-
})
|
|
1880
|
-
}
|
|
1881
|
-
],
|
|
1882
|
-
isError: false
|
|
1883
|
-
};
|
|
1884
|
-
}
|
|
1885
|
-
const a = parsed.success && parsed.data ? parsed.data : {};
|
|
1886
|
-
const now = Date.now();
|
|
1887
|
-
let items = Array.from(this.pendingPlans.entries()).map(
|
|
1888
|
-
([approvalId, data]) => ({
|
|
1889
|
-
approvalId,
|
|
1890
|
-
task: data?.task,
|
|
1891
|
-
createdAt: Number(data?.createdAt || 0) || null
|
|
1892
|
-
})
|
|
1893
|
-
);
|
|
1894
|
-
items = filterPending(items, a, now);
|
|
1895
|
-
return {
|
|
1896
|
-
content: [
|
|
1897
|
-
{ type: "text", text: JSON.stringify({ ok: true, pending: items }) }
|
|
1898
|
-
],
|
|
1899
|
-
isError: false
|
|
1900
|
-
};
|
|
1901
|
-
}
|
|
1902
|
-
async handlePendingClear(args) {
|
|
1903
|
-
const removed = [];
|
|
1904
|
-
const now = Date.now();
|
|
1905
|
-
const all = Boolean(args?.all);
|
|
1906
|
-
const approvalId = args?.approvalId ? String(args.approvalId) : void 0;
|
|
1907
|
-
const olderThanMs = Number.isFinite(Number(args?.olderThanMs)) ? Number(args.olderThanMs) : void 0;
|
|
1908
|
-
if (all) {
|
|
1909
|
-
for (const id of this.pendingPlans.keys()) removed.push(id);
|
|
1910
|
-
this.pendingPlans.clear();
|
|
1911
|
-
this.savePendingPlans();
|
|
1912
|
-
return {
|
|
1913
|
-
content: [
|
|
1914
|
-
{ type: "text", text: JSON.stringify({ ok: true, removed }) }
|
|
1915
|
-
],
|
|
1916
|
-
isError: false
|
|
1917
|
-
};
|
|
1918
|
-
}
|
|
1919
|
-
if (approvalId) {
|
|
1920
|
-
if (this.pendingPlans.has(approvalId)) {
|
|
1921
|
-
this.pendingPlans.delete(approvalId);
|
|
1922
|
-
removed.push(approvalId);
|
|
1923
|
-
this.savePendingPlans();
|
|
1924
|
-
}
|
|
1925
|
-
return {
|
|
1926
|
-
content: [
|
|
1927
|
-
{ type: "text", text: JSON.stringify({ ok: true, removed }) }
|
|
1928
|
-
],
|
|
1929
|
-
isError: false
|
|
1930
|
-
};
|
|
1931
|
-
}
|
|
1932
|
-
if (olderThanMs !== void 0 && olderThanMs >= 0) {
|
|
1933
|
-
for (const [id, data] of this.pendingPlans.entries()) {
|
|
1934
|
-
const ts = Number(data?.createdAt || 0);
|
|
1935
|
-
if (ts && now - ts > olderThanMs) {
|
|
1936
|
-
this.pendingPlans.delete(id);
|
|
1937
|
-
removed.push(id);
|
|
1938
|
-
}
|
|
1939
|
-
}
|
|
1940
|
-
this.savePendingPlans();
|
|
1941
|
-
return {
|
|
1942
|
-
content: [
|
|
1943
|
-
{ type: "text", text: JSON.stringify({ ok: true, removed }) }
|
|
1944
|
-
],
|
|
1945
|
-
isError: false
|
|
1946
|
-
};
|
|
1947
|
-
}
|
|
1948
|
-
return {
|
|
1949
|
-
content: [
|
|
1950
|
-
{
|
|
1951
|
-
type: "text",
|
|
1952
|
-
text: JSON.stringify({
|
|
1953
|
-
ok: false,
|
|
1954
|
-
error: "Specify approvalId, all=true, or olderThanMs"
|
|
1955
|
-
})
|
|
1956
|
-
}
|
|
1957
|
-
],
|
|
1958
|
-
isError: false
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
|
-
async handlePendingShow(args) {
|
|
1962
|
-
const approvalId = String(args?.approvalId || "");
|
|
1963
|
-
const data = this.pendingPlans.get(approvalId);
|
|
1964
|
-
if (!data) {
|
|
1965
|
-
return {
|
|
1966
|
-
content: [
|
|
1967
|
-
{
|
|
1968
|
-
type: "text",
|
|
1969
|
-
text: JSON.stringify({ ok: false, error: "Invalid approvalId" })
|
|
1970
|
-
}
|
|
1971
|
-
],
|
|
1972
|
-
isError: false
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
|
-
const compact = Boolean(args.compact);
|
|
1976
|
-
const planOut = compact ? compactPlan(data.plan) : data.plan;
|
|
1977
|
-
return {
|
|
1978
|
-
content: [
|
|
1979
|
-
{
|
|
1980
|
-
type: "text",
|
|
1981
|
-
text: JSON.stringify({
|
|
1982
|
-
ok: true,
|
|
1983
|
-
approvalId,
|
|
1984
|
-
task: data.task,
|
|
1985
|
-
plan: planOut,
|
|
1986
|
-
createdAt: data.createdAt || null
|
|
1987
|
-
})
|
|
1988
|
-
}
|
|
1989
|
-
],
|
|
1990
|
-
isError: false
|
|
1991
|
-
};
|
|
1992
|
-
}
|
|
1776
|
+
// [REMOVED in v1.14] Conductor/multimodal handlers + pending plan handlers
|
|
1993
1777
|
async handleGetContext(args) {
|
|
1994
1778
|
const { query: query2 = "", limit = 10 } = args;
|
|
1995
1779
|
const contexts = Array.from(this.contexts.values()).sort((a, b) => b.importance - a.importance).slice(0, limit);
|
|
@@ -3341,6 +3125,123 @@ ${catLines.join("\n")}`
|
|
|
3341
3125
|
const transport = new StdioServerTransport();
|
|
3342
3126
|
await this.server.connect(transport);
|
|
3343
3127
|
console.error("StackMemory MCP Server started");
|
|
3128
|
+
const printCacheSummary = () => {
|
|
3129
|
+
if (this.sessionCacheHits > 0 || this.sessionCacheMisses > 0) {
|
|
3130
|
+
const total = this.sessionCacheHits + this.sessionCacheMisses;
|
|
3131
|
+
const rate = (this.sessionCacheHits / total * 100).toFixed(1);
|
|
3132
|
+
console.error(
|
|
3133
|
+
`
|
|
3134
|
+
\u{1F4CA} Cache: ${this.sessionCacheHits}/${total} hits (${rate}%), ~${this.sessionTokensSaved.toLocaleString()} tokens saved`
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
};
|
|
3138
|
+
process.on("SIGINT", printCacheSummary);
|
|
3139
|
+
process.on("SIGTERM", printCacheSummary);
|
|
3140
|
+
process.on("exit", printCacheSummary);
|
|
3141
|
+
}
|
|
3142
|
+
// ── Master Task Handlers ───────────────────────────────────
|
|
3143
|
+
resolveMasterTasksPath() {
|
|
3144
|
+
const smPath = join(
|
|
3145
|
+
this.projectRoot,
|
|
3146
|
+
".stackmemory",
|
|
3147
|
+
"tasks",
|
|
3148
|
+
"master-tasks.md"
|
|
3149
|
+
);
|
|
3150
|
+
if (existsSync(smPath)) return smPath;
|
|
3151
|
+
const rootPath = join(this.projectRoot, "master-tasks.md");
|
|
3152
|
+
if (existsSync(rootPath)) return rootPath;
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
handleGetNextMasterTask(args) {
|
|
3156
|
+
const mdPath = this.resolveMasterTasksPath();
|
|
3157
|
+
if (!mdPath) {
|
|
3158
|
+
return {
|
|
3159
|
+
content: [
|
|
3160
|
+
{
|
|
3161
|
+
type: "text",
|
|
3162
|
+
text: 'No master-tasks.md found. Run "stackmemory tasks init" to create one.'
|
|
3163
|
+
}
|
|
3164
|
+
]
|
|
3165
|
+
};
|
|
3166
|
+
}
|
|
3167
|
+
let tasks = parseMasterTasks(readFileSync(mdPath, "utf-8"));
|
|
3168
|
+
if (args.owner) {
|
|
3169
|
+
tasks = tasks.filter((t) => t.owner === String(args.owner));
|
|
3170
|
+
}
|
|
3171
|
+
const next = getNextTask(tasks);
|
|
3172
|
+
if (!next) {
|
|
3173
|
+
return {
|
|
3174
|
+
content: [{ type: "text", text: "No actionable tasks found." }]
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
return {
|
|
3178
|
+
content: [{ type: "text", text: JSON.stringify(next, null, 2) }]
|
|
3179
|
+
};
|
|
3180
|
+
}
|
|
3181
|
+
handleUpdateMasterTask(args) {
|
|
3182
|
+
const mdPath = this.resolveMasterTasksPath();
|
|
3183
|
+
if (!mdPath) {
|
|
3184
|
+
return {
|
|
3185
|
+
content: [{ type: "text", text: "No master-tasks.md found." }]
|
|
3186
|
+
};
|
|
3187
|
+
}
|
|
3188
|
+
const taskId = String(args.task_id || "").toUpperCase();
|
|
3189
|
+
if (!taskId) {
|
|
3190
|
+
return {
|
|
3191
|
+
content: [{ type: "text", text: "task_id is required." }],
|
|
3192
|
+
isError: true
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
3195
|
+
const updates = {};
|
|
3196
|
+
if (args.status) updates.status = String(args.status);
|
|
3197
|
+
if (args.priority) updates.priority = String(args.priority);
|
|
3198
|
+
if (args.owner) updates.owner = String(args.owner);
|
|
3199
|
+
if (args.branch_pr) updates.branchPr = String(args.branch_pr);
|
|
3200
|
+
if (args.notes) updates.notes = String(args.notes);
|
|
3201
|
+
if (args.sync) updates.sync = String(args.sync);
|
|
3202
|
+
try {
|
|
3203
|
+
updateTaskInFile(mdPath, taskId, updates);
|
|
3204
|
+
return {
|
|
3205
|
+
content: [{ type: "text", text: `Updated ${taskId}` }]
|
|
3206
|
+
};
|
|
3207
|
+
} catch (err) {
|
|
3208
|
+
return {
|
|
3209
|
+
content: [{ type: "text", text: err.message }],
|
|
3210
|
+
isError: true
|
|
3211
|
+
};
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
handleCreateMasterTask(args) {
|
|
3215
|
+
const mdPath = this.resolveMasterTasksPath();
|
|
3216
|
+
if (!mdPath) {
|
|
3217
|
+
return {
|
|
3218
|
+
content: [
|
|
3219
|
+
{
|
|
3220
|
+
type: "text",
|
|
3221
|
+
text: 'No master-tasks.md found. Run "stackmemory tasks init" to create one.'
|
|
3222
|
+
}
|
|
3223
|
+
]
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
const task = String(args.task || "");
|
|
3227
|
+
if (!task) {
|
|
3228
|
+
return {
|
|
3229
|
+
content: [{ type: "text", text: "task description is required." }],
|
|
3230
|
+
isError: true
|
|
3231
|
+
};
|
|
3232
|
+
}
|
|
3233
|
+
const id = addTaskToFile(mdPath, {
|
|
3234
|
+
priority: String(args.priority || "P1"),
|
|
3235
|
+
status: "todo",
|
|
3236
|
+
owner: String(args.owner || "@me"),
|
|
3237
|
+
sync: String(args.sync || "local"),
|
|
3238
|
+
task,
|
|
3239
|
+
branchPr: "",
|
|
3240
|
+
notes: String(args.notes || "")
|
|
3241
|
+
});
|
|
3242
|
+
return {
|
|
3243
|
+
content: [{ type: "text", text: `Created ${id}: ${task}` }]
|
|
3244
|
+
};
|
|
3344
3245
|
}
|
|
3345
3246
|
}
|
|
3346
3247
|
var server_default = LocalStackMemoryMCP;
|