om-memory-system 4.25.0 → 4.26.1
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 +17 -15
- package/dist/adapters/opencode/auto-capture-summary.js +1 -1
- package/dist/adapters/opencode/import-command.js +2 -2
- package/dist/adapters/opencode/profile-learning.js +9 -9
- package/dist/adapters/opencode/profile-model.js +1 -1
- package/dist/adapters/pi/capture.js +2 -2
- package/dist/adapters/pi/extension.js +8 -8
- package/dist/adapters/pi/import-command.js +2 -2
- package/dist/adapters/pi/live-model.js +1 -1
- package/dist/adapters/pi/profile.js +2 -2
- package/dist/cli/web-power.d.ts +2 -1
- package/dist/cli/web-power.js +2 -2
- package/dist/config.js +1 -1
- package/dist/core/profile-analysis.js +1 -1
- package/dist/core/profile-backoff.js +1 -1
- package/dist/importer/auto-backfill.js +14 -4
- package/dist/importer/backfill-controls.js +1 -1
- package/dist/importer/claude-hook-api.js +5 -5
- package/dist/importer/claude-profile-learning.js +1 -1
- package/dist/importer/import-readiness.d.ts +3 -0
- package/dist/importer/import-readiness.js +11 -4
- package/dist/importer/run-import.js +4 -3
- package/dist/index.js +19 -19
- package/dist/services/ai/providers/anthropic-messages.js +2 -2
- package/dist/services/ai/providers/google-gemini.js +1 -1
- package/dist/services/ai/providers/openai-chat-completion.js +3 -3
- package/dist/services/ai/providers/openai-responses.js +2 -2
- package/dist/services/ai-cleanup-handlers.js +1 -1
- package/dist/services/api-handlers.js +27 -27
- package/dist/services/auto-capture.js +3 -3
- package/dist/services/auto-cleanup-handlers.js +1 -1
- package/dist/services/backfill-state.d.ts +8 -2
- package/dist/services/backfill-state.js +14 -4
- package/dist/services/capture-diagnostics.js +3 -3
- package/dist/services/capture-retry-drain.js +5 -5
- package/dist/services/capture-retry-queue.js +1 -1
- package/dist/services/chat/chat-routes.js +1 -1
- package/dist/services/cleanup-service.js +5 -5
- package/dist/services/client.js +10 -10
- package/dist/services/deduplication-service.js +1 -1
- package/dist/services/embedding.js +2 -2
- package/dist/services/host-appearance.d.ts +28 -0
- package/dist/services/host-appearance.js +117 -0
- package/dist/services/logger.d.ts +7 -1
- package/dist/services/logger.js +9 -3
- package/dist/services/memory-host-routes.d.ts +11 -0
- package/dist/services/memory-host-routes.js +56 -0
- package/dist/services/memory-portability-service.js +1 -1
- package/dist/services/migration-service.js +2 -2
- package/dist/services/settings-log.d.ts +23 -2
- package/dist/services/settings-log.js +65 -7
- package/dist/services/shard-path-migration-service.js +4 -4
- package/dist/services/tag-prefix-migration.js +1 -1
- package/dist/services/turso/connection-manager.js +3 -3
- package/dist/services/turso/keyword-search.js +1 -1
- package/dist/services/turso/legacy-migrator.js +1 -1
- package/dist/services/turso/ready.js +1 -1
- package/dist/services/turso/shard-manager.js +2 -2
- package/dist/services/turso/vector-search.js +3 -3
- package/dist/services/user-profile/ai-cleanup-batches.js +1 -1
- package/dist/services/user-profile/ai-cleanup-job.js +1 -1
- package/dist/services/user-profile/auto-cleanup.js +2 -2
- package/dist/services/user-profile/category-cleanup-job.js +1 -1
- package/dist/services/user-profile/category-cleanup.js +3 -3
- package/dist/services/user-profile/merge-and-save.js +1 -1
- package/dist/services/user-profile/profile-context.js +1 -1
- package/dist/services/user-profile/profile-model.js +1 -1
- package/dist/services/user-profile/user-profile-manager.js +12 -12
- package/dist/services/web-autostart.js +2 -2
- package/dist/services/web-route-modules.js +4 -0
- package/dist/services/web-server.js +13 -13
- package/dist/services/web-update.d.ts +2 -1
- package/dist/services/web-update.js +1 -1
- package/dist/services/write-health-checks.d.ts +3 -0
- package/dist/services/write-health-checks.js +20 -0
- package/dist/services/write-health.d.ts +26 -0
- package/dist/services/write-health.js +58 -0
- package/dist/v2/adapter.js +4 -4
- package/dist/web/assets/index-CIlyLof-.js +126 -0
- package/dist/web/assets/index-DH6qnFfk.css +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-DWk9C9Sm.css +0 -2
- package/dist/web/assets/index-rHcTKCkC.js +0 -126
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ export async function resolveSessionAgent(client, sessionID) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
catch (error) {
|
|
60
|
-
log("resolveSessionAgent: session.get failed", { sessionID, error: String(error) });
|
|
60
|
+
log("resolveSessionAgent: session.get failed", { sessionID, error: String(error) }, "error");
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
if (typeof sessionClient?.messages !== "function") {
|
|
@@ -89,7 +89,7 @@ export async function resolveSessionAgent(client, sessionID) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
catch (error) {
|
|
92
|
-
log("resolveSessionAgent: session.messages failed", { sessionID, error: String(error) });
|
|
92
|
+
log("resolveSessionAgent: session.messages failed", { sessionID, error: String(error) }, "error");
|
|
93
93
|
}
|
|
94
94
|
return undefined;
|
|
95
95
|
}
|
|
@@ -117,11 +117,11 @@ async function isInternalCaptureSession(client, sessionID) {
|
|
|
117
117
|
log("internal capture session check via session.get failed", {
|
|
118
118
|
sessionID,
|
|
119
119
|
error: String(error),
|
|
120
|
-
});
|
|
120
|
+
}, "error");
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
else {
|
|
124
|
-
log("internal capture session check: session.get unavailable", { sessionID });
|
|
124
|
+
log("internal capture session check: session.get unavailable", { sessionID }, "warn");
|
|
125
125
|
}
|
|
126
126
|
return false;
|
|
127
127
|
}
|
|
@@ -164,7 +164,7 @@ export async function configureOpencodeHostTransport(ctx) {
|
|
|
164
164
|
log("OpenCode host fetch unavailable; falling back to global fetch", {
|
|
165
165
|
clientKeys: hostConfig.clientKeys,
|
|
166
166
|
sdkConfigCount: hostConfig.sdkConfigCount,
|
|
167
|
-
});
|
|
167
|
+
}, "warn");
|
|
168
168
|
}
|
|
169
169
|
const serverUrl = hostConfig.baseUrl ?? ctx.serverUrl;
|
|
170
170
|
if (serverUrl) {
|
|
@@ -195,13 +195,13 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
195
195
|
.then(({ registerOwnCopy }) => registerOwnCopy())
|
|
196
196
|
.catch((error) => log("OpenCode runtime record failed", {
|
|
197
197
|
code: error instanceof Error ? error.name : "unknown",
|
|
198
|
-
}));
|
|
198
|
+
}, "error"));
|
|
199
199
|
if (CONFIG.webServerAutoStart !== undefined && process.env.OMMS_DISABLE_WEB_AUTOSTART !== "1") {
|
|
200
200
|
void import("./services/web-autostart.js")
|
|
201
201
|
.then(({ reconcileWebAutostart }) => reconcileWebAutostart(CONFIG))
|
|
202
202
|
.catch((error) => log("OpenCode login item reconciliation failed", {
|
|
203
203
|
code: error instanceof Error ? error.name : "unknown",
|
|
204
|
-
}));
|
|
204
|
+
}, "error"));
|
|
205
205
|
}
|
|
206
206
|
logAutoCaptureProviderStatus();
|
|
207
207
|
const tags = getTags(directory);
|
|
@@ -216,7 +216,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
216
216
|
globalThis[GLOBAL_PLUGIN_WARMUP_KEY] = true;
|
|
217
217
|
}
|
|
218
218
|
catch (error) {
|
|
219
|
-
log("Plugin memory warmup failed", { error: String(error) });
|
|
219
|
+
log("Plugin memory warmup failed", { error: String(error) }, "error");
|
|
220
220
|
}
|
|
221
221
|
})();
|
|
222
222
|
}
|
|
@@ -247,11 +247,11 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
247
247
|
else {
|
|
248
248
|
log("opencode provider list empty or failed", {
|
|
249
249
|
data: JSON.stringify(providerResult.data).substring(0, 100),
|
|
250
|
-
});
|
|
250
|
+
}, "warn");
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
catch (error) {
|
|
254
|
-
log("Failed to initialize opencode provider state", { error: String(error) });
|
|
254
|
+
log("Failed to initialize opencode provider state", { error: String(error) }, "error");
|
|
255
255
|
}
|
|
256
256
|
const configModel = async () => {
|
|
257
257
|
const response = await ctx.client.config.get();
|
|
@@ -277,7 +277,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
277
277
|
}))
|
|
278
278
|
.catch((error) => log("OpenCode backfill failed", {
|
|
279
279
|
error: error instanceof Error ? error.message : String(error),
|
|
280
|
-
}));
|
|
280
|
+
}, "error"));
|
|
281
281
|
}
|
|
282
282
|
})();
|
|
283
283
|
let tursoReadyForWeb = !isConfigured();
|
|
@@ -287,7 +287,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
287
287
|
tursoReadyForWeb = true;
|
|
288
288
|
}
|
|
289
289
|
catch (error) {
|
|
290
|
-
log("Turso ready gate failed before web server start", { error: String(error) });
|
|
290
|
+
log("Turso ready gate failed before web server start", { error: String(error) }, "error");
|
|
291
291
|
if (ctx.client?.tui) {
|
|
292
292
|
ctx.client.tui
|
|
293
293
|
.showToast({
|
|
@@ -338,7 +338,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
338
338
|
}
|
|
339
339
|
});
|
|
340
340
|
})
|
|
341
|
-
.catch((error) => log("Web app start failed", { error: String(error) }));
|
|
341
|
+
.catch((error) => log("Web app start failed", { error: String(error) }, "error"));
|
|
342
342
|
}
|
|
343
343
|
let cleanedUp = false;
|
|
344
344
|
const cleanupPlugin = async () => {
|
|
@@ -357,7 +357,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
357
357
|
await cleanupPlugin();
|
|
358
358
|
}
|
|
359
359
|
catch (error) {
|
|
360
|
-
log("Shutdown error", { error: String(error) });
|
|
360
|
+
log("Shutdown error", { error: String(error) }, "error");
|
|
361
361
|
process.exitCode = 1;
|
|
362
362
|
}
|
|
363
363
|
};
|
|
@@ -477,7 +477,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
477
477
|
}
|
|
478
478
|
}
|
|
479
479
|
catch (error) {
|
|
480
|
-
log("chat.message: ERROR", { error: String(error) });
|
|
480
|
+
log("chat.message: ERROR", { error: String(error) }, "error");
|
|
481
481
|
if (ctx.client?.tui && CONFIG.showErrorToasts) {
|
|
482
482
|
await ctx.client.tui
|
|
483
483
|
.showToast({
|
|
@@ -500,7 +500,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
500
500
|
await userPromptManager.setPromptModel(input.message.id, input.model.providerID, input.model.id);
|
|
501
501
|
}
|
|
502
502
|
catch (error) {
|
|
503
|
-
log("chat.params: ERROR", { error: String(error) });
|
|
503
|
+
log("chat.params: ERROR", { error: String(error) }, "error");
|
|
504
504
|
}
|
|
505
505
|
},
|
|
506
506
|
tool: {
|
|
@@ -581,7 +581,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
581
581
|
await cleanupService.runCleanup();
|
|
582
582
|
}
|
|
583
583
|
catch (error) {
|
|
584
|
-
log("Idle processing error", { error: String(error) });
|
|
584
|
+
log("Idle processing error", { error: String(error) }, "error");
|
|
585
585
|
}
|
|
586
586
|
finally {
|
|
587
587
|
idleTimeout = null;
|
|
@@ -610,7 +610,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
610
610
|
if (!agent) {
|
|
611
611
|
log("Compaction: skipped memory injection because session agent could not be resolved", {
|
|
612
612
|
sessionID,
|
|
613
|
-
});
|
|
613
|
+
}, "warn");
|
|
614
614
|
return;
|
|
615
615
|
}
|
|
616
616
|
await ctx.client.session.prompt({
|
|
@@ -647,7 +647,7 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
647
647
|
});
|
|
648
648
|
}
|
|
649
649
|
catch (error) {
|
|
650
|
-
log("Compaction handler error", { error: String(error) });
|
|
650
|
+
log("Compaction handler error", { error: String(error) }, "error");
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
653
|
},
|
|
@@ -120,7 +120,7 @@ export class AnthropicMessagesProvider extends BaseAIProvider {
|
|
|
120
120
|
status: response.status,
|
|
121
121
|
error: errorText,
|
|
122
122
|
iteration: iterations,
|
|
123
|
-
});
|
|
123
|
+
}, "error");
|
|
124
124
|
return {
|
|
125
125
|
stopReason,
|
|
126
126
|
success: false,
|
|
@@ -168,7 +168,7 @@ export class AnthropicMessagesProvider extends BaseAIProvider {
|
|
|
168
168
|
iteration: iterations,
|
|
169
169
|
// Log only the size: the tool input is the model's reply.
|
|
170
170
|
inputChars: JSON.stringify(toolUse)?.length ?? 0,
|
|
171
|
-
});
|
|
171
|
+
}, "error");
|
|
172
172
|
return {
|
|
173
173
|
stopReason,
|
|
174
174
|
success: false,
|
|
@@ -212,7 +212,7 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
|
|
|
212
212
|
status: response.status,
|
|
213
213
|
error: errorText,
|
|
214
214
|
iteration: iterations,
|
|
215
|
-
});
|
|
215
|
+
}, "error");
|
|
216
216
|
let errorMessage = `API error: ${response.status} - ${errorText}`;
|
|
217
217
|
if (response.status === 400 &&
|
|
218
218
|
errorText.includes("unsupported_value") &&
|
|
@@ -236,7 +236,7 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
|
|
|
236
236
|
model: this.config.model,
|
|
237
237
|
status: data.status,
|
|
238
238
|
msg: data.msg,
|
|
239
|
-
});
|
|
239
|
+
}, "error");
|
|
240
240
|
return {
|
|
241
241
|
stopReason,
|
|
242
242
|
success: false,
|
|
@@ -346,7 +346,7 @@ export class OpenAIChatCompletionProvider extends BaseAIProvider {
|
|
|
346
346
|
iteration: iterations,
|
|
347
347
|
// Log only the size: the arguments are the model's reply.
|
|
348
348
|
argumentsChars: toolCall.function.arguments.length,
|
|
349
|
-
});
|
|
349
|
+
}, "error");
|
|
350
350
|
const errorMessage = `Validation failed: ${describeValidationError(validationError)}`;
|
|
351
351
|
lastErrorMessage = errorMessage;
|
|
352
352
|
await this.addToolResponse(session.id, messages, toolCallId, JSON.stringify({ success: false, error: errorMessage }));
|
|
@@ -68,7 +68,7 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
68
68
|
status: response.status,
|
|
69
69
|
error: errorText,
|
|
70
70
|
iteration: iterations,
|
|
71
|
-
});
|
|
71
|
+
}, "error");
|
|
72
72
|
return {
|
|
73
73
|
stopReason,
|
|
74
74
|
success: false,
|
|
@@ -150,7 +150,7 @@ export class OpenAIResponsesProvider extends BaseAIProvider {
|
|
|
150
150
|
error: String(error),
|
|
151
151
|
toolName: item.name,
|
|
152
152
|
arguments: item.arguments,
|
|
153
|
-
});
|
|
153
|
+
}, "error");
|
|
154
154
|
return null;
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -12,7 +12,7 @@ async function managerOf(deps) {
|
|
|
12
12
|
}
|
|
13
13
|
function handlerError(step, error) {
|
|
14
14
|
// The log never holds profile text, so only the error's kind is kept.
|
|
15
|
-
log(`AI cleanup ${step}: error`, { code: error instanceof Error ? error.name : "unknown" });
|
|
15
|
+
log(`AI cleanup ${step}: error`, { code: error instanceof Error ? error.name : "unknown" }, "error");
|
|
16
16
|
return { success: false, error: String(error) };
|
|
17
17
|
}
|
|
18
18
|
/** The profile in the order the dialog lists it, so item ids match its positions. */
|
|
@@ -97,7 +97,7 @@ export async function handleListTags() {
|
|
|
97
97
|
return { success: true, data: { project: projectTags } };
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
|
-
log("handleListTags: error", { error: String(error) });
|
|
100
|
+
log("handleListTags: error", { error: String(error) }, "error");
|
|
101
101
|
return { success: false, error: String(error) };
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -140,7 +140,7 @@ export async function handleListKeywords(tag) {
|
|
|
140
140
|
return { success: true, data: { keywords: countKeywords(rows.map((r) => r.tags)) } };
|
|
141
141
|
}
|
|
142
142
|
catch (error) {
|
|
143
|
-
log("handleListKeywords: error", { error: String(error) });
|
|
143
|
+
log("handleListKeywords: error", { error: String(error) }, "error");
|
|
144
144
|
return { success: false, error: String(error) };
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -279,7 +279,7 @@ export async function handleListMemories(tag, page = 1, pageSize = 20, includePr
|
|
|
279
279
|
return { success: true, data: { items, total, page, pageSize, totalPages } };
|
|
280
280
|
}
|
|
281
281
|
catch (error) {
|
|
282
|
-
log("handleListMemories: error", { error: String(error) });
|
|
282
|
+
log("handleListMemories: error", { error: String(error) }, "error");
|
|
283
283
|
return { success: false, error: String(error) };
|
|
284
284
|
}
|
|
285
285
|
}
|
|
@@ -329,7 +329,7 @@ export async function handleAddMemory(data) {
|
|
|
329
329
|
});
|
|
330
330
|
}
|
|
331
331
|
catch (error) {
|
|
332
|
-
log("handleAddMemory: error", { error: String(error) });
|
|
332
|
+
log("handleAddMemory: error", { error: String(error) }, "error");
|
|
333
333
|
return { success: false, error: String(error) };
|
|
334
334
|
}
|
|
335
335
|
}
|
|
@@ -359,7 +359,7 @@ export async function handleDeleteMemory(id, cascade = false) {
|
|
|
359
359
|
return { success: false, error: "Memory not found" };
|
|
360
360
|
}
|
|
361
361
|
catch (error) {
|
|
362
|
-
log("handleDeleteMemory: error", { error: String(error) });
|
|
362
|
+
log("handleDeleteMemory: error", { error: String(error) }, "error");
|
|
363
363
|
return { success: false, error: String(error) };
|
|
364
364
|
}
|
|
365
365
|
}
|
|
@@ -376,7 +376,7 @@ export async function handleBulkDelete(ids, cascade = false) {
|
|
|
376
376
|
return { success: true, data: { deleted } };
|
|
377
377
|
}
|
|
378
378
|
catch (error) {
|
|
379
|
-
log("handleBulkDelete: error", { error: String(error) });
|
|
379
|
+
log("handleBulkDelete: error", { error: String(error) }, "error");
|
|
380
380
|
return { success: false, error: String(error) };
|
|
381
381
|
}
|
|
382
382
|
}
|
|
@@ -443,7 +443,7 @@ export async function handleUpdateMemory(id, data) {
|
|
|
443
443
|
return { success: true };
|
|
444
444
|
}
|
|
445
445
|
catch (error) {
|
|
446
|
-
log("handleUpdateMemory: error", { error: String(error) });
|
|
446
|
+
log("handleUpdateMemory: error", { error: String(error) }, "error");
|
|
447
447
|
return { success: false, error: String(error) };
|
|
448
448
|
}
|
|
449
449
|
}
|
|
@@ -465,7 +465,7 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
|
|
|
465
465
|
memoryResults.push(...results);
|
|
466
466
|
}
|
|
467
467
|
catch (error) {
|
|
468
|
-
log("Shard search error", { shardId: shard.id, error: String(error) });
|
|
468
|
+
log("Shard search error", { shardId: shard.id, error: String(error) }, "error");
|
|
469
469
|
}
|
|
470
470
|
}
|
|
471
471
|
const projectPath = await getProjectPathFromTag(tag);
|
|
@@ -485,7 +485,7 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
|
|
|
485
485
|
for (const containerTag of uniqueTags) {
|
|
486
486
|
const parsed = tryExtractScopeFromContainerTag(containerTag);
|
|
487
487
|
if (!parsed) {
|
|
488
|
-
log("Skipping invalid container_tag during global search", { containerTag });
|
|
488
|
+
log("Skipping invalid container_tag during global search", { containerTag }, "warn");
|
|
489
489
|
continue;
|
|
490
490
|
}
|
|
491
491
|
const { scope, hash } = parsed;
|
|
@@ -496,7 +496,7 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
|
|
|
496
496
|
memoryResults.push(...results);
|
|
497
497
|
}
|
|
498
498
|
catch (error) {
|
|
499
|
-
log("Shard search error", { shardId: shard.id, error: String(error) });
|
|
499
|
+
log("Shard search error", { shardId: shard.id, error: String(error) }, "error");
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
502
|
}
|
|
@@ -602,7 +602,7 @@ export async function handleSearch(query, tag, page = 1, pageSize = 20) {
|
|
|
602
602
|
return { success: true, data: { items: paginatedResults, total, page, pageSize, totalPages } };
|
|
603
603
|
}
|
|
604
604
|
catch (error) {
|
|
605
|
-
log("handleSearch: error", { error: String(error) });
|
|
605
|
+
log("handleSearch: error", { error: String(error) }, "error");
|
|
606
606
|
return { success: false, error: String(error) };
|
|
607
607
|
}
|
|
608
608
|
}
|
|
@@ -637,7 +637,7 @@ export async function handleStats() {
|
|
|
637
637
|
};
|
|
638
638
|
}
|
|
639
639
|
catch (error) {
|
|
640
|
-
log("handleStats: error", { error: String(error) });
|
|
640
|
+
log("handleStats: error", { error: String(error) }, "error");
|
|
641
641
|
return { success: false, error: String(error) };
|
|
642
642
|
}
|
|
643
643
|
}
|
|
@@ -657,7 +657,7 @@ export async function handlePinMemory(id) {
|
|
|
657
657
|
return { success: false, error: "Memory not found" };
|
|
658
658
|
}
|
|
659
659
|
catch (error) {
|
|
660
|
-
log("handlePinMemory: error", { error: String(error) });
|
|
660
|
+
log("handlePinMemory: error", { error: String(error) }, "error");
|
|
661
661
|
return { success: false, error: String(error) };
|
|
662
662
|
}
|
|
663
663
|
}
|
|
@@ -677,7 +677,7 @@ export async function handleUnpinMemory(id) {
|
|
|
677
677
|
return { success: false, error: "Memory not found" };
|
|
678
678
|
}
|
|
679
679
|
catch (error) {
|
|
680
|
-
log("handleUnpinMemory: error", { error: String(error) });
|
|
680
|
+
log("handleUnpinMemory: error", { error: String(error) }, "error");
|
|
681
681
|
return { success: false, error: String(error) };
|
|
682
682
|
}
|
|
683
683
|
}
|
|
@@ -688,7 +688,7 @@ export async function handleRunCleanup() {
|
|
|
688
688
|
return { success: true, data: result };
|
|
689
689
|
}
|
|
690
690
|
catch (error) {
|
|
691
|
-
log("handleRunCleanup: error", { error: String(error) });
|
|
691
|
+
log("handleRunCleanup: error", { error: String(error) }, "error");
|
|
692
692
|
return { success: false, error: String(error) };
|
|
693
693
|
}
|
|
694
694
|
}
|
|
@@ -699,7 +699,7 @@ export async function handleRunDeduplication() {
|
|
|
699
699
|
return { success: true, data: result };
|
|
700
700
|
}
|
|
701
701
|
catch (error) {
|
|
702
|
-
log("handleRunDeduplication: error", { error: String(error) });
|
|
702
|
+
log("handleRunDeduplication: error", { error: String(error) }, "error");
|
|
703
703
|
return { success: false, error: String(error) };
|
|
704
704
|
}
|
|
705
705
|
}
|
|
@@ -710,7 +710,7 @@ export async function handleDetectMigration() {
|
|
|
710
710
|
return { success: true, data: result };
|
|
711
711
|
}
|
|
712
712
|
catch (error) {
|
|
713
|
-
log("handleDetectMigration: error", { error: String(error) });
|
|
713
|
+
log("handleDetectMigration: error", { error: String(error) }, "error");
|
|
714
714
|
return { success: false, error: String(error) };
|
|
715
715
|
}
|
|
716
716
|
}
|
|
@@ -721,7 +721,7 @@ export async function handleRunMigration(strategy) {
|
|
|
721
721
|
return { success: result.success, data: result };
|
|
722
722
|
}
|
|
723
723
|
catch (error) {
|
|
724
|
-
log("handleRunMigration: error", { error: String(error) });
|
|
724
|
+
log("handleRunMigration: error", { error: String(error) }, "error");
|
|
725
725
|
return { success: false, error: String(error) };
|
|
726
726
|
}
|
|
727
727
|
}
|
|
@@ -742,7 +742,7 @@ export async function handleDeletePrompt(id, cascade = false) {
|
|
|
742
742
|
return { success: true, data: { deletedMemory } };
|
|
743
743
|
}
|
|
744
744
|
catch (error) {
|
|
745
|
-
log("handleDeletePrompt: error", { error: String(error) });
|
|
745
|
+
log("handleDeletePrompt: error", { error: String(error) }, "error");
|
|
746
746
|
return { success: false, error: String(error) };
|
|
747
747
|
}
|
|
748
748
|
}
|
|
@@ -759,7 +759,7 @@ export async function handleBulkDeletePrompts(ids, cascade = false) {
|
|
|
759
759
|
return { success: true, data: { deleted } };
|
|
760
760
|
}
|
|
761
761
|
catch (error) {
|
|
762
|
-
log("handleBulkDeletePrompts: error", { error: String(error) });
|
|
762
|
+
log("handleBulkDeletePrompts: error", { error: String(error) }, "error");
|
|
763
763
|
return { success: false, error: String(error) };
|
|
764
764
|
}
|
|
765
765
|
}
|
|
@@ -802,7 +802,7 @@ export async function handleGetUserProfile(userId) {
|
|
|
802
802
|
};
|
|
803
803
|
}
|
|
804
804
|
catch (error) {
|
|
805
|
-
log("handleGetUserProfile: error", { error: String(error) });
|
|
805
|
+
log("handleGetUserProfile: error", { error: String(error) }, "error");
|
|
806
806
|
return { success: false, error: String(error) };
|
|
807
807
|
}
|
|
808
808
|
}
|
|
@@ -823,7 +823,7 @@ export async function handleGetProfileChangelog(profileId, limit = 5) {
|
|
|
823
823
|
return { success: true, data: formattedChangelogs };
|
|
824
824
|
}
|
|
825
825
|
catch (error) {
|
|
826
|
-
log("handleGetProfileChangelog: error", { error: String(error) });
|
|
826
|
+
log("handleGetProfileChangelog: error", { error: String(error) }, "error");
|
|
827
827
|
return { success: false, error: String(error) };
|
|
828
828
|
}
|
|
829
829
|
}
|
|
@@ -846,7 +846,7 @@ export async function handleGetProfileSnapshot(changelogId) {
|
|
|
846
846
|
};
|
|
847
847
|
}
|
|
848
848
|
catch (error) {
|
|
849
|
-
log("handleGetProfileSnapshot: error", { error: String(error) });
|
|
849
|
+
log("handleGetProfileSnapshot: error", { error: String(error) }, "error");
|
|
850
850
|
return { success: false, error: String(error) };
|
|
851
851
|
}
|
|
852
852
|
}
|
|
@@ -880,7 +880,7 @@ export async function handleRefreshProfile(userId) {
|
|
|
880
880
|
};
|
|
881
881
|
}
|
|
882
882
|
catch (error) {
|
|
883
|
-
log("handleRefreshProfile: error", { error: String(error) });
|
|
883
|
+
log("handleRefreshProfile: error", { error: String(error) }, "error");
|
|
884
884
|
return { success: false, error: String(error) };
|
|
885
885
|
}
|
|
886
886
|
}
|
|
@@ -889,7 +889,7 @@ async function categoryCleanupUser(userId) {
|
|
|
889
889
|
}
|
|
890
890
|
function categoryCleanupError(step, error) {
|
|
891
891
|
// The log never holds category names or descriptions, so only the error's kind is kept.
|
|
892
|
-
log(`category cleanup ${step}: error`, { code: error instanceof Error ? error.name : "unknown" });
|
|
892
|
+
log(`category cleanup ${step}: error`, { code: error instanceof Error ? error.name : "unknown" }, "error");
|
|
893
893
|
return { success: false, error: String(error) };
|
|
894
894
|
}
|
|
895
895
|
/** Starts a background preview job, or returns the one already running. Changes nothing in the profile. */
|
|
@@ -1013,7 +1013,7 @@ export async function handleUpdateProfileItem(body) {
|
|
|
1013
1013
|
};
|
|
1014
1014
|
}
|
|
1015
1015
|
catch (error) {
|
|
1016
|
-
log("handleUpdateProfileItem: error", { error: String(error) });
|
|
1016
|
+
log("handleUpdateProfileItem: error", { error: String(error) }, "error");
|
|
1017
1017
|
return { success: false, error: String(error) };
|
|
1018
1018
|
}
|
|
1019
1019
|
}
|
|
@@ -1114,7 +1114,7 @@ export async function handleRunTagMigrationBatch(batchSize = 5) {
|
|
|
1114
1114
|
catch (e) {
|
|
1115
1115
|
const errorMsg = String(e);
|
|
1116
1116
|
migrationProgress.errors.push(errorMsg);
|
|
1117
|
-
log("Migration error for memory", { id: item.id, error: errorMsg });
|
|
1117
|
+
log("Migration error for memory", { id: item.id, error: errorMsg }, "error");
|
|
1118
1118
|
}
|
|
1119
1119
|
finally {
|
|
1120
1120
|
migrationProgress.processed++;
|
|
@@ -97,7 +97,7 @@ async function capturePrompt(host, sessionID, directory, prompt, maxRetries) {
|
|
|
97
97
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
98
98
|
await userPromptManager.recordFailedAttempt(prompt.id);
|
|
99
99
|
if (attempt < maxRetries) {
|
|
100
|
-
log(`Auto-capture warning (attempt ${attempt}/${maxRetries})`, { error: errMsg });
|
|
100
|
+
log(`Auto-capture warning (attempt ${attempt}/${maxRetries})`, { error: errMsg }, "warn");
|
|
101
101
|
await new Promise((resolve) => setTimeout(resolve, quickRetryDelayMs(attempt)));
|
|
102
102
|
}
|
|
103
103
|
else {
|
|
@@ -108,7 +108,7 @@ async function capturePrompt(host, sessionID, directory, prompt, maxRetries) {
|
|
|
108
108
|
}
|
|
109
109
|
catch (error) {
|
|
110
110
|
const errMsg = error instanceof Error ? error.message : String(error);
|
|
111
|
-
log(`Auto-capture final error after ${attempt} attempts`, { error: errMsg });
|
|
111
|
+
log(`Auto-capture final error after ${attempt} attempts`, { error: errMsg }, "error");
|
|
112
112
|
// Only after the quick retries fail; the queue decides whether a retry can help.
|
|
113
113
|
if (workUnit)
|
|
114
114
|
await queueFailedCapture(workUnit, error, CONFIG);
|
|
@@ -128,7 +128,7 @@ async function capturePrompt(host, sessionID, directory, prompt, maxRetries) {
|
|
|
128
128
|
await userPromptManager.releaseClaim(claimedPromptId);
|
|
129
129
|
}
|
|
130
130
|
catch (releaseErr) {
|
|
131
|
-
log(`Failed to release captured=2 claim for prompt ${claimedPromptId}: ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}
|
|
131
|
+
log(`Failed to release captured=2 claim for prompt ${claimedPromptId}: ${releaseErr instanceof Error ? releaseErr.message : String(releaseErr)}`, undefined, "error");
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
@@ -13,7 +13,7 @@ function handlerError(step, error) {
|
|
|
13
13
|
// The log never holds profile text, so only the error's kind is kept.
|
|
14
14
|
log(`Automatic cleanup ${step}: error`, {
|
|
15
15
|
code: error instanceof Error ? error.name : "unknown",
|
|
16
|
-
});
|
|
16
|
+
}, "error");
|
|
17
17
|
return { success: false, error: String(error) };
|
|
18
18
|
}
|
|
19
19
|
/** The newest automatic cleanup that can still be undone, without its items. */
|
|
@@ -62,10 +62,16 @@ export declare function unresolvedSessionCount(report: {
|
|
|
62
62
|
*/
|
|
63
63
|
export declare function visibleUnresolvedCount(count: number, directories: readonly UnresolvedDirectory[], ignored: readonly string[]): number;
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* How much history a saved unresolved list covers. A `partial` list comes from
|
|
66
|
+
* a run limited to history before a cutoff or a date range; it never replaces
|
|
67
|
+
* a `full` list, which covers the host's whole source.
|
|
68
|
+
*/
|
|
69
|
+
export type UnresolvedCoverage = "full" | "partial";
|
|
70
|
+
/**
|
|
71
|
+
* Save the unresolved directories of a host's latest run or listing. They
|
|
66
72
|
* live in their own table, so recording them never sets a backfill cutoff.
|
|
67
73
|
*/
|
|
68
|
-
export declare function recordUnresolvedDirectories(host: BackfillHost, directories: UnresolvedDirectory[], now?: number): Promise<void>;
|
|
74
|
+
export declare function recordUnresolvedDirectories(host: BackfillHost, directories: UnresolvedDirectory[], now?: number, coverage?: UnresolvedCoverage): Promise<void>;
|
|
69
75
|
/** A host's latest unresolved directories, without creating a store or table. */
|
|
70
76
|
export declare function readUnresolvedDirectories(host: BackfillHost): Promise<UnresolvedDirectory[]>;
|
|
71
77
|
/** Save only numeric counts and a redacted error, never report units or prompts. */
|
|
@@ -98,16 +98,26 @@ export function visibleUnresolvedCount(count, directories, ignored) {
|
|
|
98
98
|
return Math.max(0, count - sum(directories.filter(isIgnored)));
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
* Save the unresolved directories of a host's latest
|
|
101
|
+
* Save the unresolved directories of a host's latest run or listing. They
|
|
102
102
|
* live in their own table, so recording them never sets a backfill cutoff.
|
|
103
103
|
*/
|
|
104
|
-
export async function recordUnresolvedDirectories(host, directories, now = Date.now()) {
|
|
104
|
+
export async function recordUnresolvedDirectories(host, directories, now = Date.now(), coverage = "full") {
|
|
105
105
|
const db = await tursoConnectionManager.getConnection(importLedgerDbPath());
|
|
106
106
|
await db.run(`CREATE TABLE IF NOT EXISTS unresolved_directories (
|
|
107
107
|
host TEXT PRIMARY KEY, directories TEXT NOT NULL, updated_at INTEGER NOT NULL
|
|
108
108
|
)`);
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
try {
|
|
110
|
+
await db.run("ALTER TABLE unresolved_directories ADD COLUMN coverage TEXT NOT NULL DEFAULT 'full'");
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
if (!String(error?.message ?? error).includes("duplicate column"))
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
// One statement keeps two processes writing at once correct.
|
|
117
|
+
await db.run(`INSERT INTO unresolved_directories (host, directories, updated_at, coverage) VALUES (?, ?, ?, ?)
|
|
118
|
+
ON CONFLICT(host) DO UPDATE SET directories = excluded.directories,
|
|
119
|
+
updated_at = excluded.updated_at, coverage = excluded.coverage
|
|
120
|
+
WHERE unresolved_directories.coverage = 'partial' OR excluded.coverage = 'full'`, [host, JSON.stringify(directories.slice(0, MAX_UNRESOLVED_DIRECTORIES)), now, coverage]);
|
|
111
121
|
}
|
|
112
122
|
/** A host's latest unresolved directories, without creating a store or table. */
|
|
113
123
|
export async function readUnresolvedDirectories(host) {
|
|
@@ -108,7 +108,7 @@ export function pruneTraces(config, now = new Date()) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
catch (error) {
|
|
111
|
-
log("Capture trace cleanup failed", { code: errorCode(error) });
|
|
111
|
+
log("Capture trace cleanup failed", { code: errorCode(error) }, "error");
|
|
112
112
|
}
|
|
113
113
|
return removed;
|
|
114
114
|
}
|
|
@@ -163,7 +163,7 @@ export function writeTraceEntry(record, diagnostics, config, now = new Date()) {
|
|
|
163
163
|
protectTracePath(file, 0o600);
|
|
164
164
|
}
|
|
165
165
|
catch (error) {
|
|
166
|
-
log("Capture trace write failed", { code: errorCode(error) });
|
|
166
|
+
log("Capture trace write failed", { code: errorCode(error) }, "error");
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
/**
|
|
@@ -179,7 +179,7 @@ export function emitCaptureAttempt(record, diagnostics, config, now = new Date()
|
|
|
179
179
|
}
|
|
180
180
|
void import("./capture-attempt-store.js")
|
|
181
181
|
.then(({ saveCaptureAttempt }) => saveCaptureAttempt(record, now.getTime()))
|
|
182
|
-
.catch((error) => log("Capture attempt store failed", { code: errorCode(error) }));
|
|
182
|
+
.catch((error) => log("Capture attempt store failed", { code: errorCode(error) }, "error"));
|
|
183
183
|
if (config.captureTrace === true) {
|
|
184
184
|
writeTraceEntry(record, diagnostics, config, now);
|
|
185
185
|
}
|
|
@@ -34,12 +34,12 @@ export async function queueFailedCapture(unit, error, config) {
|
|
|
34
34
|
sessionID: unit.hostSessionId,
|
|
35
35
|
reason: failure.reason,
|
|
36
36
|
httpStatus: failure.httpStatus ?? null,
|
|
37
|
-
});
|
|
37
|
+
}, "warn");
|
|
38
38
|
}
|
|
39
39
|
return result === "queued";
|
|
40
40
|
}
|
|
41
41
|
catch (queueError) {
|
|
42
|
-
log("Capture retry queue write failed", { code: errorCode(queueError) });
|
|
42
|
+
log("Capture retry queue write failed", { code: errorCode(queueError) }, "error");
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -96,7 +96,7 @@ export async function drainCaptureRetries(options) {
|
|
|
96
96
|
await options.onSettled?.(row.workUnit, outcome);
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|
|
99
|
-
log("Capture retry follow-up failed", { host, code: errorCode(error) });
|
|
99
|
+
log("Capture retry follow-up failed", { host, code: errorCode(error) }, "error");
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
if (result.captured + result.skipped + result.dropped > 0 || result.stopped) {
|
|
@@ -105,7 +105,7 @@ export async function drainCaptureRetries(options) {
|
|
|
105
105
|
return result;
|
|
106
106
|
}
|
|
107
107
|
catch (error) {
|
|
108
|
-
log("Capture retry pass failed", { host, code: errorCode(error) });
|
|
108
|
+
log("Capture retry pass failed", { host, code: errorCode(error) }, "error");
|
|
109
109
|
return { ...result, status: "error" };
|
|
110
110
|
}
|
|
111
111
|
finally {
|
|
@@ -125,7 +125,7 @@ export function startCaptureRetryDrain(host) {
|
|
|
125
125
|
const drain = registeredDrains.get(host);
|
|
126
126
|
if (!drain)
|
|
127
127
|
return;
|
|
128
|
-
void drain().catch((error) => log("Capture retry pass failed", { host, code: errorCode(error) }));
|
|
128
|
+
void drain().catch((error) => log("Capture retry pass failed", { host, code: errorCode(error) }, "error"));
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
131
|
* Make every queued turn of one host due now. Starts a pass when this process
|
|
@@ -93,7 +93,7 @@ export async function enqueueCaptureRetry(unit, failure, config, now = Date.now(
|
|
|
93
93
|
const json = JSON.stringify(cleaned);
|
|
94
94
|
const size = Buffer.byteLength(json, "utf8");
|
|
95
95
|
if (size > MAX_QUEUED_TURN_BYTES) {
|
|
96
|
-
log("Capture retry skipped: turn too large", { sessionID: unit.hostSessionId, size });
|
|
96
|
+
log("Capture retry skipped: turn too large", { sessionID: unit.hostSessionId, size }, "warn");
|
|
97
97
|
return "too-large";
|
|
98
98
|
}
|
|
99
99
|
const db = await database(config);
|
|
@@ -38,7 +38,7 @@ export async function routeChat(path, method, readBody, config) {
|
|
|
38
38
|
if (error instanceof ChatStoreError) {
|
|
39
39
|
return fail(STATUS_BY_CODE[error.code], error.code, error.message);
|
|
40
40
|
}
|
|
41
|
-
log("chat route: error", { path: path.replace(/\/[0-9a-f-]{36}/g, "/:id"), method });
|
|
41
|
+
log("chat route: error", { path: path.replace(/\/[0-9a-f-]{36}/g, "/:id"), method }, "error");
|
|
42
42
|
return fail(500, "internal", "Chat request failed");
|
|
43
43
|
}
|
|
44
44
|
}
|