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/server/index.js
CHANGED
|
@@ -2339,6 +2339,14 @@ function normalizeQueryRewriteKind(value) {
|
|
|
2339
2339
|
return "none";
|
|
2340
2340
|
}
|
|
2341
2341
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2342
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2343
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
2344
|
+
function emptyOutboxRecoveryResult() {
|
|
2345
|
+
return {
|
|
2346
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
2347
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2342
2350
|
var SQLiteEventStore = class {
|
|
2343
2351
|
db;
|
|
2344
2352
|
initialized = false;
|
|
@@ -3089,7 +3097,9 @@ var SQLiteEventStore = class {
|
|
|
3089
3097
|
const placeholders = ids.map(() => "?").join(",");
|
|
3090
3098
|
sqliteRun(
|
|
3091
3099
|
this.db,
|
|
3092
|
-
`UPDATE embedding_outbox
|
|
3100
|
+
`UPDATE embedding_outbox
|
|
3101
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
3102
|
+
WHERE id IN (${placeholders})`,
|
|
3093
3103
|
ids
|
|
3094
3104
|
);
|
|
3095
3105
|
return pending.map((row) => ({
|
|
@@ -3159,6 +3169,58 @@ var SQLiteEventStore = class {
|
|
|
3159
3169
|
[error, ...ids]
|
|
3160
3170
|
);
|
|
3161
3171
|
}
|
|
3172
|
+
/**
|
|
3173
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
3174
|
+
*
|
|
3175
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
3176
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
3177
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
3178
|
+
*/
|
|
3179
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
3180
|
+
await this.initialize();
|
|
3181
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
3182
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
3183
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3184
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
3185
|
+
const result = emptyOutboxRecoveryResult();
|
|
3186
|
+
const embeddingRecovered = sqliteRun(
|
|
3187
|
+
this.db,
|
|
3188
|
+
`UPDATE embedding_outbox
|
|
3189
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
3190
|
+
WHERE status = 'processing'
|
|
3191
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
3192
|
+
[threshold]
|
|
3193
|
+
);
|
|
3194
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
3195
|
+
const embeddingRetried = sqliteRun(
|
|
3196
|
+
this.db,
|
|
3197
|
+
`UPDATE embedding_outbox
|
|
3198
|
+
SET status = 'pending', error_message = NULL
|
|
3199
|
+
WHERE status = 'failed'
|
|
3200
|
+
AND retry_count < ?`,
|
|
3201
|
+
[maxRetries]
|
|
3202
|
+
);
|
|
3203
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
3204
|
+
const vectorRecovered = sqliteRun(
|
|
3205
|
+
this.db,
|
|
3206
|
+
`UPDATE vector_outbox
|
|
3207
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3208
|
+
WHERE status = 'processing'
|
|
3209
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
3210
|
+
[now.toISOString(), threshold]
|
|
3211
|
+
);
|
|
3212
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
3213
|
+
const vectorRetried = sqliteRun(
|
|
3214
|
+
this.db,
|
|
3215
|
+
`UPDATE vector_outbox
|
|
3216
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3217
|
+
WHERE status = 'failed'
|
|
3218
|
+
AND retry_count < ?`,
|
|
3219
|
+
[now.toISOString(), maxRetries]
|
|
3220
|
+
);
|
|
3221
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3222
|
+
return result;
|
|
3223
|
+
}
|
|
3162
3224
|
/**
|
|
3163
3225
|
* Get embedding/vector outbox health statistics
|
|
3164
3226
|
*/
|
|
@@ -4030,6 +4092,7 @@ var VectorStore = class {
|
|
|
4030
4092
|
* Get total count of vectors
|
|
4031
4093
|
*/
|
|
4032
4094
|
async count() {
|
|
4095
|
+
await this.initialize();
|
|
4033
4096
|
if (!this.table)
|
|
4034
4097
|
return 0;
|
|
4035
4098
|
const result = await this.table.countRows();
|
|
@@ -4468,6 +4531,10 @@ var MemoryQueryService = class {
|
|
|
4468
4531
|
await this.initialize();
|
|
4469
4532
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
4470
4533
|
}
|
|
4534
|
+
async recoverStuckOutboxItems(options) {
|
|
4535
|
+
await this.initialize();
|
|
4536
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4537
|
+
}
|
|
4471
4538
|
async getStats() {
|
|
4472
4539
|
await this.initialize();
|
|
4473
4540
|
const deps = this.getStatsDeps();
|
|
@@ -7740,6 +7807,9 @@ var MemoryService = class {
|
|
|
7740
7807
|
async getOutboxStats() {
|
|
7741
7808
|
return this.queryService.getOutboxStats();
|
|
7742
7809
|
}
|
|
7810
|
+
async recoverStuckOutboxItems(options) {
|
|
7811
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
7812
|
+
}
|
|
7743
7813
|
async getRetrievalTraceStats() {
|
|
7744
7814
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7745
7815
|
}
|
|
@@ -8053,6 +8123,26 @@ function getServiceFromQuery(c) {
|
|
|
8053
8123
|
}
|
|
8054
8124
|
return getReadOnlyMemoryService();
|
|
8055
8125
|
}
|
|
8126
|
+
function getWritableServiceFromQuery(c) {
|
|
8127
|
+
const project = c.req.query("project") || c.req.query("projectId");
|
|
8128
|
+
if (project) {
|
|
8129
|
+
const storagePath = resolveProjectStoragePath(project);
|
|
8130
|
+
return new MemoryService({
|
|
8131
|
+
storagePath,
|
|
8132
|
+
readOnly: false,
|
|
8133
|
+
lightweightMode: true,
|
|
8134
|
+
analyticsEnabled: false,
|
|
8135
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
8136
|
+
});
|
|
8137
|
+
}
|
|
8138
|
+
return new MemoryService({
|
|
8139
|
+
storagePath: "~/.claude-code/memory",
|
|
8140
|
+
readOnly: false,
|
|
8141
|
+
lightweightMode: true,
|
|
8142
|
+
analyticsEnabled: false,
|
|
8143
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
8144
|
+
});
|
|
8145
|
+
}
|
|
8056
8146
|
function getLightweightServiceFromQuery(c) {
|
|
8057
8147
|
const project = c.req.query("project") || c.req.query("projectId");
|
|
8058
8148
|
if (project) {
|
|
@@ -9708,6 +9798,13 @@ import { Hono as Hono8 } from "hono";
|
|
|
9708
9798
|
import { streamSSE } from "hono/streaming";
|
|
9709
9799
|
import { spawn } from "child_process";
|
|
9710
9800
|
var chatRouter = new Hono8();
|
|
9801
|
+
var ProviderFailure = class extends Error {
|
|
9802
|
+
constructor(code, message) {
|
|
9803
|
+
super(message);
|
|
9804
|
+
this.code = code;
|
|
9805
|
+
this.name = "ProviderFailure";
|
|
9806
|
+
}
|
|
9807
|
+
};
|
|
9711
9808
|
var CLAUDE_TIMEOUT_MS = 12e4;
|
|
9712
9809
|
chatRouter.post("/", async (c) => {
|
|
9713
9810
|
let body;
|
|
@@ -9719,43 +9816,12 @@ chatRouter.post("/", async (c) => {
|
|
|
9719
9816
|
if (!body.message?.trim()) {
|
|
9720
9817
|
return c.json({ error: "Message is required" }, 400);
|
|
9721
9818
|
}
|
|
9722
|
-
const
|
|
9819
|
+
const memoryOnly = body.mode === "memory-only" || body.memoryOnly === true;
|
|
9820
|
+
const memoryService = memoryOnly ? getLightweightServiceFromQuery(c) : getServiceFromQuery(c);
|
|
9723
9821
|
try {
|
|
9724
9822
|
await memoryService.initialize();
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
try {
|
|
9728
|
-
const result = await memoryService.retrieveMemories(body.message, {
|
|
9729
|
-
topK: 8,
|
|
9730
|
-
minScore: 0.5
|
|
9731
|
-
});
|
|
9732
|
-
if (result.memories.length > 0) {
|
|
9733
|
-
const parts = ["## Relevant Memories\n"];
|
|
9734
|
-
for (const m of result.memories) {
|
|
9735
|
-
const date = new Date(m.event.timestamp).toISOString().split("T")[0];
|
|
9736
|
-
const content = m.event.content.slice(0, 500);
|
|
9737
|
-
parts.push(`### [${m.event.eventType}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
9738
|
-
parts.push(content);
|
|
9739
|
-
if (m.sessionContext) {
|
|
9740
|
-
parts.push(`_Context: ${m.sessionContext}_`);
|
|
9741
|
-
}
|
|
9742
|
-
parts.push("");
|
|
9743
|
-
}
|
|
9744
|
-
memoryContext = parts.join("\n");
|
|
9745
|
-
}
|
|
9746
|
-
} catch {
|
|
9747
|
-
}
|
|
9748
|
-
try {
|
|
9749
|
-
const stats = await memoryService.getStats();
|
|
9750
|
-
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
9751
|
-
statsContext = [
|
|
9752
|
-
"## Memory Stats",
|
|
9753
|
-
`- Total events: ${stats.totalEvents}`,
|
|
9754
|
-
`- Vector nodes: ${stats.vectorCount}`,
|
|
9755
|
-
`- By level: ${levels}`
|
|
9756
|
-
].join("\n");
|
|
9757
|
-
} catch {
|
|
9758
|
-
}
|
|
9823
|
+
const { memoryContext, memoryHits } = await collectMemoryContext(memoryService, body.message);
|
|
9824
|
+
const statsContext = await collectStatsContext(memoryService);
|
|
9759
9825
|
const fullPrompt = buildPrompt(
|
|
9760
9826
|
statsContext,
|
|
9761
9827
|
memoryContext,
|
|
@@ -9763,13 +9829,23 @@ chatRouter.post("/", async (c) => {
|
|
|
9763
9829
|
body.message
|
|
9764
9830
|
);
|
|
9765
9831
|
return streamSSE(c, async (stream) => {
|
|
9832
|
+
if (memoryOnly) {
|
|
9833
|
+
await streamMemoryOnlyResponse(stream, {
|
|
9834
|
+
memoryContext,
|
|
9835
|
+
memoryHits,
|
|
9836
|
+
reason: "memory-only-mode"
|
|
9837
|
+
});
|
|
9838
|
+
return;
|
|
9839
|
+
}
|
|
9766
9840
|
try {
|
|
9767
9841
|
await streamClaudeResponse(fullPrompt, stream);
|
|
9768
9842
|
} catch (err) {
|
|
9843
|
+
const diagnostic = providerDiagnostic(err);
|
|
9769
9844
|
await stream.writeSSE({
|
|
9770
|
-
event: "
|
|
9771
|
-
data: JSON.stringify(
|
|
9845
|
+
event: "provider_error",
|
|
9846
|
+
data: JSON.stringify(diagnostic)
|
|
9772
9847
|
});
|
|
9848
|
+
await streamMemoryOnlyFallback(stream, memoryContext, memoryHits);
|
|
9773
9849
|
}
|
|
9774
9850
|
});
|
|
9775
9851
|
} catch (error) {
|
|
@@ -9778,6 +9854,115 @@ chatRouter.post("/", async (c) => {
|
|
|
9778
9854
|
await memoryService.shutdown();
|
|
9779
9855
|
}
|
|
9780
9856
|
});
|
|
9857
|
+
async function collectMemoryContext(memoryService, query) {
|
|
9858
|
+
let memoryHits = [];
|
|
9859
|
+
try {
|
|
9860
|
+
const result = await memoryService.retrieveMemories?.(query, {
|
|
9861
|
+
topK: 8,
|
|
9862
|
+
minScore: 0.5
|
|
9863
|
+
});
|
|
9864
|
+
memoryHits = result?.memories ?? [];
|
|
9865
|
+
} catch {
|
|
9866
|
+
memoryHits = [];
|
|
9867
|
+
}
|
|
9868
|
+
if (memoryHits.length === 0) {
|
|
9869
|
+
try {
|
|
9870
|
+
memoryHits = await memoryService.keywordSearch?.(query, { topK: 8, minScore: 0.05 }) ?? [];
|
|
9871
|
+
} catch {
|
|
9872
|
+
memoryHits = [];
|
|
9873
|
+
}
|
|
9874
|
+
}
|
|
9875
|
+
return {
|
|
9876
|
+
memoryContext: formatMemoryContext(memoryHits),
|
|
9877
|
+
memoryHits
|
|
9878
|
+
};
|
|
9879
|
+
}
|
|
9880
|
+
async function collectStatsContext(memoryService) {
|
|
9881
|
+
try {
|
|
9882
|
+
const stats = await memoryService.getStats?.();
|
|
9883
|
+
if (!stats)
|
|
9884
|
+
return "";
|
|
9885
|
+
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
9886
|
+
return [
|
|
9887
|
+
"## Memory Stats",
|
|
9888
|
+
`- Total events: ${stats.totalEvents}`,
|
|
9889
|
+
`- Vector nodes: ${stats.vectorCount}`,
|
|
9890
|
+
`- By level: ${levels}`
|
|
9891
|
+
].join("\n");
|
|
9892
|
+
} catch {
|
|
9893
|
+
return "";
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
function formatMemoryContext(memoryHits) {
|
|
9897
|
+
if (memoryHits.length === 0)
|
|
9898
|
+
return "";
|
|
9899
|
+
const parts = ["## Relevant Memories\n"];
|
|
9900
|
+
for (const m of memoryHits) {
|
|
9901
|
+
const date = m.event.timestamp ? new Date(m.event.timestamp).toISOString().split("T")[0] : "unknown-date";
|
|
9902
|
+
const content = (m.event.content ?? "").slice(0, 500);
|
|
9903
|
+
parts.push(`### [${m.event.eventType ?? "memory"}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
9904
|
+
parts.push(content);
|
|
9905
|
+
if (m.sessionContext) {
|
|
9906
|
+
parts.push(`_Context: ${m.sessionContext}_`);
|
|
9907
|
+
}
|
|
9908
|
+
parts.push("");
|
|
9909
|
+
}
|
|
9910
|
+
return parts.join("\n");
|
|
9911
|
+
}
|
|
9912
|
+
async function streamMemoryOnlyResponse(stream, options) {
|
|
9913
|
+
await stream.writeSSE({
|
|
9914
|
+
event: "diagnostic",
|
|
9915
|
+
data: JSON.stringify({
|
|
9916
|
+
provider: "claude-cli",
|
|
9917
|
+
status: "skipped",
|
|
9918
|
+
mode: "memory-only",
|
|
9919
|
+
reason: options.reason,
|
|
9920
|
+
retrievedMemories: options.memoryHits.length
|
|
9921
|
+
})
|
|
9922
|
+
});
|
|
9923
|
+
await streamMemoryOnlyFallback(stream, options.memoryContext, options.memoryHits);
|
|
9924
|
+
}
|
|
9925
|
+
async function streamMemoryOnlyFallback(stream, memoryContext, memoryHits) {
|
|
9926
|
+
const content = memoryHits.length > 0 ? [
|
|
9927
|
+
"Provider unavailable or skipped; showing retrieved memory context directly.",
|
|
9928
|
+
"",
|
|
9929
|
+
memoryContext
|
|
9930
|
+
].join("\n") : "Provider unavailable or skipped, and no directly relevant memories were found for this query.";
|
|
9931
|
+
await stream.writeSSE({
|
|
9932
|
+
event: "message",
|
|
9933
|
+
data: JSON.stringify({ content, mode: "memory-only" })
|
|
9934
|
+
});
|
|
9935
|
+
await stream.writeSSE({ event: "done", data: "{}" });
|
|
9936
|
+
}
|
|
9937
|
+
function providerDiagnostic(err) {
|
|
9938
|
+
if (err instanceof ProviderFailure) {
|
|
9939
|
+
return {
|
|
9940
|
+
provider: "claude-cli",
|
|
9941
|
+
code: err.code,
|
|
9942
|
+
message: err.message,
|
|
9943
|
+
fallback: "memory-only"
|
|
9944
|
+
};
|
|
9945
|
+
}
|
|
9946
|
+
return {
|
|
9947
|
+
provider: "claude-cli",
|
|
9948
|
+
code: "claude-cli-error",
|
|
9949
|
+
message: err instanceof Error ? err.message : "Unknown Claude CLI failure",
|
|
9950
|
+
fallback: "memory-only"
|
|
9951
|
+
};
|
|
9952
|
+
}
|
|
9953
|
+
function classifyProviderFailure(message) {
|
|
9954
|
+
const normalized = message.toLowerCase();
|
|
9955
|
+
if (normalized.includes("401") || normalized.includes("unauthorized") || normalized.includes("auth")) {
|
|
9956
|
+
return new ProviderFailure("claude-cli-auth", "Claude CLI authentication failed; showing memory-only context.");
|
|
9957
|
+
}
|
|
9958
|
+
if (normalized.includes("not found") || normalized.includes("enoent")) {
|
|
9959
|
+
return new ProviderFailure("claude-cli-not-found", "Claude CLI was not found; showing memory-only context.");
|
|
9960
|
+
}
|
|
9961
|
+
if (normalized.includes("timed out")) {
|
|
9962
|
+
return new ProviderFailure("claude-cli-timeout", "Claude CLI timed out; showing memory-only context.");
|
|
9963
|
+
}
|
|
9964
|
+
return new ProviderFailure("claude-cli-error", "Claude CLI failed; showing memory-only context.");
|
|
9965
|
+
}
|
|
9781
9966
|
function buildPrompt(statsContext, memoryContext, history, currentMessage) {
|
|
9782
9967
|
const parts = [];
|
|
9783
9968
|
parts.push("You are a helpful assistant that answers questions about the user's code memory data.");
|
|
@@ -9820,12 +10005,13 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9820
10005
|
});
|
|
9821
10006
|
const timeout = setTimeout(() => {
|
|
9822
10007
|
proc.kill("SIGTERM");
|
|
9823
|
-
reject(
|
|
10008
|
+
reject(classifyProviderFailure("timed out"));
|
|
9824
10009
|
}, CLAUDE_TIMEOUT_MS);
|
|
9825
10010
|
proc.stdin.write(prompt);
|
|
9826
10011
|
proc.stdin.end();
|
|
9827
10012
|
let buffer = "";
|
|
9828
10013
|
let lastSentText = "";
|
|
10014
|
+
let stderrText = "";
|
|
9829
10015
|
proc.stdout.on("data", async (chunk) => {
|
|
9830
10016
|
buffer += chunk.toString();
|
|
9831
10017
|
const lines = buffer.split("\n");
|
|
@@ -9854,6 +10040,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9854
10040
|
}
|
|
9855
10041
|
});
|
|
9856
10042
|
proc.stderr.on("data", (chunk) => {
|
|
10043
|
+
stderrText += chunk.toString();
|
|
9857
10044
|
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
9858
10045
|
console.error("[chat] claude stderr:", chunk.toString());
|
|
9859
10046
|
}
|
|
@@ -9861,7 +10048,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9861
10048
|
proc.on("error", (err) => {
|
|
9862
10049
|
clearTimeout(timeout);
|
|
9863
10050
|
if (err.code === "ENOENT") {
|
|
9864
|
-
reject(
|
|
10051
|
+
reject(classifyProviderFailure("ENOENT not found"));
|
|
9865
10052
|
} else {
|
|
9866
10053
|
reject(err);
|
|
9867
10054
|
}
|
|
@@ -9878,7 +10065,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9878
10065
|
}
|
|
9879
10066
|
}
|
|
9880
10067
|
if (code !== 0 && code !== null) {
|
|
9881
|
-
reject(
|
|
10068
|
+
reject(classifyProviderFailure(stderrText || `Claude CLI exited with code ${code}`));
|
|
9882
10069
|
} else {
|
|
9883
10070
|
resolve4();
|
|
9884
10071
|
}
|
|
@@ -9927,6 +10114,49 @@ healthRouter.get("/", async (c) => {
|
|
|
9927
10114
|
await memoryService.shutdown();
|
|
9928
10115
|
}
|
|
9929
10116
|
});
|
|
10117
|
+
healthRouter.post("/recover", async (c) => {
|
|
10118
|
+
const memoryService = getWritableServiceFromQuery(c);
|
|
10119
|
+
try {
|
|
10120
|
+
await memoryService.initialize();
|
|
10121
|
+
const body = await c.req.json().catch(() => ({}));
|
|
10122
|
+
const options = {};
|
|
10123
|
+
if (typeof body.stuckThresholdMs === "number" && Number.isFinite(body.stuckThresholdMs)) {
|
|
10124
|
+
options.stuckThresholdMs = body.stuckThresholdMs;
|
|
10125
|
+
}
|
|
10126
|
+
if (typeof body.maxRetries === "number" && Number.isFinite(body.maxRetries)) {
|
|
10127
|
+
options.maxRetries = body.maxRetries;
|
|
10128
|
+
}
|
|
10129
|
+
const before = await memoryService.getOutboxStats();
|
|
10130
|
+
const recovered = await memoryService.recoverStuckOutboxItems(options);
|
|
10131
|
+
const [stats, outbox] = await Promise.all([
|
|
10132
|
+
memoryService.getStats(),
|
|
10133
|
+
memoryService.getOutboxStats()
|
|
10134
|
+
]);
|
|
10135
|
+
return c.json({
|
|
10136
|
+
status: "ok",
|
|
10137
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10138
|
+
recovered,
|
|
10139
|
+
before: {
|
|
10140
|
+
outbox: before
|
|
10141
|
+
},
|
|
10142
|
+
after: {
|
|
10143
|
+
storage: {
|
|
10144
|
+
totalEvents: stats.totalEvents,
|
|
10145
|
+
vectorCount: stats.vectorCount
|
|
10146
|
+
},
|
|
10147
|
+
outbox
|
|
10148
|
+
}
|
|
10149
|
+
});
|
|
10150
|
+
} catch (error) {
|
|
10151
|
+
return c.json({
|
|
10152
|
+
status: "error",
|
|
10153
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10154
|
+
error: error.message
|
|
10155
|
+
}, 500);
|
|
10156
|
+
} finally {
|
|
10157
|
+
await memoryService.shutdown();
|
|
10158
|
+
}
|
|
10159
|
+
});
|
|
9930
10160
|
|
|
9931
10161
|
// src/apps/server/api/index.ts
|
|
9932
10162
|
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);
|