@stackmemoryai/stackmemory 1.10.5 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -23
- package/dist/src/cli/claude-sm.js +266 -84
- package/dist/src/cli/codex-sm.js +185 -33
- package/dist/src/cli/commands/bench.js +209 -2
- package/dist/src/cli/commands/cache.js +126 -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/optimize.js +117 -0
- package/dist/src/cli/commands/orchestrate.js +230 -5
- package/dist/src/cli/commands/orchestrator.js +312 -24
- package/dist/src/cli/commands/pack.js +322 -0
- package/dist/src/cli/commands/search.js +40 -1
- package/dist/src/cli/commands/setup.js +177 -7
- package/dist/src/cli/commands/skills.js +10 -1
- package/dist/src/cli/commands/state.js +265 -0
- package/dist/src/cli/gemini-sm.js +19 -29
- package/dist/src/cli/index.js +90 -29
- 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 +44 -0
- package/dist/src/core/cache/content-cache.js +238 -0
- package/dist/src/core/cache/index.js +11 -0
- package/dist/src/core/cache/token-estimator.js +16 -0
- package/dist/src/core/context/frame-database.js +38 -30
- package/dist/src/core/cross-search/cross-project-search.js +269 -0
- package/dist/src/core/{merge → cross-search}/index.js +6 -4
- package/dist/src/core/database/sqlite-adapter.js +0 -83
- package/dist/src/core/extensions/provider-adapter.js +5 -0
- package/dist/src/core/models/model-router.js +22 -2
- package/dist/src/core/monitoring/logger.js +2 -1
- package/dist/src/core/optimization/trace-optimizer.js +413 -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/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 +66 -0
- package/dist/src/core/trace/trace-event-store.js +282 -0
- package/dist/src/core/trace/trace-event.js +4 -0
- package/dist/src/daemon/daemon-config.js +7 -0
- package/dist/src/daemon/services/github-service.js +126 -0
- package/dist/src/daemon/unified-daemon.js +30 -0
- package/dist/src/features/sweep/pty-wrapper.js +13 -5
- package/dist/src/hooks/schemas.js +2 -0
- package/dist/src/integrations/claude-code/subagent-client.js +89 -0
- package/dist/src/integrations/github/pr-state.js +158 -0
- package/dist/src/integrations/linear/client.js +4 -1
- package/dist/src/integrations/mcp/handlers/cross-search-handlers.js +188 -0
- package/dist/src/integrations/mcp/handlers/index.js +40 -59
- package/dist/src/integrations/mcp/server.js +425 -311
- package/dist/src/integrations/mcp/tool-alias-registry.js +370 -0
- package/dist/src/integrations/mcp/tool-definitions.js +98 -229
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +3 -40
- package/dist/src/integrations/ralph/learning/pattern-learner.js +1 -20
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -2
- package/dist/src/mcp/stackmemory-mcp-server.js +315 -0
- package/dist/src/orchestrators/multimodal/determinism.js +243 -0
- package/dist/src/orchestrators/multimodal/harness.js +147 -77
- package/dist/src/orchestrators/multimodal/providers.js +44 -3
- package/dist/src/utils/hook-installer.js +0 -8
- package/package.json +10 -1
- 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/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/integrations/mcp/handlers/cord-handlers.js +0 -397
- package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
- /package/dist/src/core/{merge → cache}/types.js +0 -0
|
@@ -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
|
+
};
|
|
@@ -2,7 +2,9 @@ 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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export
|
|
5
|
+
import {
|
|
6
|
+
CrossProjectSearch
|
|
7
|
+
} from "./cross-project-search.js";
|
|
8
|
+
export {
|
|
9
|
+
CrossProjectSearch
|
|
10
|
+
};
|
|
@@ -1530,89 +1530,6 @@ class SQLiteAdapter extends FeatureAwareDatabaseAdapter {
|
|
|
1530
1530
|
}
|
|
1531
1531
|
return Buffer.from(JSON.stringify(data, null, 2));
|
|
1532
1532
|
}
|
|
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
1533
|
async importData(data, format, options) {
|
|
1617
1534
|
if (!this.db)
|
|
1618
1535
|
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
|
}
|
|
@@ -35,7 +35,10 @@ const MODEL_TOKEN_LIMITS = {
|
|
|
35
35
|
// Cerebras
|
|
36
36
|
"llama-4-scout-17b-16e-instruct": 131072,
|
|
37
37
|
// DeepInfra
|
|
38
|
-
"THUDM/glm-4-9b-chat": 128e3
|
|
38
|
+
"THUDM/glm-4-9b-chat": 128e3,
|
|
39
|
+
// Moonshot (Kimi)
|
|
40
|
+
"kimi-k2.6": 256e3,
|
|
41
|
+
"kimi-k2.5": 256e3
|
|
39
42
|
};
|
|
40
43
|
const DEFAULT_MODEL_TOKEN_LIMIT = 2e5;
|
|
41
44
|
function getModelTokenLimit(model) {
|
|
@@ -91,6 +94,12 @@ const DEFAULT_CONFIG = {
|
|
|
91
94
|
baseUrl: "https://openrouter.ai/api",
|
|
92
95
|
apiKeyEnv: "OPENROUTER_API_KEY"
|
|
93
96
|
},
|
|
97
|
+
moonshot: {
|
|
98
|
+
provider: "moonshot",
|
|
99
|
+
model: "kimi-k2.6",
|
|
100
|
+
baseUrl: "https://api.moonshot.ai/v1",
|
|
101
|
+
apiKeyEnv: "MOONSHOT_API_KEY"
|
|
102
|
+
},
|
|
94
103
|
"anthropic-batch": {
|
|
95
104
|
provider: "anthropic-batch",
|
|
96
105
|
model: "claude-sonnet-4-5-20250929",
|
|
@@ -235,8 +244,19 @@ const OPTIMAL_ROUTING = {
|
|
|
235
244
|
apiKeyEnv: "ANTHROPIC_API_KEY"
|
|
236
245
|
}
|
|
237
246
|
};
|
|
238
|
-
const FALLBACK_CHAIN = [
|
|
247
|
+
const FALLBACK_CHAIN = [
|
|
248
|
+
"moonshot",
|
|
249
|
+
"deepinfra",
|
|
250
|
+
"cerebras",
|
|
251
|
+
"anthropic"
|
|
252
|
+
];
|
|
239
253
|
const CHEAP_PROVIDERS = [
|
|
254
|
+
{
|
|
255
|
+
provider: "moonshot",
|
|
256
|
+
model: "kimi-k2.6",
|
|
257
|
+
apiKeyEnv: "MOONSHOT_API_KEY",
|
|
258
|
+
baseUrl: "https://api.moonshot.ai/v1"
|
|
259
|
+
},
|
|
240
260
|
{
|
|
241
261
|
provider: "openrouter",
|
|
242
262
|
model: "meta-llama/llama-4-scout",
|
|
@@ -76,6 +76,7 @@ class Logger {
|
|
|
76
76
|
fileLoggingDisabledNotified = false;
|
|
77
77
|
constructor() {
|
|
78
78
|
const envLevel = process.env["STACKMEMORY_LOG_LEVEL"]?.toUpperCase();
|
|
79
|
+
const jsonCliMode = !envLevel && process.argv.includes("--json");
|
|
79
80
|
switch (envLevel) {
|
|
80
81
|
case "ERROR":
|
|
81
82
|
this.logLevel = 0 /* ERROR */;
|
|
@@ -87,7 +88,7 @@ class Logger {
|
|
|
87
88
|
this.logLevel = 3 /* DEBUG */;
|
|
88
89
|
break;
|
|
89
90
|
default:
|
|
90
|
-
this.logLevel = 2 /* INFO */;
|
|
91
|
+
this.logLevel = jsonCliMode ? 0 /* ERROR */ : 2 /* INFO */;
|
|
91
92
|
}
|
|
92
93
|
if (this.logLevel === 3 /* DEBUG */ || process.env["STACKMEMORY_LOG_FILE"]) {
|
|
93
94
|
this.logFile = process.env["STACKMEMORY_LOG_FILE"] || path.join(
|