om-memory-system 3.4.0-next.23 → 3.4.0-next.27
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 +21 -7
- package/dist/adapters/opencode/auto-capture-summary.js +1 -1
- package/dist/adapters/opencode/backfill-models.d.ts +1 -1
- package/dist/adapters/opencode/backfill-models.js +6 -2
- package/dist/adapters/opencode/backfill-startup.d.ts +8 -0
- package/dist/adapters/opencode/backfill-startup.js +19 -0
- package/dist/adapters/opencode/import-command.d.ts +3 -3
- package/dist/adapters/opencode/import-command.js +4 -3
- package/dist/adapters/opencode/internal-capture-sessions.d.ts +4 -0
- package/dist/{services/ai → adapters/opencode}/internal-capture-sessions.js +1 -14
- package/dist/{services/ai → adapters/opencode}/opencode-import-models.js +1 -1
- package/dist/{services/user-memory-learning.d.ts → adapters/opencode/profile-learning.d.ts} +1 -1
- package/dist/{services/user-memory-learning.js → adapters/opencode/profile-learning.js} +14 -14
- package/dist/{services/ai → adapters/opencode}/profile-llm-client.js +1 -1
- package/dist/adapters/opencode/profile-model.d.ts +23 -0
- package/dist/adapters/opencode/profile-model.js +117 -0
- package/dist/adapters/opencode/user-prompt.d.ts +2 -1
- package/dist/adapters/opencode/user-prompt.js +3 -10
- package/dist/adapters/pi/backfill-models.d.ts +4 -0
- package/dist/adapters/pi/backfill-models.js +3 -0
- package/dist/adapters/pi/capture.d.ts +1 -1
- package/dist/adapters/pi/capture.js +1 -1
- package/dist/adapters/pi/extension.js +7 -0
- package/dist/adapters/pi/import-command.js +1 -0
- package/dist/cli/index.js +13 -1
- package/dist/config.d.ts +11 -4
- package/dist/config.js +29 -6
- package/dist/core/internal-prompt.d.ts +6 -0
- package/dist/core/internal-prompt.js +11 -0
- package/dist/core/profile-analysis.d.ts +3 -0
- package/dist/importer/auto-backfill.d.ts +5 -0
- package/dist/importer/auto-backfill.js +29 -10
- package/dist/importer/backfill-controls.d.ts +53 -0
- package/dist/importer/backfill-controls.js +126 -0
- package/dist/importer/backfill-lock.d.ts +1 -0
- package/dist/importer/backfill-lock.js +2 -2
- package/dist/importer/backfill-model.d.ts +1 -1
- package/dist/importer/backfill-model.js +2 -0
- package/dist/importer/external-api-test.d.ts +18 -0
- package/dist/importer/external-api-test.js +50 -0
- package/dist/importer/external-backfill-models.d.ts +15 -0
- package/dist/importer/external-backfill-models.js +14 -0
- package/dist/importer/import-path-maps.d.ts +14 -0
- package/dist/importer/import-path-maps.js +49 -0
- package/dist/importer/import-progress.d.ts +27 -0
- package/dist/importer/import-progress.js +38 -0
- package/dist/importer/import-readiness.js +1 -1
- package/dist/importer/import-runs.d.ts +43 -0
- package/dist/importer/import-runs.js +141 -0
- package/dist/importer/import-sessions.js +9 -9
- package/dist/importer/importer.d.ts +2 -1
- package/dist/importer/importer.js +2 -2
- package/dist/importer/map-suggestions.d.ts +39 -0
- package/dist/importer/map-suggestions.js +133 -0
- package/dist/importer/opencode-import.d.ts +2 -1
- package/dist/{services/ai/internal-capture-sessions.d.ts → importer/opencode-internal-sessions.d.ts} +0 -3
- package/dist/importer/opencode-internal-sessions.js +14 -0
- package/dist/importer/opencode-reader.js +1 -1
- package/dist/{adapters/pi/conversation.d.ts → importer/pi-conversation.d.ts} +1 -1
- package/dist/importer/profile-import.js +3 -2
- package/dist/importer/run-import.d.ts +21 -3
- package/dist/importer/run-import.js +82 -2
- package/dist/importer/session-loader.d.ts +1 -1
- package/dist/importer/settings-health.js +8 -1
- package/dist/{services → importer}/settings-models.d.ts +4 -5
- package/dist/{services → importer}/settings-models.js +24 -13
- package/dist/importer/web-import-api.d.ts +3 -0
- package/dist/importer/web-import-api.js +3 -0
- package/dist/importer/web-import-jobs.js +5 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +22 -9
- package/dist/services/ai/live-model-choice.d.ts +4 -1
- package/dist/services/ai/live-model-choice.js +18 -0
- package/dist/services/api-handlers.d.ts +6 -1
- package/dist/services/api-handlers.js +71 -62
- package/dist/services/backfill-state.d.ts +30 -0
- package/dist/services/backfill-state.js +47 -0
- package/dist/services/capture-diagnostics.d.ts +2 -8
- package/dist/services/capture-diagnostics.js +3 -49
- package/dist/services/global-config-writer.d.ts +2 -0
- package/dist/services/global-config-writer.js +37 -6
- package/dist/services/global-version.d.ts +31 -0
- package/dist/services/global-version.js +78 -0
- package/dist/services/memory-key-source.d.ts +35 -0
- package/dist/services/memory-key-source.js +123 -0
- package/dist/services/package-version.d.ts +2 -0
- package/dist/services/package-version.js +21 -0
- package/dist/services/private-path.d.ts +13 -0
- package/dist/services/private-path.js +54 -0
- package/dist/services/settings-snapshot.d.ts +17 -0
- package/dist/services/settings-snapshot.js +51 -14
- package/dist/services/turso/vector-search.d.ts +1 -1
- package/dist/services/turso/vector-search.js +5 -2
- package/dist/services/user-profile/ai-cleanup.d.ts +0 -16
- package/dist/services/user-profile/ai-cleanup.js +17 -91
- package/dist/services/user-profile/profile-model.d.ts +8 -0
- package/dist/services/user-profile/profile-model.js +31 -0
- package/dist/services/user-profile/user-profile-manager.d.ts +1 -1
- package/dist/services/user-profile/user-profile-manager.js +20 -74
- package/dist/services/web-server.d.ts +2 -0
- package/dist/services/web-server.js +75 -2
- package/dist/shared/api/schemas.d.ts +1 -1
- package/dist/v2/plugin.js +1 -1
- package/dist/web/assets/index-DvZhF1BJ.css +2 -0
- package/dist/web/assets/index-jqmX5i_i.js +124 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -1
- package/dist/web/assets/index-B-_gMSn9.js +0 -124
- package/dist/web/assets/index-D5ak5KsI.css +0 -2
- /package/dist/{services/ai → adapters/opencode}/opencode-diagnostics.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-diagnostics.js +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-host-config.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-host-config.js +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-import-models.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-provider-loader.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-provider-loader.js +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-provider.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-provider.js +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-sdk-client.d.ts +0 -0
- /package/dist/{services/ai → adapters/opencode}/opencode-sdk-client.js +0 -0
- /package/dist/{services/ai → adapters/opencode}/profile-llm-client.d.ts +0 -0
- /package/dist/{adapters/pi/conversation.js → importer/pi-conversation.js} +0 -0
|
@@ -26,7 +26,8 @@ export interface OpencodeImportOptions {
|
|
|
26
26
|
profileBatch?: number;
|
|
27
27
|
provider?: CaptureSummaryProvider;
|
|
28
28
|
profileModel?: ModelPort;
|
|
29
|
-
|
|
29
|
+
/** `alreadyHandled`: units found already done so far, which need no model call. */
|
|
30
|
+
onProgress?: (processed: number, total: number, promptPreview: string, alreadyHandled?: number) => void;
|
|
30
31
|
signal?: AbortSignal;
|
|
31
32
|
}
|
|
32
33
|
export interface OpencodeImportReport extends ImportReport {
|
package/dist/{services/ai/internal-capture-sessions.d.ts → importer/opencode-internal-sessions.d.ts}
RENAMED
|
@@ -3,6 +3,3 @@ export declare const INTERNAL_CAPTURE_SESSION_TITLE = "omms capture";
|
|
|
3
3
|
/** Every title omms has used for its own sessions (capture, profile learning, profile cleanup). */
|
|
4
4
|
export declare const INTERNAL_CAPTURE_SESSION_TITLES: readonly ["omms capture", "opencode-mem capture", "omms profile cleanup", "opencode-mem profile cleanup"];
|
|
5
5
|
export declare function isInternalCaptureSessionTitle(title: string | undefined | null): boolean;
|
|
6
|
-
export declare function trackInternalCaptureSession(sessionID: string): void;
|
|
7
|
-
export declare function untrackInternalCaptureSession(sessionID: string): void;
|
|
8
|
-
export declare function isTrackedInternalCaptureSession(sessionID: string): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Title used for transient structured-output sessions (capture / profile learning). */
|
|
2
|
+
export const INTERNAL_CAPTURE_SESSION_TITLE = "omms capture";
|
|
3
|
+
/** Title used by opencode-mem builds; sessions left by older versions stay internal. */
|
|
4
|
+
const LEGACY_INTERNAL_CAPTURE_SESSION_TITLE = "opencode-mem capture";
|
|
5
|
+
/** Every title omms has used for its own sessions (capture, profile learning, profile cleanup). */
|
|
6
|
+
export const INTERNAL_CAPTURE_SESSION_TITLES = [
|
|
7
|
+
INTERNAL_CAPTURE_SESSION_TITLE,
|
|
8
|
+
LEGACY_INTERNAL_CAPTURE_SESSION_TITLE,
|
|
9
|
+
"omms profile cleanup",
|
|
10
|
+
"opencode-mem profile cleanup",
|
|
11
|
+
];
|
|
12
|
+
export function isInternalCaptureSessionTitle(title) {
|
|
13
|
+
return (title === INTERNAL_CAPTURE_SESSION_TITLE || title === LEGACY_INTERNAL_CAPTURE_SESSION_TITLE);
|
|
14
|
+
}
|
|
@@ -3,7 +3,7 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { DatabaseSync } from "node:sqlite";
|
|
6
|
-
import { INTERNAL_CAPTURE_SESSION_TITLES } from "
|
|
6
|
+
import { INTERNAL_CAPTURE_SESSION_TITLES } from "./opencode-internal-sessions.js";
|
|
7
7
|
import { acquirePrivateSnapshot, opencodeSnapshots, OpencodeSnapshotError, } from "./opencode-snapshot.js";
|
|
8
8
|
export const DEFAULT_OPENCODE_DB = join(homedir(), ".local/share/opencode/opencode.db");
|
|
9
9
|
function validateSchema(db) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CaptureConversation } from "
|
|
1
|
+
import type { CaptureConversation } from "../core/host.js";
|
|
2
2
|
/**
|
|
3
3
|
* Minimal structural view of Pi session entries (see Pi session-format docs).
|
|
4
4
|
* Kept structural so the normaliser stays testable without importing Pi types.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isStructuredSummaryPromptMessage } from "../core/internal-prompt.js";
|
|
2
2
|
import { analyzeProfile } from "../core/profile-analysis.js";
|
|
3
3
|
import { isFullyPrivate, stripPrivateContent } from "../services/privacy.js";
|
|
4
4
|
import { getTags } from "../services/tags.js";
|
|
@@ -37,7 +37,8 @@ export async function importProfileFromHistory(source, options) {
|
|
|
37
37
|
const prompt = stripPrivateContent(unit.userPrompt).trim();
|
|
38
38
|
if (!prompt ||
|
|
39
39
|
isFullyPrivate(unit.userPrompt) ||
|
|
40
|
-
|
|
40
|
+
// OpenCode history already leaves out omms's own capture sessions by title.
|
|
41
|
+
isStructuredSummaryPromptMessage(prompt)) {
|
|
41
42
|
continue;
|
|
42
43
|
}
|
|
43
44
|
const key = buildImportKey(session.sessionId, unit, options.host);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { CaptureSummaryProvider } from "../core/host.js";
|
|
2
2
|
import type { ModelPort } from "../core/profile-analysis.js";
|
|
3
3
|
import type { HistoryImportArgs, ImportHost } from "./import-args.js";
|
|
4
|
-
import type { ImportReport } from "./importer.js";
|
|
4
|
+
import type { ImportPathMap, ImportReport } from "./importer.js";
|
|
5
5
|
import type { UnresolvedProject } from "./opencode-project.js";
|
|
6
|
+
import type { ImportSurface } from "./import-runs.js";
|
|
6
7
|
/** The two model roles an import uses; absent in a dry run or when the step is skipped. */
|
|
7
8
|
export interface HistoryImportModels {
|
|
8
9
|
capture?: CaptureSummaryProvider;
|
|
@@ -12,8 +13,17 @@ export interface HistoryImportRun {
|
|
|
12
13
|
/** Directory that relative paths and the default project resolve against. */
|
|
13
14
|
cwd: string;
|
|
14
15
|
models: HistoryImportModels;
|
|
15
|
-
|
|
16
|
+
/** `alreadyHandled`: units found already done so far, which need no model call. */
|
|
17
|
+
onProgress?: (processed: number, total: number, promptPreview: string, alreadyHandled?: number) => void;
|
|
16
18
|
signal?: AbortSignal;
|
|
19
|
+
/** Record progress and take the host's lock for a run that calls models. */
|
|
20
|
+
track?: {
|
|
21
|
+
surface: ImportSurface;
|
|
22
|
+
lockHeld?: boolean;
|
|
23
|
+
expectedTotal?: number;
|
|
24
|
+
};
|
|
25
|
+
/** Saved directory maps; defaults to the global `importPathMaps`. */
|
|
26
|
+
savedPathMaps?: readonly ImportPathMap[];
|
|
17
27
|
/** Web selection, already re-resolved against the source; `args.source` is its real path. */
|
|
18
28
|
selection?: {
|
|
19
29
|
keys: string[];
|
|
@@ -26,7 +36,15 @@ export type HistoryImportReport = ImportReport & {
|
|
|
26
36
|
childSessionsFolded?: number;
|
|
27
37
|
unresolvedProjects?: UnresolvedProject[];
|
|
28
38
|
};
|
|
29
|
-
/**
|
|
39
|
+
/** Another process or surface holds this host's import lock. */
|
|
40
|
+
export declare class ImportAlreadyRunningError extends Error {
|
|
41
|
+
constructor(host: ImportHost);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Run one host's history import with already-parsed, validated arguments.
|
|
45
|
+
* With `track`, a run that calls models takes the host's cross-process lock
|
|
46
|
+
* and records its progress; a dry run never does either.
|
|
47
|
+
*/
|
|
30
48
|
export declare function runHistoryImport(host: ImportHost, args: HistoryImportArgs, run: HistoryImportRun): Promise<HistoryImportReport>;
|
|
31
49
|
/** The same plain-text report for every host and surface. */
|
|
32
50
|
export declare function formatHistoryImportReport(host: ImportHost, report: HistoryImportReport, model?: string): string;
|
|
@@ -4,11 +4,91 @@ const dryRunCapture = {
|
|
|
4
4
|
throw new Error("A dry-run cannot call the capture model");
|
|
5
5
|
},
|
|
6
6
|
};
|
|
7
|
-
/**
|
|
7
|
+
/** Another process or surface holds this host's import lock. */
|
|
8
|
+
export class ImportAlreadyRunningError extends Error {
|
|
9
|
+
constructor(host) {
|
|
10
|
+
super(`${host === "pi" ? "A Pi" : "An OpenCode"} import is already running`);
|
|
11
|
+
this.name = "ImportAlreadyRunningError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Run one host's history import with already-parsed, validated arguments.
|
|
16
|
+
* With `track`, a run that calls models takes the host's cross-process lock
|
|
17
|
+
* and records its progress; a dry run never does either.
|
|
18
|
+
*/
|
|
8
19
|
export async function runHistoryImport(host, args, run) {
|
|
20
|
+
if (!run.track || args.dryRun)
|
|
21
|
+
return runUntracked(host, args, run);
|
|
22
|
+
let release = null;
|
|
23
|
+
if (!run.track.lockHeld) {
|
|
24
|
+
const { tryAcquireBackfillLock } = await import("./backfill-lock.js");
|
|
25
|
+
release = await tryAcquireBackfillLock(host);
|
|
26
|
+
if (!release)
|
|
27
|
+
throw new ImportAlreadyRunningError(host);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const { startImportRun } = await import("./import-runs.js");
|
|
31
|
+
const { workProgress } = await import("./import-progress.js");
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const onAbort = () => controller.abort();
|
|
34
|
+
run.signal?.addEventListener("abort", onAbort, { once: true });
|
|
35
|
+
if (run.signal?.aborted)
|
|
36
|
+
controller.abort();
|
|
37
|
+
const recorder = await startImportRun(host, run.track.surface, {
|
|
38
|
+
onPause: () => controller.abort(),
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const report = await runUntracked(host, args, {
|
|
42
|
+
...run,
|
|
43
|
+
signal: controller.signal,
|
|
44
|
+
onProgress: (processed, total, preview, alreadyHandled = 0) => {
|
|
45
|
+
// Units already in the ledger need no model call: leave them out of
|
|
46
|
+
// the progress so the bar and the time left describe real work.
|
|
47
|
+
const work = workProgress(processed, total, alreadyHandled, run.track.expectedTotal);
|
|
48
|
+
recorder.progress(work.done, work.total);
|
|
49
|
+
run.onProgress?.(processed, total, preview, alreadyHandled);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const handled = report.unitsImported + report.unitsSkipped + report.unitsFailed;
|
|
53
|
+
await recorder.finish(recorder.pauseRequested ? "paused" : controller.signal.aborted ? "stopped" : "done", {
|
|
54
|
+
// The total stays the one progress last reported; a real run has no
|
|
55
|
+
// "would import" count of its own.
|
|
56
|
+
done: handled,
|
|
57
|
+
imported: report.unitsImported,
|
|
58
|
+
skipped: report.unitsSkipped,
|
|
59
|
+
failed: report.unitsFailed,
|
|
60
|
+
});
|
|
61
|
+
await recordUnresolved(host, report);
|
|
62
|
+
return report;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
await recorder
|
|
66
|
+
.finish(recorder.pauseRequested ? "paused" : controller.signal.aborted ? "stopped" : "failed", {}, error)
|
|
67
|
+
.catch(() => { });
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
run.signal?.removeEventListener("abort", onAbort);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
await release?.();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function recordUnresolved(host, report) {
|
|
79
|
+
try {
|
|
80
|
+
const { recordUnresolvedDirectories, unresolvedDirectoriesOf } = await import("../services/backfill-state.js");
|
|
81
|
+
await recordUnresolvedDirectories(host, unresolvedDirectoriesOf(report));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// The list is a convenience for the Settings page; the import itself succeeded.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function runUntracked(host, args, run) {
|
|
9
88
|
const at = (path) => resolve(run.cwd, path);
|
|
10
89
|
const project = args.scope === "current-project" ? at(args.project ?? ".") : undefined;
|
|
11
|
-
const
|
|
90
|
+
const { runPathMaps } = await import("./import-path-maps.js");
|
|
91
|
+
const pathMaps = await runPathMaps(args.pathMaps, run.cwd, run.savedPathMaps);
|
|
12
92
|
const capture = run.models.capture ?? dryRunCapture;
|
|
13
93
|
if (host === "opencode") {
|
|
14
94
|
const { importOpencodeHistory } = await import("./opencode-import.js");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PiSessionEntry } from "
|
|
1
|
+
import type { PiSessionEntry } from "./pi-conversation.js";
|
|
2
2
|
/**
|
|
3
3
|
* Loads a Pi session file for import through Pi's own exported session model
|
|
4
4
|
* (`SessionManager.open`), which handles parsing and legacy-version migration
|
|
@@ -4,6 +4,13 @@ import { isLoopbackHost } from "../services/web-api-auth.js";
|
|
|
4
4
|
import { safeHealthError } from "../services/safe-health-error.js";
|
|
5
5
|
export { safeHealthError } from "../services/safe-health-error.js";
|
|
6
6
|
const PROBE = "Reply with a short acknowledgement.";
|
|
7
|
+
async function createOpencodeImportModels(ref, directory) {
|
|
8
|
+
const { getOpencodeHostModels } = await import("./backfill-controls.js");
|
|
9
|
+
const opencode = getOpencodeHostModels();
|
|
10
|
+
if (!opencode)
|
|
11
|
+
throw new Error("the OpenCode client is not ready; retry in a moment");
|
|
12
|
+
return opencode.createImportModels(ref, directory);
|
|
13
|
+
}
|
|
7
14
|
export function captureFailureHealth(byModel, byReason) {
|
|
8
15
|
const total = byModel.reduce((count, row) => count + row.total, 0);
|
|
9
16
|
const failed = byModel.reduce((count, row) => count + row.failed, 0);
|
|
@@ -93,7 +100,7 @@ export async function runSettingsHealth(input) {
|
|
|
93
100
|
if (ref?.modelID === "inherit")
|
|
94
101
|
throw new Error("A session model needs an active session to test");
|
|
95
102
|
const capture = ref
|
|
96
|
-
? (await
|
|
103
|
+
? (await createOpencodeImportModels(ref, input.directory)).capture
|
|
97
104
|
: (await import("./model-selection.js")).selectImportModel({}).capture;
|
|
98
105
|
await capture.summarize({
|
|
99
106
|
userPrompt: PROBE,
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import type { OpencodeClient } from "@opencode-ai/sdk/v2/client";
|
|
2
|
+
import type { SettingsModel } from "./backfill-controls.js";
|
|
3
|
+
/** Connected models from an OpenCode client; null when the client returns no list. */
|
|
4
|
+
export declare function listOpencodeClientModels(client: Pick<OpencodeClient, "provider">): Promise<SettingsModel[] | null>;
|
|
2
5
|
export declare function listOpencodeSettingsModels(client?: Pick<OpencodeClient, "provider">): Promise<{
|
|
3
6
|
available: false;
|
|
4
7
|
reason: string;
|
|
5
8
|
models?: undefined;
|
|
6
9
|
} | {
|
|
7
10
|
available: true;
|
|
8
|
-
models:
|
|
9
|
-
provider: string;
|
|
10
|
-
model: string;
|
|
11
|
-
name: string;
|
|
12
|
-
}[];
|
|
11
|
+
models: SettingsModel[];
|
|
13
12
|
reason?: undefined;
|
|
14
13
|
}>;
|
|
15
14
|
export declare function listPiSettingsModels(loadSdk?: () => Promise<typeof import("@earendil-works/pi-coding-agent")>): Promise<{
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
+
/** Connected models from an OpenCode client; null when the client returns no list. */
|
|
2
|
+
export async function listOpencodeClientModels(client) {
|
|
3
|
+
const result = await client.provider.list();
|
|
4
|
+
if (!result.data)
|
|
5
|
+
return null;
|
|
6
|
+
const connected = new Set(result.data.connected);
|
|
7
|
+
return result.data.all
|
|
8
|
+
.filter((provider) => connected.has(provider.id))
|
|
9
|
+
.flatMap((provider) => Object.entries(provider.models).map(([model, info]) => ({
|
|
10
|
+
provider: provider.id,
|
|
11
|
+
model,
|
|
12
|
+
name: info.name,
|
|
13
|
+
})));
|
|
14
|
+
}
|
|
1
15
|
export async function listOpencodeSettingsModels(client) {
|
|
2
16
|
try {
|
|
3
|
-
|
|
4
|
-
if (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
17
|
+
let models;
|
|
18
|
+
if (client) {
|
|
19
|
+
models = await listOpencodeClientModels(client);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const { getOpencodeHostModels } = await import("./backfill-controls.js");
|
|
23
|
+
const opencode = getOpencodeHostModels();
|
|
24
|
+
models = opencode ? await opencode.listSettingsModels() : null;
|
|
25
|
+
}
|
|
26
|
+
if (!models)
|
|
8
27
|
return { available: false, reason: "OpenCode model list unavailable" };
|
|
9
|
-
const connected = new Set(result.data.connected);
|
|
10
|
-
const models = result.data.all
|
|
11
|
-
.filter((provider) => connected.has(provider.id))
|
|
12
|
-
.flatMap((provider) => Object.entries(provider.models).map(([model, info]) => ({
|
|
13
|
-
provider: provider.id,
|
|
14
|
-
model,
|
|
15
|
-
name: info.name,
|
|
16
|
-
})));
|
|
17
28
|
return { available: true, models };
|
|
18
29
|
}
|
|
19
30
|
catch {
|
|
@@ -6,3 +6,6 @@ export { importReadiness } from "./import-readiness.js";
|
|
|
6
6
|
export { browseImportSources, validateImportSource } from "./import-sources.js";
|
|
7
7
|
export { listImportSessions, validateSessionListRequest } from "./import-sessions.js";
|
|
8
8
|
export { opencodeSnapshots, sweepOrphanSnapshots } from "./opencode-snapshot.js";
|
|
9
|
+
export { BackfillControls } from "./backfill-controls.js";
|
|
10
|
+
export { directoryMapsView } from "./map-suggestions.js";
|
|
11
|
+
export { testExternalApi } from "./external-api-test.js";
|
|
@@ -6,3 +6,6 @@ export { importReadiness } from "./import-readiness.js";
|
|
|
6
6
|
export { browseImportSources, validateImportSource } from "./import-sources.js";
|
|
7
7
|
export { listImportSessions, validateSessionListRequest } from "./import-sessions.js";
|
|
8
8
|
export { opencodeSnapshots, sweepOrphanSnapshots } from "./opencode-snapshot.js";
|
|
9
|
+
export { BackfillControls } from "./backfill-controls.js";
|
|
10
|
+
export { directoryMapsView } from "./map-suggestions.js";
|
|
11
|
+
export { testExternalApi } from "./external-api-test.js";
|
|
@@ -173,12 +173,11 @@ export class SettingsImportJobs {
|
|
|
173
173
|
}
|
|
174
174
|
else {
|
|
175
175
|
const [providerID = "", ...id] = input.modelChoice.split("/");
|
|
176
|
-
const {
|
|
177
|
-
const
|
|
178
|
-
if (!isProviderConnected(providerID))
|
|
176
|
+
const { getOpencodeHostModels } = await import("./backfill-controls.js");
|
|
177
|
+
const opencode = getOpencodeHostModels();
|
|
178
|
+
if (!opencode?.isProviderConnected(providerID))
|
|
179
179
|
throw new Error("OpenCode provider is not connected");
|
|
180
|
-
|
|
181
|
-
models = await createOpencodeImportModels({ providerID, modelID: id.join("/") }, directory);
|
|
180
|
+
models = await opencode.createImportModels({ providerID, modelID: id.join("/") }, directory);
|
|
182
181
|
}
|
|
183
182
|
const { memoryClient } = await import("../services/client.js");
|
|
184
183
|
await memoryClient.warmup();
|
|
@@ -187,6 +186,7 @@ export class SettingsImportJobs {
|
|
|
187
186
|
cwd: directory,
|
|
188
187
|
models,
|
|
189
188
|
signal: controller.signal,
|
|
189
|
+
track: { surface: "web" },
|
|
190
190
|
selection: {
|
|
191
191
|
keys: selection.keys,
|
|
192
192
|
cutoff: selection.cutoff,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
2
2
|
import { isStructuredSummaryPromptMessage } from "./adapters/opencode/user-prompt.js";
|
|
3
|
-
import { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle } from "./
|
|
3
|
+
import { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle } from "./adapters/opencode/internal-capture-sessions.js";
|
|
4
4
|
export { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle };
|
|
5
5
|
export { isStructuredSummaryPromptMessage };
|
|
6
6
|
/**
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { isInternalPrompt, isStructuredSummaryPromptMessage, recordUserPrompt, }
|
|
|
8
8
|
import { executeMemoryOperation } from "./core/memory-operations.js";
|
|
9
9
|
import { OPENCODE_IMPORT_COMMAND, OPENCODE_IMPORT_DESCRIPTION, latestUserMessageModel, runOpencodeImportCommand, } from "./adapters/opencode/import-command.js";
|
|
10
10
|
import { formatMemoriesForCompaction } from "./core/retrieval.js";
|
|
11
|
-
import { performUserProfileLearning } from "./
|
|
11
|
+
import { performUserProfileLearning } from "./adapters/opencode/profile-learning.js";
|
|
12
12
|
import { userPromptManager } from "./services/user-prompt/user-prompt-manager.js";
|
|
13
13
|
import { startWebServer, WebServer } from "./services/web-server.js";
|
|
14
14
|
import { pruneTraces } from "./services/capture-diagnostics.js";
|
|
@@ -19,10 +19,11 @@ import { isConfigured, CONFIG, initConfigWithLegacyMigration } from "./config.js
|
|
|
19
19
|
import { resolveOpencodeHostModel } from "./services/ai/live-model-choice.js";
|
|
20
20
|
import { log } from "./services/logger.js";
|
|
21
21
|
import { getLanguageName } from "./services/language-detector.js";
|
|
22
|
-
import { getHostClientConfig } from "./
|
|
23
|
-
import { loadOpencodeProvider } from "./
|
|
24
|
-
import { STRUCTURED_OUTPUT_AGENT, STRUCTURED_OUTPUT_TOOLS, } from "./
|
|
25
|
-
import { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle, isTrackedInternalCaptureSession, } from "./
|
|
22
|
+
import { getHostClientConfig } from "./adapters/opencode/opencode-host-config.js";
|
|
23
|
+
import { loadOpencodeProvider } from "./adapters/opencode/opencode-provider-loader.js";
|
|
24
|
+
import { STRUCTURED_OUTPUT_AGENT, STRUCTURED_OUTPUT_TOOLS, } from "./adapters/opencode/opencode-provider.js";
|
|
25
|
+
import { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle, isTrackedInternalCaptureSession, } from "./adapters/opencode/internal-capture-sessions.js";
|
|
26
|
+
import { registerOpencodeProfileModel } from "./adapters/opencode/profile-model.js";
|
|
26
27
|
export { INTERNAL_CAPTURE_SESSION_TITLE, isInternalCaptureSessionTitle };
|
|
27
28
|
export { isStructuredSummaryPromptMessage };
|
|
28
29
|
function extractSessionTitle(response) {
|
|
@@ -209,6 +210,13 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
209
210
|
})();
|
|
210
211
|
}
|
|
211
212
|
await configureOpencodeHostTransport(ctx);
|
|
213
|
+
// Shared profile dedup, conflict, description, and cleanup calls use OpenCode's host model.
|
|
214
|
+
registerOpencodeProfileModel();
|
|
215
|
+
// Web imports, Health, and Settings served by this process can list and use OpenCode's models.
|
|
216
|
+
// Awaited so the web server never serves those routes before the registry is filled.
|
|
217
|
+
await import("./adapters/opencode/backfill-startup.js")
|
|
218
|
+
.then(({ registerOpencodeImportModels }) => registerOpencodeImportModels())
|
|
219
|
+
.catch(() => { });
|
|
212
220
|
(async () => {
|
|
213
221
|
let connected = [];
|
|
214
222
|
try {
|
|
@@ -231,15 +239,20 @@ export const OmmsPlugin = async (ctx) => {
|
|
|
231
239
|
catch (error) {
|
|
232
240
|
log("Failed to initialize opencode provider state", { error: String(error) });
|
|
233
241
|
}
|
|
242
|
+
const configModel = async () => {
|
|
243
|
+
const response = await ctx.client.config.get();
|
|
244
|
+
return unwrapSdkData(response)?.model ?? null;
|
|
245
|
+
};
|
|
246
|
+
// Run now on a Settings page served by this process can use OpenCode's signed-in models.
|
|
247
|
+
void import("./adapters/opencode/backfill-startup.js")
|
|
248
|
+
.then(({ registerOpencodeBackfillModels }) => registerOpencodeBackfillModels({ connected, directory, configModel }))
|
|
249
|
+
.catch(() => { });
|
|
234
250
|
if (CONFIG.autoBackfill && process.env.OMMS_DISABLE_AUTO_BACKFILL !== "1" && isConfigured()) {
|
|
235
251
|
void import("./adapters/opencode/backfill-startup.js")
|
|
236
252
|
.then(({ startOpencodeBackfill }) => startOpencodeBackfill({
|
|
237
253
|
connected,
|
|
238
254
|
directory,
|
|
239
|
-
configModel
|
|
240
|
-
const response = await ctx.client.config.get();
|
|
241
|
-
return unwrapSdkData(response)?.model ?? null;
|
|
242
|
-
},
|
|
255
|
+
configModel,
|
|
243
256
|
notify: (message) => {
|
|
244
257
|
void ctx.client.tui
|
|
245
258
|
?.showToast({
|
|
@@ -14,7 +14,8 @@ export interface AutoCaptureProviderRuntimeConfig {
|
|
|
14
14
|
/**
|
|
15
15
|
* Where live capture and profile learning send model calls, the same on both hosts:
|
|
16
16
|
* - `opencode`: the configured host model (`opencodeModel`/`piModel`; "inherit" = session model)
|
|
17
|
-
* - `manual`:
|
|
17
|
+
* - `manual`: the external API (`memoryModel`/`memoryApiUrl`/`memoryApiKey`), because the
|
|
18
|
+
* host model is "external" or no host model is set
|
|
18
19
|
* - `session`: neither is set, so the session's own model through the host's sign-in
|
|
19
20
|
*/
|
|
20
21
|
export type AutoCaptureProviderStatus = {
|
|
@@ -50,3 +51,5 @@ export declare function resolvePiLiveModel(config: AutoCaptureProviderRuntimeCon
|
|
|
50
51
|
}): PiLiveModelChoice;
|
|
51
52
|
/** True when the external API is fully configured and can serve as a fallback. */
|
|
52
53
|
export declare function isExternalModelReady(config: AutoCaptureProviderRuntimeConfig): boolean;
|
|
54
|
+
/** Why the external API cannot be used as a host's chosen model, or an empty list. */
|
|
55
|
+
export declare function externalModelIssues(config: AutoCaptureProviderRuntimeConfig): string[];
|
|
@@ -33,12 +33,21 @@ function getManualProviderStatus(config) {
|
|
|
33
33
|
issues,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
/** The host chose the external API: it is the primary call, so there is no fallback. */
|
|
37
|
+
function externalChoiceStatus(config) {
|
|
38
|
+
const manual = getManualProviderStatus(config);
|
|
39
|
+
return manual.ready
|
|
40
|
+
? { ready: true, mode: "manual", issues: [] }
|
|
41
|
+
: { ready: false, issues: manual.issues };
|
|
42
|
+
}
|
|
36
43
|
export function getAutoCaptureProviderStatus(config) {
|
|
37
44
|
const hasOpencodeProvider = hasValue(config.opencodeProvider);
|
|
38
45
|
const hasOpencodeModel = hasValue(config.opencodeModel);
|
|
39
46
|
if (config.opencodeModel?.trim() === "inherit") {
|
|
40
47
|
return { ready: true, mode: "session", issues: [] };
|
|
41
48
|
}
|
|
49
|
+
if (config.opencodeModel?.trim() === "external")
|
|
50
|
+
return externalChoiceStatus(config);
|
|
42
51
|
if (hasOpencodeProvider && hasOpencodeModel) {
|
|
43
52
|
return { ready: true, mode: "opencode", issues: [] };
|
|
44
53
|
}
|
|
@@ -70,6 +79,10 @@ export function resolveOpencodeHostModel(config) {
|
|
|
70
79
|
export function resolvePiLiveModel(config) {
|
|
71
80
|
if (config.piModel?.trim() === "inherit")
|
|
72
81
|
return { kind: "session" };
|
|
82
|
+
if (config.piModel?.trim() === "external") {
|
|
83
|
+
const status = externalChoiceStatus(config);
|
|
84
|
+
return status.ready ? { kind: "manual" } : { kind: "unready", issues: status.issues };
|
|
85
|
+
}
|
|
73
86
|
if (hasValue(config.piProvider) && hasValue(config.piModel)) {
|
|
74
87
|
return { kind: "pi", provider: config.piProvider.trim(), model: config.piModel.trim() };
|
|
75
88
|
}
|
|
@@ -84,3 +97,8 @@ export function resolvePiLiveModel(config) {
|
|
|
84
97
|
export function isExternalModelReady(config) {
|
|
85
98
|
return getManualProviderStatus(config).ready;
|
|
86
99
|
}
|
|
100
|
+
/** Why the external API cannot be used as a host's chosen model, or an empty list. */
|
|
101
|
+
export function externalModelIssues(config) {
|
|
102
|
+
const manual = getManualProviderStatus(config);
|
|
103
|
+
return manual.ready ? [] : manual.issues;
|
|
104
|
+
}
|
|
@@ -41,7 +41,7 @@ interface PaginatedResponse<T> {
|
|
|
41
41
|
export declare function handleListTags(): Promise<ApiResponse<{
|
|
42
42
|
project: TagInfo[];
|
|
43
43
|
}>>;
|
|
44
|
-
export declare function handleListMemories(tag?: string, page?: number, pageSize?: number, includePrompts?: boolean): Promise<ApiResponse<PaginatedResponse<Memory | any>>>;
|
|
44
|
+
export declare function handleListMemories(tag?: string, page?: number, pageSize?: number, includePrompts?: boolean, keyword?: string): Promise<ApiResponse<PaginatedResponse<Memory | any>>>;
|
|
45
45
|
export declare function handleAddMemory(data: {
|
|
46
46
|
content: string;
|
|
47
47
|
containerTag: string;
|
|
@@ -178,6 +178,11 @@ interface MigrationProgress {
|
|
|
178
178
|
errors: string[];
|
|
179
179
|
}
|
|
180
180
|
export declare function handleGetTagMigrationProgress(): Promise<ApiResponse<MigrationProgress>>;
|
|
181
|
+
/**
|
|
182
|
+
* Tag the memories that have no tags, a few per request. Only untagged
|
|
183
|
+
* memories are touched: tagged ones keep their vectors. A memory that fails
|
|
184
|
+
* is recorded and passed over, so one bad row cannot stall the run.
|
|
185
|
+
*/
|
|
181
186
|
export declare function handleRunTagMigrationBatch(batchSize?: number): Promise<ApiResponse<{
|
|
182
187
|
processed: number;
|
|
183
188
|
total: number;
|