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/api/index.js
CHANGED
|
@@ -2327,6 +2327,14 @@ function normalizeQueryRewriteKind(value) {
|
|
|
2327
2327
|
return "none";
|
|
2328
2328
|
}
|
|
2329
2329
|
var REWRITTEN_QUERY_REWRITE_KIND_SQL = `LOWER(TRIM(COALESCE(query_rewrite_kind, 'none'))) IN ('follow-up-context', 'intent-rewrite')`;
|
|
2330
|
+
var DEFAULT_OUTBOX_STUCK_THRESHOLD_MS = 5 * 60 * 1e3;
|
|
2331
|
+
var DEFAULT_OUTBOX_MAX_RETRIES = 3;
|
|
2332
|
+
function emptyOutboxRecoveryResult() {
|
|
2333
|
+
return {
|
|
2334
|
+
embedding: { recoveredProcessing: 0, retriedFailed: 0 },
|
|
2335
|
+
vector: { recoveredProcessing: 0, retriedFailed: 0 }
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2330
2338
|
var SQLiteEventStore = class {
|
|
2331
2339
|
db;
|
|
2332
2340
|
initialized = false;
|
|
@@ -3077,7 +3085,9 @@ var SQLiteEventStore = class {
|
|
|
3077
3085
|
const placeholders = ids.map(() => "?").join(",");
|
|
3078
3086
|
sqliteRun(
|
|
3079
3087
|
this.db,
|
|
3080
|
-
`UPDATE embedding_outbox
|
|
3088
|
+
`UPDATE embedding_outbox
|
|
3089
|
+
SET status = 'processing', processed_at = datetime('now'), error_message = NULL
|
|
3090
|
+
WHERE id IN (${placeholders})`,
|
|
3081
3091
|
ids
|
|
3082
3092
|
);
|
|
3083
3093
|
return pending.map((row) => ({
|
|
@@ -3147,6 +3157,58 @@ var SQLiteEventStore = class {
|
|
|
3147
3157
|
[error, ...ids]
|
|
3148
3158
|
);
|
|
3149
3159
|
}
|
|
3160
|
+
/**
|
|
3161
|
+
* Recover abandoned outbox work after a worker/process crash.
|
|
3162
|
+
*
|
|
3163
|
+
* Rows in `processing` are claimed work. If the process exits before marking
|
|
3164
|
+
* them done/failed, they otherwise remain invisible to future processing.
|
|
3165
|
+
* Recovery is deliberately age-gated so an active worker is not disturbed.
|
|
3166
|
+
*/
|
|
3167
|
+
async recoverStuckOutboxItems(options = {}) {
|
|
3168
|
+
await this.initialize();
|
|
3169
|
+
const thresholdMs = Number.isFinite(options.stuckThresholdMs) && (options.stuckThresholdMs ?? 0) >= 0 ? options.stuckThresholdMs : DEFAULT_OUTBOX_STUCK_THRESHOLD_MS;
|
|
3170
|
+
const maxRetries = Number.isFinite(options.maxRetries) && (options.maxRetries ?? 0) > 0 ? options.maxRetries : DEFAULT_OUTBOX_MAX_RETRIES;
|
|
3171
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
3172
|
+
const threshold = new Date(now.getTime() - thresholdMs).toISOString();
|
|
3173
|
+
const result = emptyOutboxRecoveryResult();
|
|
3174
|
+
const embeddingRecovered = sqliteRun(
|
|
3175
|
+
this.db,
|
|
3176
|
+
`UPDATE embedding_outbox
|
|
3177
|
+
SET status = 'pending', processed_at = NULL, error_message = NULL
|
|
3178
|
+
WHERE status = 'processing'
|
|
3179
|
+
AND datetime(COALESCE(processed_at, created_at)) < datetime(?)`,
|
|
3180
|
+
[threshold]
|
|
3181
|
+
);
|
|
3182
|
+
result.embedding.recoveredProcessing = Number(embeddingRecovered.changes ?? 0);
|
|
3183
|
+
const embeddingRetried = sqliteRun(
|
|
3184
|
+
this.db,
|
|
3185
|
+
`UPDATE embedding_outbox
|
|
3186
|
+
SET status = 'pending', error_message = NULL
|
|
3187
|
+
WHERE status = 'failed'
|
|
3188
|
+
AND retry_count < ?`,
|
|
3189
|
+
[maxRetries]
|
|
3190
|
+
);
|
|
3191
|
+
result.embedding.retriedFailed = Number(embeddingRetried.changes ?? 0);
|
|
3192
|
+
const vectorRecovered = sqliteRun(
|
|
3193
|
+
this.db,
|
|
3194
|
+
`UPDATE vector_outbox
|
|
3195
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3196
|
+
WHERE status = 'processing'
|
|
3197
|
+
AND datetime(updated_at) < datetime(?)`,
|
|
3198
|
+
[now.toISOString(), threshold]
|
|
3199
|
+
);
|
|
3200
|
+
result.vector.recoveredProcessing = Number(vectorRecovered.changes ?? 0);
|
|
3201
|
+
const vectorRetried = sqliteRun(
|
|
3202
|
+
this.db,
|
|
3203
|
+
`UPDATE vector_outbox
|
|
3204
|
+
SET status = 'pending', updated_at = ?, error = NULL
|
|
3205
|
+
WHERE status = 'failed'
|
|
3206
|
+
AND retry_count < ?`,
|
|
3207
|
+
[now.toISOString(), maxRetries]
|
|
3208
|
+
);
|
|
3209
|
+
result.vector.retriedFailed = Number(vectorRetried.changes ?? 0);
|
|
3210
|
+
return result;
|
|
3211
|
+
}
|
|
3150
3212
|
/**
|
|
3151
3213
|
* Get embedding/vector outbox health statistics
|
|
3152
3214
|
*/
|
|
@@ -4018,6 +4080,7 @@ var VectorStore = class {
|
|
|
4018
4080
|
* Get total count of vectors
|
|
4019
4081
|
*/
|
|
4020
4082
|
async count() {
|
|
4083
|
+
await this.initialize();
|
|
4021
4084
|
if (!this.table)
|
|
4022
4085
|
return 0;
|
|
4023
4086
|
const result = await this.table.countRows();
|
|
@@ -4456,6 +4519,10 @@ var MemoryQueryService = class {
|
|
|
4456
4519
|
await this.initialize();
|
|
4457
4520
|
return this.getMaintenanceStore("getOutboxStats").getOutboxStats();
|
|
4458
4521
|
}
|
|
4522
|
+
async recoverStuckOutboxItems(options) {
|
|
4523
|
+
await this.initialize();
|
|
4524
|
+
return this.getMaintenanceStore("recoverStuckOutboxItems").recoverStuckOutboxItems(options);
|
|
4525
|
+
}
|
|
4459
4526
|
async getStats() {
|
|
4460
4527
|
await this.initialize();
|
|
4461
4528
|
const deps = this.getStatsDeps();
|
|
@@ -7728,6 +7795,9 @@ var MemoryService = class {
|
|
|
7728
7795
|
async getOutboxStats() {
|
|
7729
7796
|
return this.queryService.getOutboxStats();
|
|
7730
7797
|
}
|
|
7798
|
+
async recoverStuckOutboxItems(options) {
|
|
7799
|
+
return this.queryService.recoverStuckOutboxItems(options);
|
|
7800
|
+
}
|
|
7731
7801
|
async getRetrievalTraceStats() {
|
|
7732
7802
|
return this.retrievalAnalyticsService.getRetrievalTraceStats();
|
|
7733
7803
|
}
|
|
@@ -8041,6 +8111,26 @@ function getServiceFromQuery(c) {
|
|
|
8041
8111
|
}
|
|
8042
8112
|
return getReadOnlyMemoryService();
|
|
8043
8113
|
}
|
|
8114
|
+
function getWritableServiceFromQuery(c) {
|
|
8115
|
+
const project = c.req.query("project") || c.req.query("projectId");
|
|
8116
|
+
if (project) {
|
|
8117
|
+
const storagePath = resolveProjectStoragePath(project);
|
|
8118
|
+
return new MemoryService({
|
|
8119
|
+
storagePath,
|
|
8120
|
+
readOnly: false,
|
|
8121
|
+
lightweightMode: true,
|
|
8122
|
+
analyticsEnabled: false,
|
|
8123
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
8124
|
+
});
|
|
8125
|
+
}
|
|
8126
|
+
return new MemoryService({
|
|
8127
|
+
storagePath: "~/.claude-code/memory",
|
|
8128
|
+
readOnly: false,
|
|
8129
|
+
lightweightMode: true,
|
|
8130
|
+
analyticsEnabled: false,
|
|
8131
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
8132
|
+
});
|
|
8133
|
+
}
|
|
8044
8134
|
function getLightweightServiceFromQuery(c) {
|
|
8045
8135
|
const project = c.req.query("project") || c.req.query("projectId");
|
|
8046
8136
|
if (project) {
|
|
@@ -9696,6 +9786,13 @@ import { Hono as Hono8 } from "hono";
|
|
|
9696
9786
|
import { streamSSE } from "hono/streaming";
|
|
9697
9787
|
import { spawn } from "child_process";
|
|
9698
9788
|
var chatRouter = new Hono8();
|
|
9789
|
+
var ProviderFailure = class extends Error {
|
|
9790
|
+
constructor(code, message) {
|
|
9791
|
+
super(message);
|
|
9792
|
+
this.code = code;
|
|
9793
|
+
this.name = "ProviderFailure";
|
|
9794
|
+
}
|
|
9795
|
+
};
|
|
9699
9796
|
var CLAUDE_TIMEOUT_MS = 12e4;
|
|
9700
9797
|
chatRouter.post("/", async (c) => {
|
|
9701
9798
|
let body;
|
|
@@ -9707,43 +9804,12 @@ chatRouter.post("/", async (c) => {
|
|
|
9707
9804
|
if (!body.message?.trim()) {
|
|
9708
9805
|
return c.json({ error: "Message is required" }, 400);
|
|
9709
9806
|
}
|
|
9710
|
-
const
|
|
9807
|
+
const memoryOnly = body.mode === "memory-only" || body.memoryOnly === true;
|
|
9808
|
+
const memoryService = memoryOnly ? getLightweightServiceFromQuery(c) : getServiceFromQuery(c);
|
|
9711
9809
|
try {
|
|
9712
9810
|
await memoryService.initialize();
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
try {
|
|
9716
|
-
const result = await memoryService.retrieveMemories(body.message, {
|
|
9717
|
-
topK: 8,
|
|
9718
|
-
minScore: 0.5
|
|
9719
|
-
});
|
|
9720
|
-
if (result.memories.length > 0) {
|
|
9721
|
-
const parts = ["## Relevant Memories\n"];
|
|
9722
|
-
for (const m of result.memories) {
|
|
9723
|
-
const date = new Date(m.event.timestamp).toISOString().split("T")[0];
|
|
9724
|
-
const content = m.event.content.slice(0, 500);
|
|
9725
|
-
parts.push(`### [${m.event.eventType}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
9726
|
-
parts.push(content);
|
|
9727
|
-
if (m.sessionContext) {
|
|
9728
|
-
parts.push(`_Context: ${m.sessionContext}_`);
|
|
9729
|
-
}
|
|
9730
|
-
parts.push("");
|
|
9731
|
-
}
|
|
9732
|
-
memoryContext = parts.join("\n");
|
|
9733
|
-
}
|
|
9734
|
-
} catch {
|
|
9735
|
-
}
|
|
9736
|
-
try {
|
|
9737
|
-
const stats = await memoryService.getStats();
|
|
9738
|
-
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
9739
|
-
statsContext = [
|
|
9740
|
-
"## Memory Stats",
|
|
9741
|
-
`- Total events: ${stats.totalEvents}`,
|
|
9742
|
-
`- Vector nodes: ${stats.vectorCount}`,
|
|
9743
|
-
`- By level: ${levels}`
|
|
9744
|
-
].join("\n");
|
|
9745
|
-
} catch {
|
|
9746
|
-
}
|
|
9811
|
+
const { memoryContext, memoryHits } = await collectMemoryContext(memoryService, body.message);
|
|
9812
|
+
const statsContext = await collectStatsContext(memoryService);
|
|
9747
9813
|
const fullPrompt = buildPrompt(
|
|
9748
9814
|
statsContext,
|
|
9749
9815
|
memoryContext,
|
|
@@ -9751,13 +9817,23 @@ chatRouter.post("/", async (c) => {
|
|
|
9751
9817
|
body.message
|
|
9752
9818
|
);
|
|
9753
9819
|
return streamSSE(c, async (stream) => {
|
|
9820
|
+
if (memoryOnly) {
|
|
9821
|
+
await streamMemoryOnlyResponse(stream, {
|
|
9822
|
+
memoryContext,
|
|
9823
|
+
memoryHits,
|
|
9824
|
+
reason: "memory-only-mode"
|
|
9825
|
+
});
|
|
9826
|
+
return;
|
|
9827
|
+
}
|
|
9754
9828
|
try {
|
|
9755
9829
|
await streamClaudeResponse(fullPrompt, stream);
|
|
9756
9830
|
} catch (err) {
|
|
9831
|
+
const diagnostic = providerDiagnostic(err);
|
|
9757
9832
|
await stream.writeSSE({
|
|
9758
|
-
event: "
|
|
9759
|
-
data: JSON.stringify(
|
|
9833
|
+
event: "provider_error",
|
|
9834
|
+
data: JSON.stringify(diagnostic)
|
|
9760
9835
|
});
|
|
9836
|
+
await streamMemoryOnlyFallback(stream, memoryContext, memoryHits);
|
|
9761
9837
|
}
|
|
9762
9838
|
});
|
|
9763
9839
|
} catch (error) {
|
|
@@ -9766,6 +9842,115 @@ chatRouter.post("/", async (c) => {
|
|
|
9766
9842
|
await memoryService.shutdown();
|
|
9767
9843
|
}
|
|
9768
9844
|
});
|
|
9845
|
+
async function collectMemoryContext(memoryService, query) {
|
|
9846
|
+
let memoryHits = [];
|
|
9847
|
+
try {
|
|
9848
|
+
const result = await memoryService.retrieveMemories?.(query, {
|
|
9849
|
+
topK: 8,
|
|
9850
|
+
minScore: 0.5
|
|
9851
|
+
});
|
|
9852
|
+
memoryHits = result?.memories ?? [];
|
|
9853
|
+
} catch {
|
|
9854
|
+
memoryHits = [];
|
|
9855
|
+
}
|
|
9856
|
+
if (memoryHits.length === 0) {
|
|
9857
|
+
try {
|
|
9858
|
+
memoryHits = await memoryService.keywordSearch?.(query, { topK: 8, minScore: 0.05 }) ?? [];
|
|
9859
|
+
} catch {
|
|
9860
|
+
memoryHits = [];
|
|
9861
|
+
}
|
|
9862
|
+
}
|
|
9863
|
+
return {
|
|
9864
|
+
memoryContext: formatMemoryContext(memoryHits),
|
|
9865
|
+
memoryHits
|
|
9866
|
+
};
|
|
9867
|
+
}
|
|
9868
|
+
async function collectStatsContext(memoryService) {
|
|
9869
|
+
try {
|
|
9870
|
+
const stats = await memoryService.getStats?.();
|
|
9871
|
+
if (!stats)
|
|
9872
|
+
return "";
|
|
9873
|
+
const levels = stats.levelStats.map((l) => `${l.level}: ${l.count}`).join(", ");
|
|
9874
|
+
return [
|
|
9875
|
+
"## Memory Stats",
|
|
9876
|
+
`- Total events: ${stats.totalEvents}`,
|
|
9877
|
+
`- Vector nodes: ${stats.vectorCount}`,
|
|
9878
|
+
`- By level: ${levels}`
|
|
9879
|
+
].join("\n");
|
|
9880
|
+
} catch {
|
|
9881
|
+
return "";
|
|
9882
|
+
}
|
|
9883
|
+
}
|
|
9884
|
+
function formatMemoryContext(memoryHits) {
|
|
9885
|
+
if (memoryHits.length === 0)
|
|
9886
|
+
return "";
|
|
9887
|
+
const parts = ["## Relevant Memories\n"];
|
|
9888
|
+
for (const m of memoryHits) {
|
|
9889
|
+
const date = m.event.timestamp ? new Date(m.event.timestamp).toISOString().split("T")[0] : "unknown-date";
|
|
9890
|
+
const content = (m.event.content ?? "").slice(0, 500);
|
|
9891
|
+
parts.push(`### [${m.event.eventType ?? "memory"}] ${date} (score: ${m.score.toFixed(2)})`);
|
|
9892
|
+
parts.push(content);
|
|
9893
|
+
if (m.sessionContext) {
|
|
9894
|
+
parts.push(`_Context: ${m.sessionContext}_`);
|
|
9895
|
+
}
|
|
9896
|
+
parts.push("");
|
|
9897
|
+
}
|
|
9898
|
+
return parts.join("\n");
|
|
9899
|
+
}
|
|
9900
|
+
async function streamMemoryOnlyResponse(stream, options) {
|
|
9901
|
+
await stream.writeSSE({
|
|
9902
|
+
event: "diagnostic",
|
|
9903
|
+
data: JSON.stringify({
|
|
9904
|
+
provider: "claude-cli",
|
|
9905
|
+
status: "skipped",
|
|
9906
|
+
mode: "memory-only",
|
|
9907
|
+
reason: options.reason,
|
|
9908
|
+
retrievedMemories: options.memoryHits.length
|
|
9909
|
+
})
|
|
9910
|
+
});
|
|
9911
|
+
await streamMemoryOnlyFallback(stream, options.memoryContext, options.memoryHits);
|
|
9912
|
+
}
|
|
9913
|
+
async function streamMemoryOnlyFallback(stream, memoryContext, memoryHits) {
|
|
9914
|
+
const content = memoryHits.length > 0 ? [
|
|
9915
|
+
"Provider unavailable or skipped; showing retrieved memory context directly.",
|
|
9916
|
+
"",
|
|
9917
|
+
memoryContext
|
|
9918
|
+
].join("\n") : "Provider unavailable or skipped, and no directly relevant memories were found for this query.";
|
|
9919
|
+
await stream.writeSSE({
|
|
9920
|
+
event: "message",
|
|
9921
|
+
data: JSON.stringify({ content, mode: "memory-only" })
|
|
9922
|
+
});
|
|
9923
|
+
await stream.writeSSE({ event: "done", data: "{}" });
|
|
9924
|
+
}
|
|
9925
|
+
function providerDiagnostic(err) {
|
|
9926
|
+
if (err instanceof ProviderFailure) {
|
|
9927
|
+
return {
|
|
9928
|
+
provider: "claude-cli",
|
|
9929
|
+
code: err.code,
|
|
9930
|
+
message: err.message,
|
|
9931
|
+
fallback: "memory-only"
|
|
9932
|
+
};
|
|
9933
|
+
}
|
|
9934
|
+
return {
|
|
9935
|
+
provider: "claude-cli",
|
|
9936
|
+
code: "claude-cli-error",
|
|
9937
|
+
message: err instanceof Error ? err.message : "Unknown Claude CLI failure",
|
|
9938
|
+
fallback: "memory-only"
|
|
9939
|
+
};
|
|
9940
|
+
}
|
|
9941
|
+
function classifyProviderFailure(message) {
|
|
9942
|
+
const normalized = message.toLowerCase();
|
|
9943
|
+
if (normalized.includes("401") || normalized.includes("unauthorized") || normalized.includes("auth")) {
|
|
9944
|
+
return new ProviderFailure("claude-cli-auth", "Claude CLI authentication failed; showing memory-only context.");
|
|
9945
|
+
}
|
|
9946
|
+
if (normalized.includes("not found") || normalized.includes("enoent")) {
|
|
9947
|
+
return new ProviderFailure("claude-cli-not-found", "Claude CLI was not found; showing memory-only context.");
|
|
9948
|
+
}
|
|
9949
|
+
if (normalized.includes("timed out")) {
|
|
9950
|
+
return new ProviderFailure("claude-cli-timeout", "Claude CLI timed out; showing memory-only context.");
|
|
9951
|
+
}
|
|
9952
|
+
return new ProviderFailure("claude-cli-error", "Claude CLI failed; showing memory-only context.");
|
|
9953
|
+
}
|
|
9769
9954
|
function buildPrompt(statsContext, memoryContext, history, currentMessage) {
|
|
9770
9955
|
const parts = [];
|
|
9771
9956
|
parts.push("You are a helpful assistant that answers questions about the user's code memory data.");
|
|
@@ -9808,12 +9993,13 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9808
9993
|
});
|
|
9809
9994
|
const timeout = setTimeout(() => {
|
|
9810
9995
|
proc.kill("SIGTERM");
|
|
9811
|
-
reject(
|
|
9996
|
+
reject(classifyProviderFailure("timed out"));
|
|
9812
9997
|
}, CLAUDE_TIMEOUT_MS);
|
|
9813
9998
|
proc.stdin.write(prompt);
|
|
9814
9999
|
proc.stdin.end();
|
|
9815
10000
|
let buffer = "";
|
|
9816
10001
|
let lastSentText = "";
|
|
10002
|
+
let stderrText = "";
|
|
9817
10003
|
proc.stdout.on("data", async (chunk) => {
|
|
9818
10004
|
buffer += chunk.toString();
|
|
9819
10005
|
const lines = buffer.split("\n");
|
|
@@ -9842,6 +10028,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9842
10028
|
}
|
|
9843
10029
|
});
|
|
9844
10030
|
proc.stderr.on("data", (chunk) => {
|
|
10031
|
+
stderrText += chunk.toString();
|
|
9845
10032
|
if (process.env.CLAUDE_MEMORY_DEBUG) {
|
|
9846
10033
|
console.error("[chat] claude stderr:", chunk.toString());
|
|
9847
10034
|
}
|
|
@@ -9849,7 +10036,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9849
10036
|
proc.on("error", (err) => {
|
|
9850
10037
|
clearTimeout(timeout);
|
|
9851
10038
|
if (err.code === "ENOENT") {
|
|
9852
|
-
reject(
|
|
10039
|
+
reject(classifyProviderFailure("ENOENT not found"));
|
|
9853
10040
|
} else {
|
|
9854
10041
|
reject(err);
|
|
9855
10042
|
}
|
|
@@ -9866,7 +10053,7 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
9866
10053
|
}
|
|
9867
10054
|
}
|
|
9868
10055
|
if (code !== 0 && code !== null) {
|
|
9869
|
-
reject(
|
|
10056
|
+
reject(classifyProviderFailure(stderrText || `Claude CLI exited with code ${code}`));
|
|
9870
10057
|
} else {
|
|
9871
10058
|
resolve3();
|
|
9872
10059
|
}
|
|
@@ -9915,6 +10102,49 @@ healthRouter.get("/", async (c) => {
|
|
|
9915
10102
|
await memoryService.shutdown();
|
|
9916
10103
|
}
|
|
9917
10104
|
});
|
|
10105
|
+
healthRouter.post("/recover", async (c) => {
|
|
10106
|
+
const memoryService = getWritableServiceFromQuery(c);
|
|
10107
|
+
try {
|
|
10108
|
+
await memoryService.initialize();
|
|
10109
|
+
const body = await c.req.json().catch(() => ({}));
|
|
10110
|
+
const options = {};
|
|
10111
|
+
if (typeof body.stuckThresholdMs === "number" && Number.isFinite(body.stuckThresholdMs)) {
|
|
10112
|
+
options.stuckThresholdMs = body.stuckThresholdMs;
|
|
10113
|
+
}
|
|
10114
|
+
if (typeof body.maxRetries === "number" && Number.isFinite(body.maxRetries)) {
|
|
10115
|
+
options.maxRetries = body.maxRetries;
|
|
10116
|
+
}
|
|
10117
|
+
const before = await memoryService.getOutboxStats();
|
|
10118
|
+
const recovered = await memoryService.recoverStuckOutboxItems(options);
|
|
10119
|
+
const [stats, outbox] = await Promise.all([
|
|
10120
|
+
memoryService.getStats(),
|
|
10121
|
+
memoryService.getOutboxStats()
|
|
10122
|
+
]);
|
|
10123
|
+
return c.json({
|
|
10124
|
+
status: "ok",
|
|
10125
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10126
|
+
recovered,
|
|
10127
|
+
before: {
|
|
10128
|
+
outbox: before
|
|
10129
|
+
},
|
|
10130
|
+
after: {
|
|
10131
|
+
storage: {
|
|
10132
|
+
totalEvents: stats.totalEvents,
|
|
10133
|
+
vectorCount: stats.vectorCount
|
|
10134
|
+
},
|
|
10135
|
+
outbox
|
|
10136
|
+
}
|
|
10137
|
+
});
|
|
10138
|
+
} catch (error) {
|
|
10139
|
+
return c.json({
|
|
10140
|
+
status: "error",
|
|
10141
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10142
|
+
error: error.message
|
|
10143
|
+
}, 500);
|
|
10144
|
+
} finally {
|
|
10145
|
+
await memoryService.shutdown();
|
|
10146
|
+
}
|
|
10147
|
+
});
|
|
9918
10148
|
|
|
9919
10149
|
// src/apps/server/api/index.ts
|
|
9920
10150
|
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);
|