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/README.md
CHANGED
|
@@ -106,10 +106,12 @@ Browse captured notes alongside example user input and technical outcomes.
|
|
|
106
106
|
|
|
107
107
|
### Memory
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
Pick a host card (Pi, OpenCode or Claude Code) to see its import status, sessions
|
|
110
|
+
and folders that need a map. The Shared card holds profile learning, memory limits
|
|
111
|
+
and other settings that apply to every host. The cards also warn you when memories
|
|
112
|
+
cannot be saved.
|
|
111
113
|
|
|
112
|
-

|
|
113
115
|
|
|
114
116
|
### Settings
|
|
115
117
|
|
|
@@ -119,18 +121,18 @@ Choose models and manage access. Diagnostics show capture outcomes and component
|
|
|
119
121
|
|
|
120
122
|
## Documentation
|
|
121
123
|
|
|
122
|
-
| Guide | Covers
|
|
123
|
-
| ------------------------------------------- |
|
|
124
|
-
| [Using memory](docs/using-memory.md) | Daily use, capture, recall and the user profile
|
|
125
|
-
| [Web UI](docs/web-ui.md) | Starting the app, browsing memories and safe network access
|
|
126
|
-
| [Memory page](docs/web-ui-memory.md) |
|
|
127
|
-
| [Settings page](docs/web-ui-settings.md) | Models, credentials, diagnostics and profile identities
|
|
128
|
-
| [Configuration](docs/configuration.md) | Config files, model selection, privacy and embeddings
|
|
129
|
-
| [Moving projects](docs/moving-projects.md) | Folder moves, backup and restore
|
|
130
|
-
| [Updating and upgrading](docs/upgrading.md) | Updates, version pinning and older stores
|
|
131
|
-
| [CLI reference](docs/cli.md) | Terminal commands
|
|
132
|
-
| [For developers](docs/developers.md) | Building, testing and architecture
|
|
133
|
-
| [Contributing](CONTRIBUTING.md) | Development workflow and pull requests
|
|
124
|
+
| Guide | Covers |
|
|
125
|
+
| ------------------------------------------- | ----------------------------------------------------------- |
|
|
126
|
+
| [Using memory](docs/using-memory.md) | Daily use, capture, recall and the user profile |
|
|
127
|
+
| [Web UI](docs/web-ui.md) | Starting the app, browsing memories and safe network access |
|
|
128
|
+
| [Memory page](docs/web-ui-memory.md) | One card per host, imports, shared settings and avatars |
|
|
129
|
+
| [Settings page](docs/web-ui-settings.md) | Models, credentials, diagnostics and profile identities |
|
|
130
|
+
| [Configuration](docs/configuration.md) | Config files, model selection, privacy and embeddings |
|
|
131
|
+
| [Moving projects](docs/moving-projects.md) | Folder moves, backup and restore |
|
|
132
|
+
| [Updating and upgrading](docs/upgrading.md) | Updates, version pinning and older stores |
|
|
133
|
+
| [CLI reference](docs/cli.md) | Terminal commands |
|
|
134
|
+
| [For developers](docs/developers.md) | Building, testing and architecture |
|
|
135
|
+
| [Contributing](CONTRIBUTING.md) | Development workflow and pull requests |
|
|
134
136
|
|
|
135
137
|
History import guides: [OpenCode](docs/opencode-history-import.md),
|
|
136
138
|
[Pi](docs/pi-history-import.md) and [Claude Code](docs/claude-code-history-import.md).
|
|
@@ -103,7 +103,7 @@ export async function generateOpenCodeAutoCaptureSummary(request, notify) {
|
|
|
103
103
|
diagnostics.retryAfterMs = errorRetryAfterMs(error);
|
|
104
104
|
log("auto-capture: opencode provider failed, falling back to external API", {
|
|
105
105
|
error: String(error),
|
|
106
|
-
});
|
|
106
|
+
}, "warn");
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
if (!CONFIG.memoryModel || !CONFIG.memoryApiUrl) {
|
|
@@ -61,7 +61,7 @@ export async function runOpencodeImportCommand(input) {
|
|
|
61
61
|
await memoryClient.warmup();
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
|
-
log("OpenCode import: memory warmup failed", { error: String(error) });
|
|
64
|
+
log("OpenCode import: memory warmup failed", { error: String(error) }, "error");
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
let lastProgress = 0;
|
|
@@ -81,7 +81,7 @@ export async function runOpencodeImportCommand(input) {
|
|
|
81
81
|
}
|
|
82
82
|
catch (error) {
|
|
83
83
|
const message = error instanceof Error ? error.message : String(error);
|
|
84
|
-
log("OpenCode history import failed", { error: message });
|
|
84
|
+
log("OpenCode history import failed", { error: message }, "error");
|
|
85
85
|
return `${name} failed: ${message}`;
|
|
86
86
|
}
|
|
87
87
|
finally {
|
|
@@ -24,7 +24,7 @@ export async function performUserProfileLearning(ctx, directory) {
|
|
|
24
24
|
if (!CONFIG.autoCaptureProviderStatus || !CONFIG.autoCaptureProviderStatus.ready) {
|
|
25
25
|
log("user-profile-learning: skipped (provider not ready)", {
|
|
26
26
|
issues: CONFIG.autoCaptureProviderStatus?.issues ?? ["status undefined"],
|
|
27
|
-
});
|
|
27
|
+
}, "warn");
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
30
|
isLearningRunning = true;
|
|
@@ -92,7 +92,7 @@ Rules:
|
|
|
92
92
|
log("user-profile-learning: validation skipped", {
|
|
93
93
|
prefCount: topPrefs.length,
|
|
94
94
|
reason: "needs ≥ 5 preferences",
|
|
95
|
-
});
|
|
95
|
+
}, "warn");
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
else if (existingProfile) {
|
|
@@ -139,7 +139,7 @@ Rules:
|
|
|
139
139
|
log("user-profile-learning: retry merge", {
|
|
140
140
|
retry: retries,
|
|
141
141
|
profileId: existingProfile.id,
|
|
142
|
-
});
|
|
142
|
+
}, "warn");
|
|
143
143
|
}
|
|
144
144
|
cleanupPreviousPromptCount = existingProfile.totalPromptsAnalyzed;
|
|
145
145
|
let changeSummary = generateChangeSummary(JSON.parse(existingProfile.profileData), updatedProfileData);
|
|
@@ -153,7 +153,7 @@ Rules:
|
|
|
153
153
|
profileId: existingProfile.id,
|
|
154
154
|
userId,
|
|
155
155
|
retry: retries,
|
|
156
|
-
});
|
|
156
|
+
}, "warn");
|
|
157
157
|
}
|
|
158
158
|
retries++;
|
|
159
159
|
}
|
|
@@ -397,7 +397,7 @@ async function applyValidations(profileData, llmResult, profileId, prefKeys) {
|
|
|
397
397
|
continue;
|
|
398
398
|
const item = allItems.find((i) => `${i.category || "_"}||${(i.description || "").substring(0, 30)}` === key);
|
|
399
399
|
if (!item) {
|
|
400
|
-
log("user-profile-learning: validation match failed", { index: v.index, key });
|
|
400
|
+
log("user-profile-learning: validation match failed", { index: v.index, key }, "warn");
|
|
401
401
|
continue;
|
|
402
402
|
}
|
|
403
403
|
if (v.verdict === "confirmed") {
|
|
@@ -509,7 +509,7 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
|
|
|
509
509
|
opencodeProviderError = e;
|
|
510
510
|
log("user-profile-learning: opencode provider failed, falling back to external API", {
|
|
511
511
|
error: String(e),
|
|
512
|
-
});
|
|
512
|
+
}, "warn");
|
|
513
513
|
}
|
|
514
514
|
}
|
|
515
515
|
if (!CONFIG.memoryModel || !CONFIG.memoryApiUrl) {
|
|
@@ -519,7 +519,7 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
|
|
|
519
519
|
log("User Profile Config Check Failed:", {
|
|
520
520
|
memoryModel: CONFIG.memoryModel,
|
|
521
521
|
memoryApiUrl: CONFIG.memoryApiUrl,
|
|
522
|
-
});
|
|
522
|
+
}, "error");
|
|
523
523
|
throw new Error("External API not configured for user memory learning");
|
|
524
524
|
}
|
|
525
525
|
const { AIProviderFactory } = await import("../../services/ai/ai-provider-factory.js");
|
|
@@ -594,7 +594,7 @@ If no clear chains, return { "paths": [] }.`;
|
|
|
594
594
|
}
|
|
595
595
|
}
|
|
596
596
|
catch (e) {
|
|
597
|
-
log("learning paths: native provider failed", { error: String(e) });
|
|
597
|
+
log("learning paths: native provider failed", { error: String(e) }, "warn");
|
|
598
598
|
}
|
|
599
599
|
}
|
|
600
600
|
if (!result && CONFIG.memoryModel && CONFIG.memoryApiUrl) {
|
|
@@ -624,7 +624,7 @@ If no clear chains, return { "paths": [] }.`;
|
|
|
624
624
|
}
|
|
625
625
|
}
|
|
626
626
|
catch (e) {
|
|
627
|
-
log("learning paths: external API failed", { error: String(e) });
|
|
627
|
+
log("learning paths: external API failed", { error: String(e) }, "error");
|
|
628
628
|
}
|
|
629
629
|
}
|
|
630
630
|
if (!result?.paths?.length)
|
|
@@ -109,7 +109,7 @@ export function registerOpencodeProfileModel() {
|
|
|
109
109
|
return null;
|
|
110
110
|
const v2Client = (await loadOpencodeProvider()).getV2Client();
|
|
111
111
|
if (!v2Client) {
|
|
112
|
-
log("profile model: opencode client unavailable, falling back to external API");
|
|
112
|
+
log("profile model: opencode client unavailable, falling back to external API", undefined, "warn");
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
115
115
|
return adaptOpencodeProfileModel(v2Client, hostModel);
|
|
@@ -20,7 +20,7 @@ async function captureWithQuickRetries(unit, provider) {
|
|
|
20
20
|
throw error;
|
|
21
21
|
log(`Pi auto-capture warning (attempt ${attempt}/${maxRetries})`, {
|
|
22
22
|
sessionID: unit.hostSessionId,
|
|
23
|
-
});
|
|
23
|
+
}, "warn");
|
|
24
24
|
await new Promise((resolve) => setTimeout(resolve, quickRetryDelayMs(attempt)));
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -95,7 +95,7 @@ export async function capturePiSettledWorkUnit(input) {
|
|
|
95
95
|
}
|
|
96
96
|
catch (error) {
|
|
97
97
|
const message = error instanceof Error ? error.message : String(error);
|
|
98
|
-
log(`Pi auto-capture error: ${message}`, { sessionID: input.sessionId });
|
|
98
|
+
log(`Pi auto-capture error: ${message}`, { sessionID: input.sessionId }, "error");
|
|
99
99
|
// The next prompt moves capture on to a newer turn, so keep this one for a later retry.
|
|
100
100
|
if (workUnit)
|
|
101
101
|
await queueFailedCapture(workUnit, error, CONFIG);
|
|
@@ -110,14 +110,14 @@ export default function ommsPiExtension(pi) {
|
|
|
110
110
|
// Record this copy first: the login item and the other hosts run the newest recorded copy.
|
|
111
111
|
void import("../../services/runtime-handoff.js")
|
|
112
112
|
.then(({ registerOwnCopy }) => registerOwnCopy())
|
|
113
|
-
.catch((error) => log("Pi runtime record failed", { code: error instanceof Error ? error.name : "unknown" }));
|
|
113
|
+
.catch((error) => log("Pi runtime record failed", { code: error instanceof Error ? error.name : "unknown" }, "error"));
|
|
114
114
|
if (CONFIG.webServerAutoStart !== undefined &&
|
|
115
115
|
process.env.OMMS_DISABLE_WEB_AUTOSTART !== "1") {
|
|
116
116
|
void import("../../services/web-autostart.js")
|
|
117
117
|
.then(({ reconcileWebAutostart }) => reconcileWebAutostart(CONFIG))
|
|
118
118
|
.catch((error) => log("Pi login item reconciliation failed", {
|
|
119
119
|
code: error instanceof Error ? error.name : "unknown",
|
|
120
|
-
}));
|
|
120
|
+
}, "error"));
|
|
121
121
|
}
|
|
122
122
|
if (process.env.OMMS_DISABLE_WEB_AUTOSTART !== "1") {
|
|
123
123
|
// Start the shared web app if none runs. The session does not wait for it.
|
|
@@ -141,7 +141,7 @@ export default function ommsPiExtension(pi) {
|
|
|
141
141
|
})
|
|
142
142
|
.catch((error) => log("Pi web app start failed", {
|
|
143
143
|
code: error instanceof Error ? error.name : "unknown",
|
|
144
|
-
}));
|
|
144
|
+
}, "error"));
|
|
145
145
|
}
|
|
146
146
|
// Retry turns whose capture failed earlier, with this session's capture model.
|
|
147
147
|
void import("../../services/capture-retry-drain.js")
|
|
@@ -196,7 +196,7 @@ export default function ommsPiExtension(pi) {
|
|
|
196
196
|
})()
|
|
197
197
|
.catch((error) => log("Pi backfill failed", {
|
|
198
198
|
error: error instanceof Error ? error.message : String(error),
|
|
199
|
-
}))
|
|
199
|
+
}, "error"))
|
|
200
200
|
.finally(() => {
|
|
201
201
|
if (globalScope[GLOBAL_PI_BACKFILL_KEY] === task) {
|
|
202
202
|
delete globalScope[GLOBAL_PI_BACKFILL_KEY];
|
|
@@ -211,7 +211,7 @@ export default function ommsPiExtension(pi) {
|
|
|
211
211
|
finishStatus(ctx, status, "connected");
|
|
212
212
|
}
|
|
213
213
|
catch (error) {
|
|
214
|
-
log("Pi plugin memory warmup failed", { error: String(error) });
|
|
214
|
+
log("Pi plugin memory warmup failed", { error: String(error) }, "error");
|
|
215
215
|
finishStatus(ctx, status, "error");
|
|
216
216
|
}
|
|
217
217
|
})();
|
|
@@ -221,7 +221,7 @@ export default function ommsPiExtension(pi) {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
catch (error) {
|
|
224
|
-
log("Pi session_start error", { error: String(error) });
|
|
224
|
+
log("Pi session_start error", { error: String(error) }, "error");
|
|
225
225
|
finishStatus(ctx, status, "error");
|
|
226
226
|
}
|
|
227
227
|
});
|
|
@@ -247,7 +247,7 @@ export default function ommsPiExtension(pi) {
|
|
|
247
247
|
};
|
|
248
248
|
}
|
|
249
249
|
catch (error) {
|
|
250
|
-
log("Pi before_agent_start retrieval error", { error: String(error) });
|
|
250
|
+
log("Pi before_agent_start retrieval error", { error: String(error) }, "error");
|
|
251
251
|
finishStatus(ctx, status, "error");
|
|
252
252
|
return undefined;
|
|
253
253
|
}
|
|
@@ -293,7 +293,7 @@ export default function ommsPiExtension(pi) {
|
|
|
293
293
|
finishStatus(ctx, status, captureResult.status === "failed" ? "error" : "connected");
|
|
294
294
|
}
|
|
295
295
|
catch (error) {
|
|
296
|
-
log("Pi agent_settled error", { error: String(error) });
|
|
296
|
+
log("Pi agent_settled error", { error: String(error) }, "error");
|
|
297
297
|
finishStatus(ctx, status, "error");
|
|
298
298
|
}
|
|
299
299
|
});
|
|
@@ -84,7 +84,7 @@ export function registerPiHistoryImportCommand(pi, getExtensionContext) {
|
|
|
84
84
|
await memoryClient.warmup();
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
87
|
-
log("Pi import: memory warmup failed", { error: String(error) });
|
|
87
|
+
log("Pi import: memory warmup failed", { error: String(error) }, "error");
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
let lastProgressNotify = 0;
|
|
@@ -109,7 +109,7 @@ export function registerPiHistoryImportCommand(pi, getExtensionContext) {
|
|
|
109
109
|
}
|
|
110
110
|
catch (error) {
|
|
111
111
|
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
-
log("Pi history import failed", { error: message });
|
|
112
|
+
log("Pi history import failed", { error: message }, "error");
|
|
113
113
|
notify(`memory-import-pi-history failed: ${message}`);
|
|
114
114
|
}
|
|
115
115
|
finally {
|
|
@@ -40,7 +40,7 @@ async function withFallback(primary, run, external, notify) {
|
|
|
40
40
|
if (!isExternalModelReady(CONFIG))
|
|
41
41
|
throw failure;
|
|
42
42
|
const reason = failure instanceof Error ? failure.message : String(failure);
|
|
43
|
-
log("Pi live model failed, falling back to external API", { error: reason });
|
|
43
|
+
log("Pi live model failed, falling back to external API", { error: reason }, "warn");
|
|
44
44
|
await Promise.resolve(notify?.({
|
|
45
45
|
title: "Using fallback provider",
|
|
46
46
|
message: `Pi model failed (${reason.length > 100 ? `${reason.slice(0, 100)}...` : reason}); using configured fallback.`,
|
|
@@ -65,7 +65,7 @@ export async function performPiProfileLearning(input) {
|
|
|
65
65
|
if (!tags.user.userEmail) {
|
|
66
66
|
log("pi profile learning: skipped (no user email resolved)", {
|
|
67
67
|
directory: input.directory,
|
|
68
|
-
});
|
|
68
|
+
}, "warn");
|
|
69
69
|
return;
|
|
70
70
|
}
|
|
71
71
|
const userId = tags.user.userEmail;
|
|
@@ -84,7 +84,7 @@ export async function performPiProfileLearning(input) {
|
|
|
84
84
|
const context = input.prompts.map((prompt, i) => `${i + 1}. ${prompt}`).join("\n");
|
|
85
85
|
const analysis = await analyzer.analyzeProfile(context, existing ? { id: existing.id, profileData: existing.profileData } : null);
|
|
86
86
|
if (!analysis) {
|
|
87
|
-
log("pi profile learning: skipped (no model available)", { userId });
|
|
87
|
+
log("pi profile learning: skipped (no model available)", { userId }, "warn");
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
if (existing && analysis.merged) {
|
package/dist/cli/web-power.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LogLevel } from "../services/logger.js";
|
|
1
2
|
import type { WebAutostartOptions } from "../services/web-autostart.js";
|
|
2
3
|
/** The two choices the page offers; the web server passes them through unchanged. */
|
|
3
4
|
export type PowerAction = "stop" | "restart";
|
|
@@ -51,7 +52,7 @@ export interface PowerDeps {
|
|
|
51
52
|
launcher: string | null;
|
|
52
53
|
cwd: string;
|
|
53
54
|
pid: number;
|
|
54
|
-
log: (message: string, data: Record<string, unknown
|
|
55
|
+
log: (message: string, data: Record<string, unknown>, level?: LogLevel) => void | Promise<void>;
|
|
55
56
|
}
|
|
56
57
|
/**
|
|
57
58
|
* The callback the web server runs after it replies to Stop or Restart.
|
package/dist/cli/web-power.js
CHANGED
|
@@ -79,7 +79,7 @@ function productionDeps(options) {
|
|
|
79
79
|
launcher: existsSync(launcherPath(ommsDir())) ? launcherPath(ommsDir()) : null,
|
|
80
80
|
cwd: homedir(),
|
|
81
81
|
pid: process.pid,
|
|
82
|
-
log: async (message, data) => (await import("../services/logger.js")).log(message, data),
|
|
82
|
+
log: async (message, data, level) => (await import("../services/logger.js")).log(message, data, level),
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
@@ -92,7 +92,7 @@ function productionDeps(options) {
|
|
|
92
92
|
*/
|
|
93
93
|
export function createPowerAction(options) {
|
|
94
94
|
const deps = { ...productionDeps(options.autostart ?? {}), ...options.deps };
|
|
95
|
-
const logFailure = (code) => Promise.resolve(deps.log("Web app restart failed", { code })).catch(() => undefined);
|
|
95
|
+
const logFailure = (code) => Promise.resolve(deps.log("Web app restart failed", { code }, "error")).catch(() => undefined);
|
|
96
96
|
let stopped = false;
|
|
97
97
|
/** Start the copy, check it, stop serving, and wait until the copy answers. */
|
|
98
98
|
async function handOff(args) {
|
package/dist/config.js
CHANGED
|
@@ -498,7 +498,7 @@ export function refreshConfigIfChanged(directory) {
|
|
|
498
498
|
// every unit. The last good directory and signature stay as they were.
|
|
499
499
|
log("Config reload failed; keeping the previous settings", {
|
|
500
500
|
error: error instanceof Error ? error.message : String(error),
|
|
501
|
-
});
|
|
501
|
+
}, "error");
|
|
502
502
|
lastFailedConfig = attempt;
|
|
503
503
|
}
|
|
504
504
|
}
|
|
@@ -42,7 +42,7 @@ export async function analyzeProfile(model, context, existingProfile, timeoutMs
|
|
|
42
42
|
log("pi profile analysis: model reply failed shared schema validation", {
|
|
43
43
|
provider: model.provider,
|
|
44
44
|
modelId: model.modelId,
|
|
45
|
-
});
|
|
45
|
+
}, "error");
|
|
46
46
|
throw new Error("pi profile analysis: invalid profile payload");
|
|
47
47
|
}
|
|
48
48
|
const raw = parsed.data;
|
|
@@ -22,7 +22,7 @@ export const profileBackoff = {
|
|
|
22
22
|
export function recordProfileFailure(host, message, error, now = Date.now()) {
|
|
23
23
|
const reason = profileFailureCode(error);
|
|
24
24
|
profileBackoff.recordFailure(now);
|
|
25
|
-
log(message, { host, reason });
|
|
25
|
+
log(message, { host, reason }, "error");
|
|
26
26
|
return reason;
|
|
27
27
|
}
|
|
28
28
|
let catchUpActive = false;
|
|
@@ -4,7 +4,7 @@ import { tryAcquireBackfillLock } from "./backfill-lock.js";
|
|
|
4
4
|
import { getBackfillCutoff, recordUnresolvedDirectories, unresolvedDirectoriesOf, unresolvedSessionCount, updateBackfillStatus, } from "../services/backfill-state.js";
|
|
5
5
|
import { runHistoryImport, } from "./run-import.js";
|
|
6
6
|
import { isManualImportRunning } from "./manual-import-guard.js";
|
|
7
|
-
import { isBackfillPaused } from "./import-runs.js";
|
|
7
|
+
import { isBackfillPaused, startImportRun } from "./import-runs.js";
|
|
8
8
|
import { workProgress } from "./import-progress.js";
|
|
9
9
|
const zero = () => ({ imported: 0, skipped: 0, failed: 0, pending: 0, unresolved: 0 });
|
|
10
10
|
function counts(report) {
|
|
@@ -79,15 +79,25 @@ export async function scheduleAutoBackfill(options) {
|
|
|
79
79
|
signal: controller.signal,
|
|
80
80
|
});
|
|
81
81
|
current = counts(dry);
|
|
82
|
-
|
|
82
|
+
// The dry run stops at the cutoff, so its list covers only history before it.
|
|
83
|
+
await recordUnresolvedDirectories(options.host, unresolvedDirectoriesOf(dry), clock(), "partial").catch((error) => log("Backfill unresolved directories write failed", {
|
|
83
84
|
host: options.host,
|
|
84
85
|
error: error instanceof Error ? error.message : String(error),
|
|
85
|
-
}));
|
|
86
|
+
}, "error"));
|
|
86
87
|
if (controller.signal.aborted) {
|
|
87
88
|
state = "stopped";
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
90
91
|
if (!current.pending) {
|
|
92
|
+
// A pressed Run now needs an answer on the page: record a finished run.
|
|
93
|
+
if (options.userStarted) {
|
|
94
|
+
await startImportRun(options.host, options.surface ?? "web")
|
|
95
|
+
.then((recorder) => recorder.finish("done", {}))
|
|
96
|
+
.catch((error) => log("Backfill run record write failed", {
|
|
97
|
+
host: options.host,
|
|
98
|
+
error: error instanceof Error ? error.message : String(error),
|
|
99
|
+
}, "error"));
|
|
100
|
+
}
|
|
91
101
|
state = "done";
|
|
92
102
|
return;
|
|
93
103
|
}
|
|
@@ -155,7 +165,7 @@ export async function scheduleAutoBackfill(options) {
|
|
|
155
165
|
log("Backfill status write failed", {
|
|
156
166
|
host: options.host,
|
|
157
167
|
error: error instanceof Error ? error.message : String(error),
|
|
158
|
-
});
|
|
168
|
+
}, "error");
|
|
159
169
|
});
|
|
160
170
|
},
|
|
161
171
|
});
|
|
@@ -126,7 +126,7 @@ export class BackfillControls {
|
|
|
126
126
|
.catch((error) => log(`${source} failed`, {
|
|
127
127
|
host,
|
|
128
128
|
error: error instanceof Error ? error.name : "unknown",
|
|
129
|
-
}))
|
|
129
|
+
}, "error"))
|
|
130
130
|
.finally(() => {
|
|
131
131
|
if (this.controllers.get(host) === controller)
|
|
132
132
|
this.controllers.delete(host);
|
|
@@ -136,7 +136,7 @@ function maybeStartBackfill(deps) {
|
|
|
136
136
|
backfillRequested = true;
|
|
137
137
|
void deps
|
|
138
138
|
.startBackfill()
|
|
139
|
-
.catch((error) => log("Claude Code backfill start failed", { code: errorCode(error) }));
|
|
139
|
+
.catch((error) => log("Claude Code backfill start failed", { code: errorCode(error) }, "error"));
|
|
140
140
|
}
|
|
141
141
|
async function retrievalSection(request, budgetBytes) {
|
|
142
142
|
// Every section this api returns is wrapped in the retrieval tag, so each
|
|
@@ -195,7 +195,7 @@ export async function handleClaudeRetrieve(body, deps = {}) {
|
|
|
195
195
|
event: request.event,
|
|
196
196
|
sessionID: request.sessionId,
|
|
197
197
|
code: errorCode(error),
|
|
198
|
-
});
|
|
198
|
+
}, "error");
|
|
199
199
|
return { additionalContext: "" };
|
|
200
200
|
}
|
|
201
201
|
}
|
|
@@ -218,7 +218,7 @@ export function handleClaudeCapture(body, deps = {}) {
|
|
|
218
218
|
const request = parseCaptureRequest(body);
|
|
219
219
|
captureChain = captureChain
|
|
220
220
|
.then(() => captureTurn(request, deps))
|
|
221
|
-
.catch((error) => log("Claude Code capture failed", { sessionID: request.sessionId, code: errorCode(error) }));
|
|
221
|
+
.catch((error) => log("Claude Code capture failed", { sessionID: request.sessionId, code: errorCode(error) }, "error"));
|
|
222
222
|
return { queued: true };
|
|
223
223
|
}
|
|
224
224
|
/** Resolves when every queued capture has finished. */
|
|
@@ -226,7 +226,7 @@ export function whenClaudeCaptureIdle() {
|
|
|
226
226
|
return captureChain;
|
|
227
227
|
}
|
|
228
228
|
function skipped(request, code, data = {}) {
|
|
229
|
-
log("Claude Code capture skipped", { sessionID: request.sessionId, code, ...data });
|
|
229
|
+
log("Claude Code capture skipped", { sessionID: request.sessionId, code, ...data }, "warn");
|
|
230
230
|
}
|
|
231
231
|
async function readTranscript(request) {
|
|
232
232
|
let text;
|
|
@@ -406,7 +406,7 @@ async function recordPrompt(request, unit, memoryId) {
|
|
|
406
406
|
log("Claude Code prompt record failed", {
|
|
407
407
|
sessionID: request.sessionId,
|
|
408
408
|
code: errorCode(error),
|
|
409
|
-
});
|
|
409
|
+
}, "error");
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
412
|
// --- Web app start ---
|
|
@@ -31,7 +31,7 @@ export async function learnClaudeProfile(directory, resolveModel) {
|
|
|
31
31
|
return;
|
|
32
32
|
const user = getTags(directory).user;
|
|
33
33
|
if (!user.userEmail) {
|
|
34
|
-
log("Claude Code profile learning skipped", { code: "no-user-email" });
|
|
34
|
+
log("Claude Code profile learning skipped", { code: "no-user-email" }, "warn");
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
37
|
const batch = await userPromptManager.getPromptsForUserLearning(interval, {
|
|
@@ -34,6 +34,8 @@ export interface ImportReadiness {
|
|
|
34
34
|
defaultRootFound: boolean;
|
|
35
35
|
modelChoices: ["external"];
|
|
36
36
|
};
|
|
37
|
+
/** Whether each host's default history source exists on this machine. */
|
|
38
|
+
defaultSourceFound: Record<ImportHost, boolean>;
|
|
37
39
|
}
|
|
38
40
|
export interface ReadinessDeps {
|
|
39
41
|
supportedProviders?: () => Promise<string[]>;
|
|
@@ -47,6 +49,7 @@ export interface ReadinessDeps {
|
|
|
47
49
|
}>;
|
|
48
50
|
loadPiSdk?: () => Promise<unknown>;
|
|
49
51
|
claudeRoot?: () => string;
|
|
52
|
+
sourceExists?: (path: string) => boolean;
|
|
50
53
|
}
|
|
51
54
|
/** Mirrors the checks in `selectImportModel`, without building a provider. */
|
|
52
55
|
export declare function externalApiState(supportedProviders?: () => Promise<string[]>): Promise<ExternalApiState>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { CONFIG } from "../config.js";
|
|
3
|
-
import { defaultClaudeSourcePath } from "./import-sources.js";
|
|
3
|
+
import { defaultClaudeSourcePath, defaultImportSourcePath } from "./import-sources.js";
|
|
4
4
|
const EXTERNAL_REASONS = {
|
|
5
5
|
"missing-model": "The external API has no memoryModel in the global config",
|
|
6
6
|
"missing-url": "The external API has no memoryApiUrl in the global config",
|
|
@@ -33,18 +33,25 @@ export async function importReadiness(deps = {}) {
|
|
|
33
33
|
reason: "The Pi SDK (@earendil-works/pi-coding-agent) was not found. Install Pi on this computer.",
|
|
34
34
|
})),
|
|
35
35
|
]);
|
|
36
|
+
const exists = deps.sourceExists ?? existsSync;
|
|
37
|
+
const claudeCode = claudeReadiness(deps.claudeRoot?.() ?? defaultClaudeSourcePath(CONFIG.claudeConfigDir), exists);
|
|
36
38
|
return {
|
|
37
39
|
external: { state, provider: CONFIG.memoryProvider, model: CONFIG.memoryModel ?? null },
|
|
38
40
|
opencode: { available: opencode.available, models: opencode.models ?? [] },
|
|
39
41
|
piReader,
|
|
40
|
-
claudeCode
|
|
42
|
+
claudeCode,
|
|
43
|
+
defaultSourceFound: {
|
|
44
|
+
pi: exists(defaultImportSourcePath("pi")),
|
|
45
|
+
opencode: exists(defaultImportSourcePath("opencode")),
|
|
46
|
+
"claude-code": claudeCode.defaultRootFound,
|
|
47
|
+
},
|
|
41
48
|
};
|
|
42
49
|
}
|
|
43
|
-
function claudeReadiness(defaultRoot) {
|
|
50
|
+
function claudeReadiness(defaultRoot, exists) {
|
|
44
51
|
return {
|
|
45
52
|
available: true,
|
|
46
53
|
defaultRoot,
|
|
47
|
-
defaultRootFound:
|
|
54
|
+
defaultRootFound: exists(defaultRoot),
|
|
48
55
|
modelChoices: ["external"],
|
|
49
56
|
};
|
|
50
57
|
}
|
|
@@ -72,7 +72,7 @@ export async function runHistoryImport(host, args, run) {
|
|
|
72
72
|
}, report.profile?.error ? profileFailureCode(report.profile.error) : undefined);
|
|
73
73
|
// Only a run over every project and every session sees the full list.
|
|
74
74
|
if (args.scope === "all-projects" && !run.selection)
|
|
75
|
-
await recordUnresolved(host, report);
|
|
75
|
+
await recordUnresolved(host, report, args.since !== undefined || args.until !== undefined);
|
|
76
76
|
return report;
|
|
77
77
|
}
|
|
78
78
|
catch (error) {
|
|
@@ -89,10 +89,11 @@ export async function runHistoryImport(host, args, run) {
|
|
|
89
89
|
await release?.();
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
async function recordUnresolved(host, report) {
|
|
92
|
+
async function recordUnresolved(host, report, dateLimited) {
|
|
93
93
|
try {
|
|
94
94
|
const { recordUnresolvedDirectories, unresolvedDirectoriesOf } = await import("../services/backfill-state.js");
|
|
95
|
-
|
|
95
|
+
// A run limited by date misses unresolved sessions outside it, so it must not replace a whole-history list.
|
|
96
|
+
await recordUnresolvedDirectories(host, unresolvedDirectoriesOf(report), Date.now(), dateLimited ? "partial" : "full");
|
|
96
97
|
}
|
|
97
98
|
catch {
|
|
98
99
|
// The list is a convenience for the Settings page; the import itself succeeded.
|