@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
|
@@ -2,10 +2,10 @@ import { fileURLToPath as __fileURLToPath } from 'url';
|
|
|
2
2
|
import { dirname as __pathDirname } from 'path';
|
|
3
3
|
const __filename = __fileURLToPath(import.meta.url);
|
|
4
4
|
const __dirname = __pathDirname(__filename);
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { ContentCache } from "./content-cache.js";
|
|
6
|
+
import { estimateTokens, hashContent } from "./token-estimator.js";
|
|
7
7
|
export {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
ContentCache,
|
|
9
|
+
estimateTokens,
|
|
10
|
+
hashContent
|
|
11
11
|
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { createHash } from "crypto";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
let encoder = null;
|
|
8
|
+
let initAttempted = false;
|
|
9
|
+
function getEncoder() {
|
|
10
|
+
if (initAttempted) return encoder;
|
|
11
|
+
initAttempted = true;
|
|
12
|
+
try {
|
|
13
|
+
const require2 = createRequire(import.meta.url);
|
|
14
|
+
const tiktoken = require2("js-tiktoken");
|
|
15
|
+
encoder = tiktoken.getEncoding("cl100k_base");
|
|
16
|
+
} catch {
|
|
17
|
+
encoder = null;
|
|
18
|
+
}
|
|
19
|
+
return encoder;
|
|
20
|
+
}
|
|
21
|
+
function estimateTokens(content) {
|
|
22
|
+
if (!content) return 0;
|
|
23
|
+
const enc = getEncoder();
|
|
24
|
+
if (enc) {
|
|
25
|
+
return enc.encode(content).length;
|
|
26
|
+
}
|
|
27
|
+
return Math.ceil(content.length / 4);
|
|
28
|
+
}
|
|
29
|
+
function isTiktokenActive() {
|
|
30
|
+
return getEncoder() !== null;
|
|
31
|
+
}
|
|
32
|
+
function hashContent(content) {
|
|
33
|
+
return createHash("sha256").update(content).digest("hex");
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
estimateTokens,
|
|
37
|
+
hashContent,
|
|
38
|
+
isTiktokenActive
|
|
39
|
+
};
|
|
@@ -15,8 +15,7 @@ function isFeatureEnabled(feature) {
|
|
|
15
15
|
return process.env["STACKMEMORY_AI"] !== "false" && (!!process.env["ANTHROPIC_API_KEY"] || !!process.env["OPENAI_API_KEY"]);
|
|
16
16
|
case "skills":
|
|
17
17
|
return process.env["STACKMEMORY_SKILLS"] === "true" || process.env["STACKMEMORY_SKILLS"] === "1";
|
|
18
|
-
|
|
19
|
-
return process.env["STACKMEMORY_RALPH"] !== "false";
|
|
18
|
+
// For npm package users, must be explicitly enabled
|
|
20
19
|
case "multiProvider":
|
|
21
20
|
return process.env["STACKMEMORY_MULTI_PROVIDER"] === "true" || process.env["STACKMEMORY_MULTI_PROVIDER"] === "1";
|
|
22
21
|
default:
|
|
@@ -29,7 +28,6 @@ function getFeatureFlags() {
|
|
|
29
28
|
linear: isFeatureEnabled("linear"),
|
|
30
29
|
aiSummaries: isFeatureEnabled("aiSummaries"),
|
|
31
30
|
skills: isFeatureEnabled("skills"),
|
|
32
|
-
ralph: isFeatureEnabled("ralph"),
|
|
33
31
|
multiProvider: isFeatureEnabled("multiProvider")
|
|
34
32
|
};
|
|
35
33
|
}
|
|
@@ -49,9 +47,7 @@ function logFeatureStatus() {
|
|
|
49
47
|
console.log(
|
|
50
48
|
` Skills: ${flags.skills ? "enabled" : "disabled (set STACKMEMORY_SKILLS=true)"}`
|
|
51
49
|
);
|
|
52
|
-
console.log(
|
|
53
|
-
` Ralph: ${flags.ralph ? "enabled" : "disabled (set STACKMEMORY_RALPH=true)"}`
|
|
54
|
-
);
|
|
50
|
+
console.log();
|
|
55
51
|
console.log(
|
|
56
52
|
` MultiProvider: ${flags.multiProvider ? "enabled" : "disabled (set STACKMEMORY_MULTI_PROVIDER=true)"}`
|
|
57
53
|
);
|
|
@@ -97,6 +97,26 @@ class FrameDatabase {
|
|
|
97
97
|
);
|
|
98
98
|
} catch {
|
|
99
99
|
}
|
|
100
|
+
const provenanceCols = [
|
|
101
|
+
{ table: "frames", col: "prov_source", def: "TEXT DEFAULT ''" },
|
|
102
|
+
{ table: "frames", col: "prov_derivation", def: "TEXT DEFAULT '[]'" },
|
|
103
|
+
{ table: "frames", col: "prov_confidence", def: "REAL DEFAULT 1.0" },
|
|
104
|
+
{ table: "frames", col: "prov_superseded_by", def: "TEXT" },
|
|
105
|
+
{
|
|
106
|
+
table: "frames",
|
|
107
|
+
col: "prov_program_version",
|
|
108
|
+
def: "TEXT DEFAULT ''"
|
|
109
|
+
},
|
|
110
|
+
{ table: "anchors", col: "prov_source", def: "TEXT DEFAULT ''" },
|
|
111
|
+
{ table: "anchors", col: "prov_confidence", def: "REAL DEFAULT 1.0" },
|
|
112
|
+
{ table: "anchors", col: "prov_superseded_by", def: "TEXT" }
|
|
113
|
+
];
|
|
114
|
+
for (const { table, col, def } of provenanceCols) {
|
|
115
|
+
try {
|
|
116
|
+
this.db.exec(`ALTER TABLE ${table} ADD COLUMN ${col} ${def}`);
|
|
117
|
+
} catch {
|
|
118
|
+
}
|
|
119
|
+
}
|
|
100
120
|
this.db.exec(`
|
|
101
121
|
CREATE INDEX IF NOT EXISTS idx_frames_run ON frames(run_id);
|
|
102
122
|
CREATE INDEX IF NOT EXISTS idx_frames_project ON frames(project_id);
|
|
@@ -189,28 +209,48 @@ class FrameDatabase {
|
|
|
189
209
|
ON entity_states(entity_name, valid_from DESC);
|
|
190
210
|
`);
|
|
191
211
|
this.db.exec(`
|
|
192
|
-
CREATE TABLE IF NOT EXISTS
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
CHECK (context_mode IN ('spawn','fork','ask')),
|
|
204
|
-
blocked_by TEXT NOT NULL DEFAULT '[]',
|
|
205
|
-
depth INTEGER NOT NULL DEFAULT 0,
|
|
206
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
207
|
-
completed_at INTEGER,
|
|
208
|
-
FOREIGN KEY (parent_id) REFERENCES cord_tasks(task_id)
|
|
212
|
+
CREATE TABLE IF NOT EXISTS cloud_sync_state (
|
|
213
|
+
table_name TEXT NOT NULL,
|
|
214
|
+
row_id TEXT NOT NULL,
|
|
215
|
+
last_pushed_at INTEGER,
|
|
216
|
+
last_pushed_version INTEGER,
|
|
217
|
+
last_pulled_at INTEGER,
|
|
218
|
+
last_pulled_version INTEGER,
|
|
219
|
+
sync_status TEXT NOT NULL DEFAULT 'pending',
|
|
220
|
+
push_error TEXT,
|
|
221
|
+
push_attempts INTEGER DEFAULT 0,
|
|
222
|
+
PRIMARY KEY (table_name, row_id)
|
|
209
223
|
);
|
|
210
|
-
CREATE INDEX IF NOT EXISTS
|
|
211
|
-
CREATE INDEX IF NOT EXISTS
|
|
212
|
-
|
|
213
|
-
CREATE
|
|
224
|
+
CREATE INDEX IF NOT EXISTS idx_cloud_sync_status ON cloud_sync_state(sync_status);
|
|
225
|
+
CREATE INDEX IF NOT EXISTS idx_cloud_sync_pushed ON cloud_sync_state(last_pushed_at);
|
|
226
|
+
|
|
227
|
+
CREATE TABLE IF NOT EXISTS cloud_sync_cursors (
|
|
228
|
+
direction TEXT PRIMARY KEY,
|
|
229
|
+
cursor_value TEXT NOT NULL,
|
|
230
|
+
updated_at INTEGER NOT NULL
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
-- Learned behavioral patterns (observe \u2192 learn \u2192 apply)
|
|
234
|
+
CREATE TABLE IF NOT EXISTS patterns (
|
|
235
|
+
id TEXT PRIMARY KEY,
|
|
236
|
+
domain TEXT NOT NULL,
|
|
237
|
+
trigger TEXT NOT NULL,
|
|
238
|
+
action TEXT NOT NULL,
|
|
239
|
+
evidence TEXT DEFAULT '[]',
|
|
240
|
+
confidence REAL DEFAULT 0.3,
|
|
241
|
+
observation_count INTEGER DEFAULT 0,
|
|
242
|
+
scope TEXT DEFAULT 'project',
|
|
243
|
+
project_id TEXT,
|
|
244
|
+
status TEXT DEFAULT 'pending',
|
|
245
|
+
source TEXT DEFAULT 'observed',
|
|
246
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
247
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
248
|
+
last_matched_at INTEGER,
|
|
249
|
+
superseded_by TEXT
|
|
250
|
+
);
|
|
251
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_project ON patterns(project_id, status);
|
|
252
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_domain ON patterns(domain, confidence DESC);
|
|
253
|
+
CREATE INDEX IF NOT EXISTS idx_patterns_status ON patterns(status, confidence DESC);
|
|
214
254
|
`);
|
|
215
255
|
logger.info("Frame database schema initialized");
|
|
216
256
|
} catch (error) {
|
|
@@ -228,8 +268,8 @@ class FrameDatabase {
|
|
|
228
268
|
insertFrame(frame) {
|
|
229
269
|
try {
|
|
230
270
|
const stmt = this.db.prepare(`
|
|
231
|
-
INSERT INTO frames (frame_id, run_id, project_id, parent_frame_id, depth, type, name, state, inputs, outputs, digest_json)
|
|
232
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
271
|
+
INSERT INTO frames (frame_id, run_id, project_id, parent_frame_id, depth, type, name, state, inputs, outputs, digest_json, prov_source, prov_derivation, prov_confidence, prov_program_version)
|
|
272
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
233
273
|
`);
|
|
234
274
|
const result = stmt.run(
|
|
235
275
|
frame.frame_id,
|
|
@@ -242,7 +282,15 @@ class FrameDatabase {
|
|
|
242
282
|
frame.state,
|
|
243
283
|
JSON.stringify(frame.inputs),
|
|
244
284
|
JSON.stringify(frame.outputs),
|
|
245
|
-
JSON.stringify(frame.digest_json)
|
|
285
|
+
JSON.stringify(frame.digest_json),
|
|
286
|
+
frame.type,
|
|
287
|
+
// prov_source: frame type as source
|
|
288
|
+
JSON.stringify([`frame:${frame.type}`]),
|
|
289
|
+
// prov_derivation
|
|
290
|
+
1,
|
|
291
|
+
// prov_confidence: default full confidence
|
|
292
|
+
process.env["npm_package_version"] || ""
|
|
293
|
+
// prov_program_version
|
|
246
294
|
);
|
|
247
295
|
if (result.changes === 0) {
|
|
248
296
|
throw new DatabaseError(
|
|
@@ -473,8 +521,8 @@ class FrameDatabase {
|
|
|
473
521
|
insertAnchor(anchor) {
|
|
474
522
|
try {
|
|
475
523
|
const stmt = this.db.prepare(`
|
|
476
|
-
INSERT INTO anchors (anchor_id, frame_id, project_id, type, text, priority, metadata)
|
|
477
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
524
|
+
INSERT INTO anchors (anchor_id, frame_id, project_id, type, text, priority, metadata, prov_source, prov_confidence)
|
|
525
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
478
526
|
`);
|
|
479
527
|
const result = stmt.run(
|
|
480
528
|
anchor.anchor_id,
|
|
@@ -483,7 +531,11 @@ class FrameDatabase {
|
|
|
483
531
|
anchor.type,
|
|
484
532
|
anchor.text,
|
|
485
533
|
anchor.priority,
|
|
486
|
-
JSON.stringify(anchor.metadata)
|
|
534
|
+
JSON.stringify(anchor.metadata),
|
|
535
|
+
anchor.type,
|
|
536
|
+
// prov_source: anchor type (DECISION, EVENT, etc.)
|
|
537
|
+
1
|
|
538
|
+
// prov_confidence: default full confidence
|
|
487
539
|
);
|
|
488
540
|
if (result.changes === 0) {
|
|
489
541
|
throw new DatabaseError(
|
|
@@ -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 { logger } from "../monitoring/logger.js";
|
|
6
|
+
import { estimateTokens } from "../cache/token-estimator.js";
|
|
6
7
|
import { ValidationError, ErrorCode } from "../errors/index.js";
|
|
7
8
|
import * as fs from "fs";
|
|
8
9
|
import * as path from "path";
|
|
@@ -381,7 +382,6 @@ class RecursiveContextManager {
|
|
|
381
382
|
fitChunksToTokenBudget(chunks, _maxTokens) {
|
|
382
383
|
const selected = [];
|
|
383
384
|
let totalTokens = 0;
|
|
384
|
-
const estimateTokens = (text) => Math.ceil(text.length / 4);
|
|
385
385
|
for (const chunk of chunks) {
|
|
386
386
|
const chunkTokens = estimateTokens(chunk.content);
|
|
387
387
|
if (totalTokens + chunkTokens <= maxTokens) {
|
|
@@ -5,6 +5,7 @@ const __dirname = __pathDirname(__filename);
|
|
|
5
5
|
import * as fs from "fs/promises";
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import { logger } from "../monitoring/logger.js";
|
|
8
|
+
import { estimateTokens } from "../cache/token-estimator.js";
|
|
8
9
|
import {
|
|
9
10
|
getModelTokenLimit
|
|
10
11
|
} from "../models/model-router.js";
|
|
@@ -44,7 +45,7 @@ class CompactionHandler {
|
|
|
44
45
|
* Track token usage from a message
|
|
45
46
|
*/
|
|
46
47
|
trackTokens(content) {
|
|
47
|
-
const estimatedTokens =
|
|
48
|
+
const estimatedTokens = estimateTokens(content);
|
|
48
49
|
this.tokenAccumulator += estimatedTokens;
|
|
49
50
|
this.metrics.estimatedTokens += estimatedTokens;
|
|
50
51
|
if (this.isApproachingCompaction()) {
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import Database from "better-sqlite3";
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readdirSync
|
|
12
|
+
} from "fs";
|
|
13
|
+
import { join } from "path";
|
|
14
|
+
import { homedir } from "os";
|
|
15
|
+
import { logger } from "../monitoring/logger.js";
|
|
16
|
+
function sanitizeFtsQuery(query) {
|
|
17
|
+
const wantsPrefix = query.trimEnd().endsWith("*");
|
|
18
|
+
const cleaned = query.replace(/['"(){}[\]^~*\\,]/g, " ").replace(/\b(AND|OR|NOT|NEAR)\b/gi, "").trim();
|
|
19
|
+
const terms = cleaned.split(/\s+/).filter((t) => t.length > 0);
|
|
20
|
+
if (terms.length === 0) return '""';
|
|
21
|
+
const quoted = terms.map((t) => `"${t}"`);
|
|
22
|
+
if (wantsPrefix) {
|
|
23
|
+
quoted[quoted.length - 1] = quoted[quoted.length - 1] + "*";
|
|
24
|
+
}
|
|
25
|
+
return quoted.join(" ");
|
|
26
|
+
}
|
|
27
|
+
class CrossProjectSearch {
|
|
28
|
+
registryPath;
|
|
29
|
+
constructor(registryDir) {
|
|
30
|
+
const dir = registryDir || join(homedir(), ".stackmemory");
|
|
31
|
+
if (!existsSync(dir)) {
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
this.registryPath = join(dir, "projects.json");
|
|
35
|
+
}
|
|
36
|
+
// --- Project Registry CRUD ---
|
|
37
|
+
loadRegistry() {
|
|
38
|
+
if (!existsSync(this.registryPath)) {
|
|
39
|
+
return { projects: [] };
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const raw = readFileSync(this.registryPath, "utf-8");
|
|
43
|
+
return JSON.parse(raw);
|
|
44
|
+
} catch {
|
|
45
|
+
logger.warn("Failed to parse projects.json, returning empty registry");
|
|
46
|
+
return { projects: [] };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
saveRegistry(registry) {
|
|
50
|
+
writeFileSync(this.registryPath, JSON.stringify(registry, null, 2));
|
|
51
|
+
}
|
|
52
|
+
registerProject(entry) {
|
|
53
|
+
const registry = this.loadRegistry();
|
|
54
|
+
const idx = registry.projects.findIndex(
|
|
55
|
+
(p) => p.path === entry.path || p.dbPath === entry.dbPath
|
|
56
|
+
);
|
|
57
|
+
if (idx >= 0) {
|
|
58
|
+
registry.projects[idx] = entry;
|
|
59
|
+
} else {
|
|
60
|
+
registry.projects.push(entry);
|
|
61
|
+
}
|
|
62
|
+
this.saveRegistry(registry);
|
|
63
|
+
}
|
|
64
|
+
unregisterProject(pathOrName) {
|
|
65
|
+
const registry = this.loadRegistry();
|
|
66
|
+
const before = registry.projects.length;
|
|
67
|
+
registry.projects = registry.projects.filter(
|
|
68
|
+
(p) => p.path !== pathOrName && p.name !== pathOrName
|
|
69
|
+
);
|
|
70
|
+
if (registry.projects.length < before) {
|
|
71
|
+
this.saveRegistry(registry);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
listProjects() {
|
|
77
|
+
return this.loadRegistry().projects;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Auto-discover projects by scanning common directories for .stackmemory/context.db
|
|
81
|
+
*/
|
|
82
|
+
discoverProjects(basePaths) {
|
|
83
|
+
const paths = basePaths || [
|
|
84
|
+
join(homedir(), "Dev"),
|
|
85
|
+
join(homedir(), "dev"),
|
|
86
|
+
join(homedir(), "Projects"),
|
|
87
|
+
join(homedir(), "projects"),
|
|
88
|
+
join(homedir(), "Work"),
|
|
89
|
+
join(homedir(), "work"),
|
|
90
|
+
join(homedir(), "code"),
|
|
91
|
+
join(homedir(), "Code")
|
|
92
|
+
];
|
|
93
|
+
const homeDb = join(homedir(), ".stackmemory", "context.db");
|
|
94
|
+
const discovered = [];
|
|
95
|
+
if (existsSync(homeDb)) {
|
|
96
|
+
discovered.push({
|
|
97
|
+
name: "global",
|
|
98
|
+
path: homedir(),
|
|
99
|
+
dbPath: homeDb,
|
|
100
|
+
lastAccessed: Date.now()
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
for (const basePath of paths) {
|
|
104
|
+
if (!existsSync(basePath)) continue;
|
|
105
|
+
try {
|
|
106
|
+
this.scanForDatabases(basePath, 0, 3, discovered);
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const registry = this.loadRegistry();
|
|
111
|
+
for (const entry of discovered) {
|
|
112
|
+
const existing = registry.projects.find((p) => p.dbPath === entry.dbPath);
|
|
113
|
+
if (!existing) {
|
|
114
|
+
registry.projects.push(entry);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this.saveRegistry(registry);
|
|
118
|
+
return discovered;
|
|
119
|
+
}
|
|
120
|
+
scanForDatabases(dir, depth, maxDepth, results) {
|
|
121
|
+
if (depth > maxDepth) return;
|
|
122
|
+
const dbPath = join(dir, ".stackmemory", "context.db");
|
|
123
|
+
if (existsSync(dbPath)) {
|
|
124
|
+
const name = dir.split("/").pop() || dir;
|
|
125
|
+
results.push({
|
|
126
|
+
name,
|
|
127
|
+
path: dir,
|
|
128
|
+
dbPath,
|
|
129
|
+
lastAccessed: Date.now()
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
135
|
+
for (const entry of entries) {
|
|
136
|
+
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "build") {
|
|
137
|
+
this.scanForDatabases(
|
|
138
|
+
join(dir, entry.name),
|
|
139
|
+
depth + 1,
|
|
140
|
+
maxDepth,
|
|
141
|
+
results
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// --- Cross-Project Search ---
|
|
149
|
+
/**
|
|
150
|
+
* Search across all registered project databases using FTS5/BM25.
|
|
151
|
+
* Opens read-only connections. Skips missing/locked databases gracefully.
|
|
152
|
+
*/
|
|
153
|
+
async search(options) {
|
|
154
|
+
const { query, limit = 20, excludeProject } = options;
|
|
155
|
+
const registry = this.loadRegistry();
|
|
156
|
+
if (registry.projects.length === 0) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const allResults = [];
|
|
160
|
+
const perDbLimit = Math.max(limit, 10);
|
|
161
|
+
for (const project of registry.projects) {
|
|
162
|
+
if (excludeProject && project.name === excludeProject) continue;
|
|
163
|
+
if (!existsSync(project.dbPath)) {
|
|
164
|
+
logger.debug(`Skipping missing database: ${project.dbPath}`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
const results = this.searchSingleDb(project, query, perDbLimit);
|
|
169
|
+
allResults.push(...results);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
logger.debug(
|
|
172
|
+
`Skipping database ${project.dbPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
allResults.sort((a, b) => b.score - a.score);
|
|
177
|
+
return allResults.slice(0, limit);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Search a single project database (read-only connection).
|
|
181
|
+
*/
|
|
182
|
+
searchSingleDb(project, query, limit) {
|
|
183
|
+
let db = null;
|
|
184
|
+
try {
|
|
185
|
+
db = new Database(project.dbPath, {
|
|
186
|
+
readonly: true,
|
|
187
|
+
fileMustExist: true
|
|
188
|
+
});
|
|
189
|
+
const hasFts = db.prepare(
|
|
190
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name='frames_fts'"
|
|
191
|
+
).get();
|
|
192
|
+
if (hasFts) {
|
|
193
|
+
return this.searchFts(db, project, query, limit);
|
|
194
|
+
} else {
|
|
195
|
+
return this.searchLike(db, project, query, limit);
|
|
196
|
+
}
|
|
197
|
+
} finally {
|
|
198
|
+
if (db) {
|
|
199
|
+
try {
|
|
200
|
+
db.close();
|
|
201
|
+
} catch {
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
searchFts(db, project, query, limit) {
|
|
207
|
+
const sanitized = sanitizeFtsQuery(query);
|
|
208
|
+
const sql = `
|
|
209
|
+
SELECT f.frame_id, f.name, f.type, f.state, f.digest_text, f.created_at,
|
|
210
|
+
-bm25(frames_fts, 10.0, 5.0, 2.0, 1.0) as score
|
|
211
|
+
FROM frames_fts fts
|
|
212
|
+
JOIN frames f ON f.rowid = fts.rowid
|
|
213
|
+
WHERE frames_fts MATCH ?
|
|
214
|
+
ORDER BY score DESC
|
|
215
|
+
LIMIT ?
|
|
216
|
+
`;
|
|
217
|
+
const rows = db.prepare(sql).all(sanitized, limit);
|
|
218
|
+
return rows.map((row) => ({
|
|
219
|
+
projectName: project.name,
|
|
220
|
+
projectPath: project.path,
|
|
221
|
+
frameId: row.frame_id,
|
|
222
|
+
name: row.name,
|
|
223
|
+
type: row.type,
|
|
224
|
+
state: row.state,
|
|
225
|
+
digestText: row.digest_text,
|
|
226
|
+
score: row.score,
|
|
227
|
+
createdAt: row.created_at
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
230
|
+
searchLike(db, project, query, limit) {
|
|
231
|
+
const likeParam = `%${query}%`;
|
|
232
|
+
const sql = `
|
|
233
|
+
SELECT frame_id, name, type, state, digest_text, created_at,
|
|
234
|
+
CASE
|
|
235
|
+
WHEN name LIKE ? THEN 1.0
|
|
236
|
+
WHEN digest_text LIKE ? THEN 0.8
|
|
237
|
+
WHEN inputs LIKE ? THEN 0.6
|
|
238
|
+
ELSE 0.5
|
|
239
|
+
END as score
|
|
240
|
+
FROM frames
|
|
241
|
+
WHERE (name LIKE ? OR digest_text LIKE ? OR inputs LIKE ?)
|
|
242
|
+
ORDER BY score DESC
|
|
243
|
+
LIMIT ?
|
|
244
|
+
`;
|
|
245
|
+
const rows = db.prepare(sql).all(
|
|
246
|
+
likeParam,
|
|
247
|
+
likeParam,
|
|
248
|
+
likeParam,
|
|
249
|
+
likeParam,
|
|
250
|
+
likeParam,
|
|
251
|
+
likeParam,
|
|
252
|
+
limit
|
|
253
|
+
);
|
|
254
|
+
return rows.map((row) => ({
|
|
255
|
+
projectName: project.name,
|
|
256
|
+
projectPath: project.path,
|
|
257
|
+
frameId: row.frame_id,
|
|
258
|
+
name: row.name,
|
|
259
|
+
type: row.type,
|
|
260
|
+
state: row.state,
|
|
261
|
+
digestText: row.digest_text,
|
|
262
|
+
score: row.score,
|
|
263
|
+
createdAt: row.created_at
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export {
|
|
268
|
+
CrossProjectSearch
|
|
269
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import {
|
|
6
|
+
CrossProjectSearch
|
|
7
|
+
} from "./cross-project-search.js";
|
|
8
|
+
export {
|
|
9
|
+
CrossProjectSearch
|
|
10
|
+
};
|
|
@@ -44,7 +44,20 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
|
|
|
44
44
|
const config = this.config;
|
|
45
45
|
const dir = path.dirname(this.dbPath);
|
|
46
46
|
await fs.mkdir(dir, { recursive: true });
|
|
47
|
-
|
|
47
|
+
try {
|
|
48
|
+
this.db = new Database(this.dbPath);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51
|
+
if (msg.includes("NODE_MODULE_VERSION") || msg.includes("was compiled against a different Node.js version")) {
|
|
52
|
+
const nodeVersion = process.version;
|
|
53
|
+
throw new Error(
|
|
54
|
+
`better-sqlite3 was compiled for a different Node.js version than the one currently running (${nodeVersion}).
|
|
55
|
+
Fix: cd ${process.cwd()} && npm rebuild better-sqlite3
|
|
56
|
+
If that fails: npm install`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
48
61
|
this.db.pragma("foreign_keys = ON");
|
|
49
62
|
if (config.walMode !== false) {
|
|
50
63
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -1530,89 +1543,6 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
|
|
|
1530
1543
|
}
|
|
1531
1544
|
return Buffer.from(JSON.stringify(data, null, 2));
|
|
1532
1545
|
}
|
|
1533
|
-
/**
|
|
1534
|
-
* Get recent frames from other run_ids within the same project.
|
|
1535
|
-
* Used by team tools to read cross-agent context.
|
|
1536
|
-
*/
|
|
1537
|
-
async getRecentFramesExcludingRun(projectId, excludeRunId, opts) {
|
|
1538
|
-
if (!this.db)
|
|
1539
|
-
throw new DatabaseError(
|
|
1540
|
-
"Database not connected",
|
|
1541
|
-
ErrorCode.DB_CONNECTION_FAILED
|
|
1542
|
-
);
|
|
1543
|
-
const limit = opts?.limit ?? 10;
|
|
1544
|
-
const params = [projectId, excludeRunId];
|
|
1545
|
-
let whereExtra = "";
|
|
1546
|
-
if (opts?.since) {
|
|
1547
|
-
whereExtra += " AND created_at > ?";
|
|
1548
|
-
params.push(Math.floor(opts.since / 1e3));
|
|
1549
|
-
}
|
|
1550
|
-
if (opts?.types && opts.types.length > 0) {
|
|
1551
|
-
const placeholders = opts.types.map(() => "?").join(",");
|
|
1552
|
-
whereExtra += ` AND type IN (${placeholders})`;
|
|
1553
|
-
params.push(...opts.types);
|
|
1554
|
-
}
|
|
1555
|
-
params.push(limit);
|
|
1556
|
-
const rows = this.db.prepare(
|
|
1557
|
-
`SELECT * FROM frames
|
|
1558
|
-
WHERE project_id = ? AND run_id != ?${whereExtra}
|
|
1559
|
-
ORDER BY created_at DESC
|
|
1560
|
-
LIMIT ?`
|
|
1561
|
-
).all(...params);
|
|
1562
|
-
const frameIds = rows.map((r) => r.frame_id);
|
|
1563
|
-
const anchorsByFrame = /* @__PURE__ */ new Map();
|
|
1564
|
-
if (frameIds.length > 0) {
|
|
1565
|
-
const placeholders = frameIds.map(() => "?").join(",");
|
|
1566
|
-
const anchorRows = this.db.prepare(
|
|
1567
|
-
`SELECT * FROM anchors WHERE frame_id IN (${placeholders})
|
|
1568
|
-
ORDER BY priority DESC, created_at ASC`
|
|
1569
|
-
).all(...frameIds);
|
|
1570
|
-
for (const row of anchorRows) {
|
|
1571
|
-
const list = anchorsByFrame.get(row.frame_id) || [];
|
|
1572
|
-
list.push({ ...row, metadata: JSON.parse(row.metadata || "{}") });
|
|
1573
|
-
anchorsByFrame.set(row.frame_id, list);
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
return rows.map((row) => ({
|
|
1577
|
-
...row,
|
|
1578
|
-
inputs: JSON.parse(row.inputs || "{}"),
|
|
1579
|
-
outputs: JSON.parse(row.outputs || "{}"),
|
|
1580
|
-
digest_json: JSON.parse(row.digest_json || "{}"),
|
|
1581
|
-
anchors: anchorsByFrame.get(row.frame_id) || []
|
|
1582
|
-
}));
|
|
1583
|
-
}
|
|
1584
|
-
/**
|
|
1585
|
-
* Get anchors explicitly shared for team visibility.
|
|
1586
|
-
* Finds anchors where metadata contains `"shared":true`.
|
|
1587
|
-
*/
|
|
1588
|
-
async getSharedAnchors(projectId, opts) {
|
|
1589
|
-
if (!this.db)
|
|
1590
|
-
throw new DatabaseError(
|
|
1591
|
-
"Database not connected",
|
|
1592
|
-
ErrorCode.DB_CONNECTION_FAILED
|
|
1593
|
-
);
|
|
1594
|
-
const limit = opts?.limit ?? 20;
|
|
1595
|
-
const params = [projectId];
|
|
1596
|
-
let whereExtra = "";
|
|
1597
|
-
if (opts?.since) {
|
|
1598
|
-
whereExtra += " AND a.created_at > ?";
|
|
1599
|
-
params.push(Math.floor(opts.since / 1e3));
|
|
1600
|
-
}
|
|
1601
|
-
params.push(limit);
|
|
1602
|
-
const rows = this.db.prepare(
|
|
1603
|
-
`SELECT a.*, f.name as frame_name, f.run_id
|
|
1604
|
-
FROM anchors a
|
|
1605
|
-
JOIN frames f ON a.frame_id = f.frame_id
|
|
1606
|
-
WHERE f.project_id = ?
|
|
1607
|
-
AND a.metadata LIKE '%"shared":true%'${whereExtra}
|
|
1608
|
-
ORDER BY a.priority DESC, a.created_at DESC
|
|
1609
|
-
LIMIT ?`
|
|
1610
|
-
).all(...params);
|
|
1611
|
-
return rows.map((row) => ({
|
|
1612
|
-
...row,
|
|
1613
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
1614
|
-
}));
|
|
1615
|
-
}
|
|
1616
1546
|
async importData(data, format, options) {
|
|
1617
1547
|
if (!this.db)
|
|
1618
1548
|
throw new DatabaseError(
|
|
@@ -387,6 +387,11 @@ function createProvider(id, config) {
|
|
|
387
387
|
apiKey: config.apiKey,
|
|
388
388
|
baseUrl: config.baseUrl || "https://openrouter.ai/api"
|
|
389
389
|
});
|
|
390
|
+
case "moonshot":
|
|
391
|
+
return new GPTAdapter({
|
|
392
|
+
apiKey: config.apiKey,
|
|
393
|
+
baseUrl: config.baseUrl || "https://api.moonshot.ai/v1"
|
|
394
|
+
});
|
|
390
395
|
default:
|
|
391
396
|
throw new Error(`No adapter for provider: ${id}`);
|
|
392
397
|
}
|