claude-memory-layer 1.0.36 → 1.0.37
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/dist/cli/index.js +280 -43
- package/dist/cli/index.js.map +2 -2
- package/dist/core/index.js +68 -1
- package/dist/core/index.js.map +2 -2
- package/dist/hooks/post-tool-use.js +71 -1
- package/dist/hooks/post-tool-use.js.map +2 -2
- package/dist/hooks/semantic-daemon.js +71 -1
- package/dist/hooks/semantic-daemon.js.map +2 -2
- package/dist/hooks/session-end.js +71 -1
- package/dist/hooks/session-end.js.map +2 -2
- package/dist/hooks/session-start.js +71 -1
- package/dist/hooks/session-start.js.map +2 -2
- package/dist/hooks/stop.js +71 -1
- package/dist/hooks/stop.js.map +2 -2
- package/dist/hooks/user-prompt-submit.js +71 -1
- package/dist/hooks/user-prompt-submit.js.map +2 -2
- package/dist/index.js +71 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/index.js +71 -1
- package/dist/mcp/index.js.map +2 -2
- package/dist/server/api/index.js +271 -41
- package/dist/server/api/index.js.map +2 -2
- package/dist/server/index.js +271 -41
- package/dist/server/index.js.map +2 -2
- package/dist/services/memory-service.js +71 -1
- package/dist/services/memory-service.js.map +2 -2
- package/dist/ui/assets/js/chat.js +21 -3
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2329,6 +2329,14 @@ function normalizeQueryRewriteKind(value) {
|
|
|
2329
2329
|
return "none";
|
|
2330
2330
|
}
|
|
2331
2331
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2332
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2333
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
2334
|
+
function emptyOutboxRecoveryResult() {
|
|
2335
|
+
return {
|
|
2336
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
2337
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2338
|
+
};
|
|
2339
|
+
}
|
|
2332
2340
|
var SQLiteEventStore = class {
|
|
2333
2341
|
db;
|
|
2334
2342
|
initialized = false;
|
|
@@ -3079,7 +3087,9 @@ var SQLiteEventStore = class {
|
|
|
3079
3087
|
const placeholders = ids.map(() => "?").join(",");
|
|
3080
3088
|
sqliteRun(
|
|
3081
3089
|
this.db,
|
|
3082
|
-
`UPDATE embedding_outbox
|
|
3090
|
+
`UPDATE embedding_outbox
|
|
3091
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
3092
|
+
WHERE id IN (${placeholders})`,
|
|
3083
3093
|
ids
|
|
3084
3094
|
);
|
|
3085
3095
|
return pending.map((row) => ({
|
|
@@ -3149,6 +3159,58 @@ var SQLiteEventStore = class {
|
|
|
3149
3159
|
[error, ...ids]
|
|
3150
3160
|
);
|
|
3151
3161
|
}
|
|
3162
|
+
/**
|
|
3163
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
3164
|
+
*
|
|
3165
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
3166
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
3167
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
3168
|
+
*/
|
|
3169
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
3170
|
+
await this.initialize();
|
|
3171
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
3172
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
3173
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3174
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
3175
|
+
const result = emptyOutboxRecoveryResult();
|
|
3176
|
+
const embeddingRecovered = sqliteRun(
|
|
3177
|
+
this.db,
|
|
3178
|
+
`UPDATE embedding_outbox
|
|
3179
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
3180
|
+
WHERE status = 'processing'
|
|
3181
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
3182
|
+
[threshold]
|
|
3183
|
+
);
|
|
3184
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
3185
|
+
const embeddingRetried = sqliteRun(
|
|
3186
|
+
this.db,
|
|
3187
|
+
`UPDATE embedding_outbox
|
|
3188
|
+
SET status = 'pending', error_message = NULL
|
|
3189
|
+
WHERE status = 'failed'
|
|
3190
|
+
AND retry_count < ?`,
|
|
3191
|
+
[maxRetries]
|
|
3192
|
+
);
|
|
3193
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
3194
|
+
const vectorRecovered = sqliteRun(
|
|
3195
|
+
this.db,
|
|
3196
|
+
`UPDATE vector_outbox
|
|
3197
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3198
|
+
WHERE status = 'processing'
|
|
3199
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
3200
|
+
[now.toISOString(), threshold]
|
|
3201
|
+
);
|
|
3202
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
3203
|
+
const vectorRetried = sqliteRun(
|
|
3204
|
+
this.db,
|
|
3205
|
+
`UPDATE vector_outbox
|
|
3206
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3207
|
+
WHERE status = 'failed'
|
|
3208
|
+
AND retry_count < ?`,
|
|
3209
|
+
[now.toISOString(), maxRetries]
|
|
3210
|
+
);
|
|
3211
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3212
|
+
return result;
|
|
3213
|
+
}
|
|
3152
3214
|
/**
|
|
3153
3215
|
* Get embedding/vector outbox health statistics
|
|
3154
3216
|
*/
|
|
@@ -4020,6 +4082,7 @@ var VectorStore = class {
|
|
|
4020
4082
|
* Get total count of vectors
|
|
4021
4083
|
*/
|
|
4022
4084
|
async count() {
|
|
4085
|
+
await this.initialize();
|
|
4023
4086
|
if (!this.table)
|
|
4024
4087
|
return 0;
|
|
4025
4088
|
const result = await this.table.countRows();
|
|
@@ -4458,6 +4521,10 @@ var MemoryQueryService = class {
|
|
|
4458
4521
|
await this.initialize();
|
|
4459
4522
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
4460
4523
|
}
|
|
4524
|
+
async recoverStuckOutboxItems(options) {
|
|
4525
|
+
await this.initialize();
|
|
4526
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4527
|
+
}
|
|
4461
4528
|
async getStats() {
|
|
4462
4529
|
await this.initialize();
|
|
4463
4530
|
const deps = this.getStatsDeps();
|
|
@@ -7755,6 +7822,9 @@ var MemoryService = class {
|
|
|
7755
7822
|
async getOutboxStats() {
|
|
7756
7823
|
return this.queryService.getOutboxStats();
|
|
7757
7824
|
}
|
|
7825
|
+
async recoverStuckOutboxItems(options) {
|
|
7826
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
7827
|
+
}
|
|
7758
7828
|
async getRetrievalTraceStats() {
|
|
7759
7829
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7760
7830
|
}
|
|
@@ -10345,6 +10415,26 @@ function getServiceFromQuery(c) {
|
|
|
10345
10415
|
}
|
|
10346
10416
|
return getReadOnlyMemoryService();
|
|
10347
10417
|
}
|
|
10418
|
+
function getWritableServiceFromQuery(c) {
|
|
10419
|
+
const project = c.req.query("project") || c.req.query("projectId");
|
|
10420
|
+
if (project) {
|
|
10421
|
+
const storagePath = resolveProjectStoragePath(project);
|
|
10422
|
+
return new MemoryService({
|
|
10423
|
+
storagePath,
|
|
10424
|
+
readOnly: false,
|
|
10425
|
+
lightweightMode: true,
|
|
10426
|
+
analyticsEnabled: false,
|
|
10427
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
10428
|
+
});
|
|
10429
|
+
}
|
|
10430
|
+
return new MemoryService({
|
|
10431
|
+
storagePath: "~/.claude-code/memory",
|
|
10432
|
+
readOnly: false,
|
|
10433
|
+
lightweightMode: true,
|
|
10434
|
+
analyticsEnabled: false,
|
|
10435
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
10436
|
+
});
|
|
10437
|
+
}
|
|
10348
10438
|
function getLightweightServiceFromQuery(c) {
|
|
10349
10439
|
const project = c.req.query("project") || c.req.query("projectId");
|
|
10350
10440
|
if (project) {
|
|
@@ -12000,6 +12090,13 @@ import { Hono as Hono8 } from "hono";
|
|
|
12000
12090
|
import { streamSSE } from "hono/streaming";
|
|
12001
12091
|
import { spawn } from "child_process";
|
|
12002
12092
|
var chatRouter = new Hono8();
|
|
12093
|
+
var ProviderFailure = class extends Error {
|
|
12094
|
+
constructor(code, message) {
|
|
12095
|
+
super(message);
|
|
12096
|
+
this.code = code;
|
|
12097
|
+
this.name = "ProviderFailure";
|
|
12098
|
+
}
|
|
12099
|
+
};
|
|
12003
12100
|
var CLAUDE_TIMEOUT_MS = 12e4;
|
|
12004
12101
|
chatRouter.post("/", async (c) => {
|
|
12005
12102
|
let body;
|
|
@@ -12011,43 +12108,12 @@ chatRouter.post("/", async (c) => {
|
|
|
12011
12108
|
if (!body.message?.trim()) {
|
|
12012
12109
|
return c.json({ error: "Message is required" }, 400);
|
|
12013
12110
|
}
|
|
12014
|
-
const
|
|
12111
|
+
const memoryOnly = body.mode === "memory-only" || body.memoryOnly === true;
|
|
12112
|
+
const memoryService = memoryOnly ? getLightweightServiceFromQuery(c) : getServiceFromQuery(c);
|
|
12015
12113
|
try {
|
|
12016
12114
|
await memoryService.initialize();
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
try {
|
|
12020
|
-
const result = await memoryService.retrieveMemories(body.message, {
|
|
12021
|
-
topK: 8,
|
|
12022
|
-
minScore: 0.5
|
|
12023
|
-
});
|
|
12024
|
-
if (result.memories.length > 0) {
|
|
12025
|
-
const parts = ["## Relevant Memories\n"];
|
|
12026
|
-
for (const m of result.memories) {
|
|
12027
|
-
const date = new Date(m.event.timestamp).toISOString().split("T")[0];
|
|
12028
|
-
const content = m.event.content.slice(0, 500);
|
|
12029
|
-
parts.push(`### [${m.event.eventType}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
12030
|
-
parts.push(content);
|
|
12031
|
-
if (m.sessionContext) {
|
|
12032
|
-
parts.push(`_Context: ${m.sessionContext}_`);
|
|
12033
|
-
}
|
|
12034
|
-
parts.push("");
|
|
12035
|
-
}
|
|
12036
|
-
memoryContext = parts.join("\n");
|
|
12037
|
-
}
|
|
12038
|
-
} catch {
|
|
12039
|
-
}
|
|
12040
|
-
try {
|
|
12041
|
-
const stats = await memoryService.getStats();
|
|
12042
|
-
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
12043
|
-
statsContext = [
|
|
12044
|
-
"## Memory Stats",
|
|
12045
|
-
`- Total events: ${stats.totalEvents}`,
|
|
12046
|
-
`- Vector nodes: ${stats.vectorCount}`,
|
|
12047
|
-
`- By level: ${levels}`
|
|
12048
|
-
].join("\n");
|
|
12049
|
-
} catch {
|
|
12050
|
-
}
|
|
12115
|
+
const { memoryContext, memoryHits } = await collectMemoryContext(memoryService, body.message);
|
|
12116
|
+
const statsContext = await collectStatsContext(memoryService);
|
|
12051
12117
|
const fullPrompt = buildPrompt(
|
|
12052
12118
|
statsContext,
|
|
12053
12119
|
memoryContext,
|
|
@@ -12055,13 +12121,23 @@ chatRouter.post("/", async (c) => {
|
|
|
12055
12121
|
body.message
|
|
12056
12122
|
);
|
|
12057
12123
|
return streamSSE(c, async (stream) => {
|
|
12124
|
+
if (memoryOnly) {
|
|
12125
|
+
await streamMemoryOnlyResponse(stream, {
|
|
12126
|
+
memoryContext,
|
|
12127
|
+
memoryHits,
|
|
12128
|
+
reason: "memory-only-mode"
|
|
12129
|
+
});
|
|
12130
|
+
return;
|
|
12131
|
+
}
|
|
12058
12132
|
try {
|
|
12059
12133
|
await streamClaudeResponse(fullPrompt, stream);
|
|
12060
12134
|
} catch (err) {
|
|
12135
|
+
const diagnostic = providerDiagnostic(err);
|
|
12061
12136
|
await stream.writeSSE({
|
|
12062
|
-
event: "
|
|
12063
|
-
data: JSON.stringify(
|
|
12137
|
+
event: "provider_error",
|
|
12138
|
+
data: JSON.stringify(diagnostic)
|
|
12064
12139
|
});
|
|
12140
|
+
await streamMemoryOnlyFallback(stream, memoryContext, memoryHits);
|
|
12065
12141
|
}
|
|
12066
12142
|
});
|
|
12067
12143
|
} catch (error) {
|
|
@@ -12070,6 +12146,115 @@ chatRouter.post("/", async (c) => {
|
|
|
12070
12146
|
await memoryService.shutdown();
|
|
12071
12147
|
}
|
|
12072
12148
|
});
|
|
12149
|
+
async function collectMemoryContext(memoryService, query) {
|
|
12150
|
+
let memoryHits = [];
|
|
12151
|
+
try {
|
|
12152
|
+
const result = await memoryService.retrieveMemories?.(query, {
|
|
12153
|
+
topK: 8,
|
|
12154
|
+
minScore: 0.5
|
|
12155
|
+
});
|
|
12156
|
+
memoryHits = result?.memories ?? [];
|
|
12157
|
+
} catch {
|
|
12158
|
+
memoryHits = [];
|
|
12159
|
+
}
|
|
12160
|
+
if (memoryHits.length === 0) {
|
|
12161
|
+
try {
|
|
12162
|
+
memoryHits = await memoryService.keywordSearch?.(query, { topK: 8, minScore: 0.05 }) ?? [];
|
|
12163
|
+
} catch {
|
|
12164
|
+
memoryHits = [];
|
|
12165
|
+
}
|
|
12166
|
+
}
|
|
12167
|
+
return {
|
|
12168
|
+
memoryContext: formatMemoryContext(memoryHits),
|
|
12169
|
+
memoryHits
|
|
12170
|
+
};
|
|
12171
|
+
}
|
|
12172
|
+
async function collectStatsContext(memoryService) {
|
|
12173
|
+
try {
|
|
12174
|
+
const stats = await memoryService.getStats?.();
|
|
12175
|
+
if (!stats)
|
|
12176
|
+
return "";
|
|
12177
|
+
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
12178
|
+
return [
|
|
12179
|
+
"## Memory Stats",
|
|
12180
|
+
`- Total events: ${stats.totalEvents}`,
|
|
12181
|
+
`- Vector nodes: ${stats.vectorCount}`,
|
|
12182
|
+
`- By level: ${levels}`
|
|
12183
|
+
].join("\n");
|
|
12184
|
+
} catch {
|
|
12185
|
+
return "";
|
|
12186
|
+
}
|
|
12187
|
+
}
|
|
12188
|
+
function formatMemoryContext(memoryHits) {
|
|
12189
|
+
if (memoryHits.length === 0)
|
|
12190
|
+
return "";
|
|
12191
|
+
const parts = ["## Relevant Memories\n"];
|
|
12192
|
+
for (const m of memoryHits) {
|
|
12193
|
+
const date = m.event.timestamp ? new Date(m.event.timestamp).toISOString().split("T")[0] : "unknown-date";
|
|
12194
|
+
const content = (m.event.content ?? "").slice(0, 500);
|
|
12195
|
+
parts.push(`### [${m.event.eventType ?? "memory"}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
12196
|
+
parts.push(content);
|
|
12197
|
+
if (m.sessionContext) {
|
|
12198
|
+
parts.push(`_Context: ${m.sessionContext}_`);
|
|
12199
|
+
}
|
|
12200
|
+
parts.push("");
|
|
12201
|
+
}
|
|
12202
|
+
return parts.join("\n");
|
|
12203
|
+
}
|
|
12204
|
+
async function streamMemoryOnlyResponse(stream, options) {
|
|
12205
|
+
await stream.writeSSE({
|
|
12206
|
+
event: "diagnostic",
|
|
12207
|
+
data: JSON.stringify({
|
|
12208
|
+
provider: "claude-cli",
|
|
12209
|
+
status: "skipped",
|
|
12210
|
+
mode: "memory-only",
|
|
12211
|
+
reason: options.reason,
|
|
12212
|
+
retrievedMemories: options.memoryHits.length
|
|
12213
|
+
})
|
|
12214
|
+
});
|
|
12215
|
+
await streamMemoryOnlyFallback(stream, options.memoryContext, options.memoryHits);
|
|
12216
|
+
}
|
|
12217
|
+
async function streamMemoryOnlyFallback(stream, memoryContext, memoryHits) {
|
|
12218
|
+
const content = memoryHits.length > 0 ? [
|
|
12219
|
+
"Provider unavailable or skipped; showing retrieved memory context directly.",
|
|
12220
|
+
"",
|
|
12221
|
+
memoryContext
|
|
12222
|
+
].join("\n") : "Provider unavailable or skipped, and no directly relevant memories were found for this query.";
|
|
12223
|
+
await stream.writeSSE({
|
|
12224
|
+
event: "message",
|
|
12225
|
+
data: JSON.stringify({ content, mode: "memory-only" })
|
|
12226
|
+
});
|
|
12227
|
+
await stream.writeSSE({ event: "done", data: "{}" });
|
|
12228
|
+
}
|
|
12229
|
+
function providerDiagnostic(err) {
|
|
12230
|
+
if (err instanceof ProviderFailure) {
|
|
12231
|
+
return {
|
|
12232
|
+
provider: "claude-cli",
|
|
12233
|
+
code: err.code,
|
|
12234
|
+
message: err.message,
|
|
12235
|
+
fallback: "memory-only"
|
|
12236
|
+
};
|
|
12237
|
+
}
|
|
12238
|
+
return {
|
|
12239
|
+
provider: "claude-cli",
|
|
12240
|
+
code: "claude-cli-error",
|
|
12241
|
+
message: err instanceof Error ? err.message : "Unknown Claude CLI failure",
|
|
12242
|
+
fallback: "memory-only"
|
|
12243
|
+
};
|
|
12244
|
+
}
|
|
12245
|
+
function classifyProviderFailure(message) {
|
|
12246
|
+
const normalized = message.toLowerCase();
|
|
12247
|
+
if (normalized.includes("401") || normalized.includes("unauthorized") || normalized.includes("auth")) {
|
|
12248
|
+
return new ProviderFailure("claude-cli-auth", "Claude CLI authentication failed; showing memory-only context.");
|
|
12249
|
+
}
|
|
12250
|
+
if (normalized.includes("not found") || normalized.includes("enoent")) {
|
|
12251
|
+
return new ProviderFailure("claude-cli-not-found", "Claude CLI was not found; showing memory-only context.");
|
|
12252
|
+
}
|
|
12253
|
+
if (normalized.includes("timed out")) {
|
|
12254
|
+
return new ProviderFailure("claude-cli-timeout", "Claude CLI timed out; showing memory-only context.");
|
|
12255
|
+
}
|
|
12256
|
+
return new ProviderFailure("claude-cli-error", "Claude CLI failed; showing memory-only context.");
|
|
12257
|
+
}
|
|
12073
12258
|
function buildPrompt(statsContext, memoryContext, history, currentMessage) {
|
|
12074
12259
|
const parts = [];
|
|
12075
12260
|
parts.push("You are a helpful assistant that answers questions about the user's code memory data.");
|
|
@@ -12112,12 +12297,13 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
12112
12297
|
});
|
|
12113
12298
|
const timeout = setTimeout(() => {
|
|
12114
12299
|
proc.kill("SIGTERM");
|
|
12115
|
-
reject(
|
|
12300
|
+
reject(classifyProviderFailure("timed out"));
|
|
12116
12301
|
}, CLAUDE_TIMEOUT_MS);
|
|
12117
12302
|
proc.stdin.write(prompt);
|
|
12118
12303
|
proc.stdin.end();
|
|
12119
12304
|
let buffer = "";
|
|
12120
12305
|
let lastSentText = "";
|
|
12306
|
+
let stderrText = "";
|
|
12121
12307
|
proc.stdout.on("data", async (chunk) => {
|
|
12122
12308
|
buffer += chunk.toString();
|
|
12123
12309
|
const lines = buffer.split("\n");
|
|
@@ -12146,6 +12332,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
12146
12332
|
}
|
|
12147
12333
|
});
|
|
12148
12334
|
proc.stderr.on("data", (chunk) => {
|
|
12335
|
+
stderrText += chunk.toString();
|
|
12149
12336
|
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
12150
12337
|
console.error("[chat] claude stderr:", chunk.toString());
|
|
12151
12338
|
}
|
|
@@ -12153,7 +12340,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
12153
12340
|
proc.on("error", (err) => {
|
|
12154
12341
|
clearTimeout(timeout);
|
|
12155
12342
|
if (err.code === "ENOENT") {
|
|
12156
|
-
reject(
|
|
12343
|
+
reject(classifyProviderFailure("ENOENT not found"));
|
|
12157
12344
|
} else {
|
|
12158
12345
|
reject(err);
|
|
12159
12346
|
}
|
|
@@ -12170,7 +12357,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
12170
12357
|
}
|
|
12171
12358
|
}
|
|
12172
12359
|
if (code !== 0 && code !== null) {
|
|
12173
|
-
reject(
|
|
12360
|
+
reject(classifyProviderFailure(stderrText || `Claude CLI exited with code ${code}`));
|
|
12174
12361
|
} else {
|
|
12175
12362
|
resolve8();
|
|
12176
12363
|
}
|
|
@@ -12219,6 +12406,49 @@ healthRouter.get("/", async (c) => {
|
|
|
12219
12406
|
await memoryService.shutdown();
|
|
12220
12407
|
}
|
|
12221
12408
|
});
|
|
12409
|
+
healthRouter.post("/recover", async (c) => {
|
|
12410
|
+
const memoryService = getWritableServiceFromQuery(c);
|
|
12411
|
+
try {
|
|
12412
|
+
await memoryService.initialize();
|
|
12413
|
+
const body = await c.req.json().catch(() => ({}));
|
|
12414
|
+
const options = {};
|
|
12415
|
+
if (typeof body.stuckThresholdMs === "number" && Number.isFinite(body.stuckThresholdMs)) {
|
|
12416
|
+
options.stuckThresholdMs = body.stuckThresholdMs;
|
|
12417
|
+
}
|
|
12418
|
+
if (typeof body.maxRetries === "number" && Number.isFinite(body.maxRetries)) {
|
|
12419
|
+
options.maxRetries = body.maxRetries;
|
|
12420
|
+
}
|
|
12421
|
+
const before = await memoryService.getOutboxStats();
|
|
12422
|
+
const recovered = await memoryService.recoverStuckOutboxItems(options);
|
|
12423
|
+
const [stats, outbox] = await Promise.all([
|
|
12424
|
+
memoryService.getStats(),
|
|
12425
|
+
memoryService.getOutboxStats()
|
|
12426
|
+
]);
|
|
12427
|
+
return c.json({
|
|
12428
|
+
status: "ok",
|
|
12429
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12430
|
+
recovered,
|
|
12431
|
+
before: {
|
|
12432
|
+
outbox: before
|
|
12433
|
+
},
|
|
12434
|
+
after: {
|
|
12435
|
+
storage: {
|
|
12436
|
+
totalEvents: stats.totalEvents,
|
|
12437
|
+
vectorCount: stats.vectorCount
|
|
12438
|
+
},
|
|
12439
|
+
outbox
|
|
12440
|
+
}
|
|
12441
|
+
});
|
|
12442
|
+
} catch (error) {
|
|
12443
|
+
return c.json({
|
|
12444
|
+
status: "error",
|
|
12445
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12446
|
+
error: error.message
|
|
12447
|
+
}, 500);
|
|
12448
|
+
} finally {
|
|
12449
|
+
await memoryService.shutdown();
|
|
12450
|
+
}
|
|
12451
|
+
});
|
|
12222
12452
|
|
|
12223
12453
|
// src/apps/server/api/index.ts
|
|
12224
12454
|
var apiRouter = new Hono10().route("/sessions", sessionsRouter).route("/events", eventsRouter).route("/search", searchRouter).route("/stats", statsRouter).route("/citations", citationsRouter).route("/turns", turnsRouter).route("/projects", projectsRouter).route("/chat", chatRouter).route("/health", healthRouter);
|
|
@@ -14012,7 +14242,7 @@ async function runMarketContextCommand(options) {
|
|
|
14012
14242
|
}
|
|
14013
14243
|
}
|
|
14014
14244
|
var program = new Command();
|
|
14015
|
-
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.
|
|
14245
|
+
program.name("claude-memory-layer").description("Claude Code Memory Plugin CLI").version("1.0.37");
|
|
14016
14246
|
program.command("market-context").description("Fetch read-only DART/FRED/Finnhub context with structured MarketContextSnapshot bull/bear/risk/catalyst analysis").option("--company <name>", "Company name for DART fallback search and report subject").option("--dart-corp-code <code>", "Exact DART corp_code for issuer-specific filings").option("--symbol <ticker>", "Listed ticker for Finnhub company profile").option("--providers <list>", "Comma-separated providers: dart,fred,finnhub").option("--fred-series <list>", "Comma-separated FRED series IDs").option("--json", "Print structured JSON including analysis.marketSnapshot").option("--no-snapshot", "Disable MarketContextSnapshot and DART company snapshot analysis").action(async (options) => {
|
|
14017
14247
|
try {
|
|
14018
14248
|
await runMarketContextCommand(options);
|
|
@@ -14247,11 +14477,18 @@ program.command("forget [eventId]").description("Remove memories from storage").
|
|
|
14247
14477
|
process.exit(1);
|
|
14248
14478
|
}
|
|
14249
14479
|
});
|
|
14250
|
-
program.command("process").description("Process pending embeddings").option("-p, --project <path>", "Project path (defaults to cwd)").action(async (options) => {
|
|
14480
|
+
program.command("process").description("Process pending embeddings").option("-p, --project <path>", "Project path (defaults to cwd)").option("--no-recover-stuck", "Skip stale processing outbox recovery before processing").action(async (options) => {
|
|
14251
14481
|
const projectPath = options.project || process.cwd();
|
|
14252
14482
|
const service = getMemoryServiceForProject(projectPath);
|
|
14253
14483
|
try {
|
|
14254
14484
|
await service.initialize();
|
|
14485
|
+
if (options.recoverStuck !== false) {
|
|
14486
|
+
const recovered = await service.recoverStuckOutboxItems();
|
|
14487
|
+
const recoveredCount = recovered.embedding.recoveredProcessing + recovered.embedding.retriedFailed + recovered.vector.recoveredProcessing + recovered.vector.retriedFailed;
|
|
14488
|
+
if (recoveredCount > 0) {
|
|
14489
|
+
console.log(`\u267B\uFE0F Recovered stuck outbox work: embedding=${recovered.embedding.recoveredProcessing}/${recovered.embedding.retriedFailed}, vector=${recovered.vector.recoveredProcessing}/${recovered.vector.retriedFailed}`);
|
|
14490
|
+
}
|
|
14491
|
+
}
|
|
14255
14492
|
console.log("\u23F3 Processing pending embeddings...");
|
|
14256
14493
|
const count = await service.processPendingEmbeddings();
|
|
14257
14494
|
console.log(`\u2705 Processed ${count} embeddings`);
|