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
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# OMMS
|
|
1
|
+
# OMMS: Opinionated Modular Memory System
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/om-memory-system)
|
|
4
4
|
[](https://www.npmjs.com/package/om-memory-system)
|
|
@@ -26,7 +26,8 @@ the other. Everything is stored locally on your machine.
|
|
|
26
26
|
OpenCode v1.
|
|
27
27
|
- **Learns your preferences.** A user profile of your habits builds up over
|
|
28
28
|
time and follows you across projects.
|
|
29
|
-
- **Imports your past sessions.** Pi and OpenCode import older history after
|
|
29
|
+
- **Imports your past sessions.** Pi and OpenCode import older history after
|
|
30
|
+
startup. A command gives you a preview and manual control.
|
|
30
31
|
- **Lets you look and edit.** A local web page shows every memory and your
|
|
31
32
|
profile.
|
|
32
33
|
- **Keeps private things private.** Text inside `<private>` tags is never
|
|
@@ -74,6 +75,14 @@ pi install npm:om-memory-system
|
|
|
74
75
|
|
|
75
76
|
Restart Pi. You can install OMMS in both agents; they share the same memory.
|
|
76
77
|
|
|
78
|
+
**Terminal command (optional, recommended).** A global install lets the login
|
|
79
|
+
web app and the `om-memory-system` terminal commands run without `npx`:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
npm i -g om-memory-system # or: bun add -g om-memory-system
|
|
83
|
+
om-memory-system --version
|
|
84
|
+
```
|
|
85
|
+
|
|
77
86
|
### 2. Choose which model writes memories (optional)
|
|
78
87
|
|
|
79
88
|
With no settings, OMMS uses the model of the session you are working in. To
|
|
@@ -91,18 +100,22 @@ config file yet, OMMS creates this one with comments on first start.
|
|
|
91
100
|
}
|
|
92
101
|
```
|
|
93
102
|
|
|
94
|
-
You can also use any OpenAI-compatible or Anthropic API with your own key
|
|
95
|
-
|
|
103
|
+
You can also use any OpenAI-compatible or Anthropic API with your own key, set
|
|
104
|
+
up on the Settings page's **External API** card, and choose `"external"` as a
|
|
105
|
+
host's model. See [Configuration](docs/configuration.md#choosing-the-model).
|
|
96
106
|
|
|
97
107
|
### 3. Check it works
|
|
98
108
|
|
|
99
109
|
Work normally for a few turns, then open `http://127.0.0.1:4747` in your
|
|
100
|
-
browser. OpenCode or the login web app serves this page. New memories appear
|
|
101
|
-
You can also ask the agent: "search memory for what we changed today".
|
|
110
|
+
browser. OpenCode or the login web app serves this page. New memories appear
|
|
111
|
+
on the timeline. You can also ask the agent: "search memory for what we changed today".
|
|
102
112
|
|
|
103
113
|
## Import your past history
|
|
104
114
|
|
|
105
|
-
Older sessions import automatically after a host starts. This makes model
|
|
115
|
+
Older sessions import automatically after a host starts. This makes model
|
|
116
|
+
calls. To turn it off, set `"autoBackfill": false` in
|
|
117
|
+
`~/.config/omms/omms.jsonc` before you start the agent. For a manual import or
|
|
118
|
+
a custom source, preview it inside the agent:
|
|
106
119
|
|
|
107
120
|
```text
|
|
108
121
|
/memory-import-opencode-history --dry-run
|
|
@@ -134,6 +147,7 @@ backup first. See [Updating and upgrading](docs/upgrading.md) and
|
|
|
134
147
|
| [Using memory day to day](docs/using-memory.md) | How capture and recall work, the `memory` tool, the user profile |
|
|
135
148
|
| [Configuration](docs/configuration.md) | Settings, choosing the model, embeddings, troubleshooting |
|
|
136
149
|
| [Web UI](docs/web-ui.md) | The memory explorer, opening it on a network safely |
|
|
150
|
+
| [Settings page](docs/web-ui-settings.md) | Every card and control on the web Settings page |
|
|
137
151
|
| [Moving projects](docs/moving-projects.md) | Nested repositories, moved folders, backup and restore |
|
|
138
152
|
| [Updating and upgrading](docs/upgrading.md) | Updates, pinning a version, older stores |
|
|
139
153
|
| [OpenCode adapter](docs/opencode-adapter.md) | How the OpenCode plugin hooks in |
|
|
@@ -4,7 +4,7 @@ import { resolveOpencodeHostModel } from "../../services/ai/live-model-choice.js
|
|
|
4
4
|
import { buildBoundedSummaryPrompt } from "../../core/capture-context.js";
|
|
5
5
|
import { classifyCaptureReply, normalizeStopReason, parseCaptureSummary, } from "../../core/extraction.js";
|
|
6
6
|
import { log } from "../../services/logger.js";
|
|
7
|
-
import { loadOpencodeProvider } from "
|
|
7
|
+
import { loadOpencodeProvider } from "./opencode-provider-loader.js";
|
|
8
8
|
function buildSystemPrompt(languageName) {
|
|
9
9
|
return `You are a technical memory recorder for a software development project.
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { selectImportModel } from "../../importer/model-selection.js";
|
|
2
2
|
import type { HistoryImportModels } from "../../importer/run-import.js";
|
|
3
|
-
import { createOpencodeImportModels } from "
|
|
3
|
+
import { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
4
4
|
interface BackfillConfig {
|
|
5
5
|
opencodeBackfillModel?: string;
|
|
6
6
|
opencodeProvider?: string;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { CONFIG } from "../../config.js";
|
|
2
2
|
import { parseBackfillModel } from "../../importer/backfill-model.js";
|
|
3
|
+
import { resolveExternalBackfillModels } from "../../importer/external-backfill-models.js";
|
|
3
4
|
import { selectImportModel } from "../../importer/model-selection.js";
|
|
4
5
|
import { getAutoCaptureProviderStatus } from "../../services/ai/live-model-choice.js";
|
|
5
|
-
import { createOpencodeImportModels
|
|
6
|
+
import { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
6
7
|
/** Apply the live-model rule when inherit is selected; named models need sign-in. */
|
|
7
8
|
export async function resolveOpencodeBackfillModels(config = CONFIG, context) {
|
|
8
9
|
const choice = parseBackfillModel(config, "opencode");
|
|
10
|
+
if (choice === "external") {
|
|
11
|
+
return resolveExternalBackfillModels("opencode", config, context.externalModels);
|
|
12
|
+
}
|
|
9
13
|
let ref;
|
|
10
14
|
if (choice === "inherit") {
|
|
11
15
|
const live = getAutoCaptureProviderStatus(config);
|
|
@@ -24,7 +28,7 @@ export async function resolveOpencodeBackfillModels(config = CONFIG, context) {
|
|
|
24
28
|
if (!selected)
|
|
25
29
|
throw new Error("OpenCode backfill: no configured default model");
|
|
26
30
|
const parsed = parseBackfillModel({ opencodeBackfillModel: selected }, "opencode");
|
|
27
|
-
if (parsed === "
|
|
31
|
+
if (typeof parsed === "string")
|
|
28
32
|
throw new Error("OpenCode backfill: no configured default model");
|
|
29
33
|
ref = { providerID: parsed.provider, modelID: parsed.model };
|
|
30
34
|
}
|
|
@@ -5,3 +5,11 @@ export declare function startOpencodeBackfill(input: {
|
|
|
5
5
|
configModel: () => Promise<string | null>;
|
|
6
6
|
notify: (message: string) => void;
|
|
7
7
|
}): Promise<void>;
|
|
8
|
+
/** Let Run now on a Settings page served by this process use OpenCode's models. */
|
|
9
|
+
export declare function registerOpencodeBackfillModels(input: {
|
|
10
|
+
directory: string;
|
|
11
|
+
connected: string[];
|
|
12
|
+
configModel: () => Promise<string | null>;
|
|
13
|
+
}): void;
|
|
14
|
+
/** Let web imports, Health, and Settings in this process use OpenCode's signed-in models. */
|
|
15
|
+
export declare function registerOpencodeImportModels(): void;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { CONFIG } from "../../config.js";
|
|
2
2
|
import { scheduleAutoBackfill } from "../../importer/auto-backfill.js";
|
|
3
|
+
import { registerHostBackfillModels, registerOpencodeHostModels, } from "../../importer/backfill-controls.js";
|
|
4
|
+
import { listOpencodeClientModels } from "../../importer/settings-models.js";
|
|
3
5
|
import { resolveOpencodeBackfillModels } from "./backfill-models.js";
|
|
6
|
+
import { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
7
|
+
import { getV2Client, isProviderConnected } from "./opencode-provider.js";
|
|
4
8
|
/** Start after connected providers load, without delaying plugin initialisation. */
|
|
5
9
|
export function startOpencodeBackfill(input) {
|
|
6
10
|
return scheduleAutoBackfill({
|
|
@@ -10,3 +14,18 @@ export function startOpencodeBackfill(input) {
|
|
|
10
14
|
notify: input.notify,
|
|
11
15
|
});
|
|
12
16
|
}
|
|
17
|
+
/** Let Run now on a Settings page served by this process use OpenCode's models. */
|
|
18
|
+
export function registerOpencodeBackfillModels(input) {
|
|
19
|
+
registerHostBackfillModels("opencode", () => resolveOpencodeBackfillModels(CONFIG, input));
|
|
20
|
+
}
|
|
21
|
+
/** Let web imports, Health, and Settings in this process use OpenCode's signed-in models. */
|
|
22
|
+
export function registerOpencodeImportModels() {
|
|
23
|
+
registerOpencodeHostModels({
|
|
24
|
+
isProviderConnected: (providerID) => isProviderConnected(providerID),
|
|
25
|
+
createImportModels: (ref, directory) => createOpencodeImportModels(ref, directory),
|
|
26
|
+
async listSettingsModels() {
|
|
27
|
+
const client = getV2Client();
|
|
28
|
+
return client ? listOpencodeClientModels(client) : null;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { type OpencodeModelRef } from "
|
|
2
|
-
export { createOpencodeImportModels } from "
|
|
3
|
-
export type { OpencodeModelRef } from "
|
|
1
|
+
import { type OpencodeModelRef } from "./opencode-import-models.js";
|
|
2
|
+
export { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
3
|
+
export type { OpencodeModelRef } from "./opencode-import-models.js";
|
|
4
4
|
/**
|
|
5
5
|
* OpenCode command surface for the historical-session importer:
|
|
6
6
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isConfigured } from "../../config.js";
|
|
2
2
|
import { historyImportUsage, importNeedsModel, parseHistoryImportArgs, tokenizeImportArgs, } from "../../importer/import-args.js";
|
|
3
3
|
import { formatHistoryImportReport, summarizeHistoryImportReport, runHistoryImport, } from "../../importer/run-import.js";
|
|
4
|
-
import { createOpencodeImportModels
|
|
5
|
-
export { createOpencodeImportModels } from "
|
|
6
|
-
import { loadOpencodeProvider } from "
|
|
4
|
+
import { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
5
|
+
export { createOpencodeImportModels } from "./opencode-import-models.js";
|
|
6
|
+
import { loadOpencodeProvider } from "./opencode-provider-loader.js";
|
|
7
7
|
import { beginManualImport } from "../../importer/manual-import-guard.js";
|
|
8
8
|
import { memoryClient } from "../../services/client.js";
|
|
9
9
|
import { log } from "../../services/logger.js";
|
|
@@ -68,6 +68,7 @@ export async function runOpencodeImportCommand(input) {
|
|
|
68
68
|
const report = await runHistoryImport("opencode", parsed, {
|
|
69
69
|
cwd: input.directory,
|
|
70
70
|
models,
|
|
71
|
+
track: { surface: "slash" },
|
|
71
72
|
onProgress: (processed, total, promptPreview) => {
|
|
72
73
|
if (processed - lastProgress >= 25 || processed === total) {
|
|
73
74
|
lastProgress = processed;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { INTERNAL_CAPTURE_SESSION_TITLE, INTERNAL_CAPTURE_SESSION_TITLES, isInternalCaptureSessionTitle, } from "../../importer/opencode-internal-sessions.js";
|
|
2
|
+
export declare function trackInternalCaptureSession(sessionID: string): void;
|
|
3
|
+
export declare function untrackInternalCaptureSession(sessionID: string): void;
|
|
4
|
+
export declare function isTrackedInternalCaptureSession(sessionID: string): boolean;
|
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
|
|
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";
|
|
1
|
+
export { INTERNAL_CAPTURE_SESSION_TITLE, INTERNAL_CAPTURE_SESSION_TITLES, isInternalCaptureSessionTitle, } from "../../importer/opencode-internal-sessions.js";
|
|
5
2
|
/** Grace period so session.idle can still match after best-effort delete. */
|
|
6
3
|
const UNTRACK_GRACE_MS = 60_000;
|
|
7
4
|
const trackedSessionIDs = new Set();
|
|
8
5
|
const untrackTimers = new Map();
|
|
9
|
-
/** Every title omms has used for its own sessions (capture, profile learning, profile cleanup). */
|
|
10
|
-
export const INTERNAL_CAPTURE_SESSION_TITLES = [
|
|
11
|
-
INTERNAL_CAPTURE_SESSION_TITLE,
|
|
12
|
-
LEGACY_INTERNAL_CAPTURE_SESSION_TITLE,
|
|
13
|
-
"omms profile cleanup",
|
|
14
|
-
"opencode-mem profile cleanup",
|
|
15
|
-
];
|
|
16
|
-
export function isInternalCaptureSessionTitle(title) {
|
|
17
|
-
return (title === INTERNAL_CAPTURE_SESSION_TITLE || title === LEGACY_INTERNAL_CAPTURE_SESSION_TITLE);
|
|
18
|
-
}
|
|
19
6
|
export function trackInternalCaptureSession(sessionID) {
|
|
20
7
|
const pending = untrackTimers.get(sessionID);
|
|
21
8
|
if (pending) {
|
|
@@ -9,7 +9,7 @@ export async function createOpencodeImportModels(ref, directory) {
|
|
|
9
9
|
throw new Error("the OpenCode client is not ready; retry in a moment");
|
|
10
10
|
const { z } = await import("zod");
|
|
11
11
|
const { buildCaptureSystemPrompt, createUserProfileAnalysisSchema, parseCaptureSummary } = await import("../../core/extraction.js");
|
|
12
|
-
const { detectLanguage, getLanguageName } = await import("
|
|
12
|
+
const { detectLanguage, getLanguageName } = await import("../../services/language-detector.js");
|
|
13
13
|
const captureSchema = z.object({
|
|
14
14
|
summary: z.string().optional(),
|
|
15
15
|
type: z.string(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PluginInput } from "@opencode-ai/plugin";
|
|
2
2
|
export declare function shouldRunAutomaticProfileCleanup(previousPromptCount: number, addedPromptCount: number, interval?: number): boolean;
|
|
3
3
|
export declare function performUserProfileLearning(ctx: PluginInput, directory: string): Promise<void>;
|
|
4
|
-
import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX } from "
|
|
4
|
+
import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX } from "../../core/extraction.js";
|
|
5
5
|
export { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX };
|
|
6
6
|
export declare function createUserProfileToolSchema(existingProfile: boolean): {
|
|
7
7
|
type: "function";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { getTags } from "
|
|
2
|
-
import { log } from "
|
|
3
|
-
import { CONFIG, refreshConfigIfChanged } from "
|
|
4
|
-
import { resolveOpencodeHostModel } from "
|
|
5
|
-
import { userPromptManager } from "
|
|
6
|
-
import { userProfileManager } from "
|
|
7
|
-
import { sortProfileItems } from "
|
|
8
|
-
import { loadOpencodeProvider } from "./
|
|
1
|
+
import { getTags } from "../../services/tags.js";
|
|
2
|
+
import { log } from "../../services/logger.js";
|
|
3
|
+
import { CONFIG, refreshConfigIfChanged } from "../../config.js";
|
|
4
|
+
import { resolveOpencodeHostModel } from "../../services/ai/live-model-choice.js";
|
|
5
|
+
import { userPromptManager } from "../../services/user-prompt/user-prompt-manager.js";
|
|
6
|
+
import { userProfileManager } from "../../services/user-profile/user-profile-manager.js";
|
|
7
|
+
import { sortProfileItems } from "../../utils/profile.js";
|
|
8
|
+
import { loadOpencodeProvider } from "./opencode-provider-loader.js";
|
|
9
9
|
let isLearningRunning = false;
|
|
10
10
|
export function shouldRunAutomaticProfileCleanup(previousPromptCount, addedPromptCount, interval = CONFIG.userProfileAutoCleanupInterval) {
|
|
11
11
|
if (!CONFIG.userProfileAutoCleanupEnabled || !Number.isInteger(interval) || interval <= 0) {
|
|
@@ -23,7 +23,7 @@ async function runAutomaticProfileCleanup(userId) {
|
|
|
23
23
|
const itemCount = profileData.preferences.length + profileData.patterns.length + profileData.workflows.length;
|
|
24
24
|
if (itemCount < 2)
|
|
25
25
|
return;
|
|
26
|
-
const { aiCleanupProfile } = await import("
|
|
26
|
+
const { aiCleanupProfile } = await import("../../services/user-profile/ai-cleanup.js");
|
|
27
27
|
const result = await aiCleanupProfile(profileData);
|
|
28
28
|
if (result.diff.merged.length === 0 && result.diff.removed.length === 0)
|
|
29
29
|
return;
|
|
@@ -338,7 +338,7 @@ CRITICAL: Only output observations grounded in the RECENT PROMPTS above. Write d
|
|
|
338
338
|
}
|
|
339
339
|
return truncate(base);
|
|
340
340
|
}
|
|
341
|
-
import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX, } from "
|
|
341
|
+
import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX, } from "../../core/extraction.js";
|
|
342
342
|
export { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX };
|
|
343
343
|
export function createUserProfileToolSchema(existingProfile) {
|
|
344
344
|
return {
|
|
@@ -497,7 +497,7 @@ async function analyzeUserProfile(context, existingProfile) {
|
|
|
497
497
|
log("user-profile-learning: trying opencode provider");
|
|
498
498
|
try {
|
|
499
499
|
const { generateStructuredOutput } = await loadOpencodeProvider();
|
|
500
|
-
const { getOpenCodeClient } = await import("./
|
|
500
|
+
const { getOpenCodeClient } = await import("./profile-llm-client.js");
|
|
501
501
|
log("user-profile-learning: opencode provider diag", {
|
|
502
502
|
provider: resolveOpencodeHostModel(CONFIG)?.providerID,
|
|
503
503
|
model: resolveOpencodeHostModel(CONFIG)?.modelID,
|
|
@@ -556,8 +556,8 @@ Use the update_user_profile tool to save the ${existingProfile ? "updated" : "ne
|
|
|
556
556
|
});
|
|
557
557
|
throw new Error("External API not configured for user memory learning");
|
|
558
558
|
}
|
|
559
|
-
const { AIProviderFactory } = await import("
|
|
560
|
-
const { buildMemoryProviderConfig } = await import("
|
|
559
|
+
const { AIProviderFactory } = await import("../../services/ai/ai-provider-factory.js");
|
|
560
|
+
const { buildMemoryProviderConfig } = await import("../../services/ai/provider-config.js");
|
|
561
561
|
const providerConfig = buildMemoryProviderConfig(CONFIG);
|
|
562
562
|
const provider = AIProviderFactory.createProvider(CONFIG.memoryProvider, providerConfig);
|
|
563
563
|
const systemPrompt = `You are a user behavior analyst for a coding assistant.
|
|
@@ -597,7 +597,7 @@ If no clear chains, return { "paths": [] }.`;
|
|
|
597
597
|
try {
|
|
598
598
|
const { z } = await import("zod");
|
|
599
599
|
const { generateStructuredOutput } = await loadOpencodeProvider();
|
|
600
|
-
const { getOpenCodeClient } = await import("./
|
|
600
|
+
const { getOpenCodeClient } = await import("./profile-llm-client.js");
|
|
601
601
|
let v2Client;
|
|
602
602
|
try {
|
|
603
603
|
v2Client = await getOpenCodeClient();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CONFIG } from "../../config.js";
|
|
2
|
-
import { resolveOpencodeHostModel } from "
|
|
2
|
+
import { resolveOpencodeHostModel } from "../../services/ai/live-model-choice.js";
|
|
3
3
|
import { loadOpencodeProvider } from "./opencode-provider-loader.js";
|
|
4
4
|
let _cachedClient = null;
|
|
5
5
|
let _cachedProvider = null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ModelPort } from "../../core/profile-analysis.js";
|
|
2
|
+
import type { OpencodeModelRef } from "./opencode-import-models.js";
|
|
3
|
+
type PromptInfo = {
|
|
4
|
+
error?: {
|
|
5
|
+
name: string;
|
|
6
|
+
data?: {
|
|
7
|
+
message?: string;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Extract assistant text from an OpenCode session.prompt result.
|
|
13
|
+
* AssistantMessage has no `text` field; content lives in `parts` (#177).
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractTextFromPromptResult(promptResult: unknown): {
|
|
16
|
+
info: PromptInfo | undefined;
|
|
17
|
+
rawText: string;
|
|
18
|
+
};
|
|
19
|
+
/** Adapt OpenCode's host model to the shared profile code, as `adaptPiProfileModel` does for Pi. */
|
|
20
|
+
export declare function adaptOpencodeProfileModel(v2Client: unknown, hostModel: OpencodeModelRef): ModelPort;
|
|
21
|
+
/** Route shared profile calls in this process to OpenCode's host model while one is configured. */
|
|
22
|
+
export declare function registerOpencodeProfileModel(): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { CONFIG } from "../../config.js";
|
|
2
|
+
import { resolveOpencodeHostModel } from "../../services/ai/live-model-choice.js";
|
|
3
|
+
import { log } from "../../services/logger.js";
|
|
4
|
+
import { OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS } from "../../services/request-timeouts.js";
|
|
5
|
+
import { registerHostProfileModel } from "../../services/user-profile/profile-model.js";
|
|
6
|
+
import { loadOpencodeProvider } from "./opencode-provider-loader.js";
|
|
7
|
+
import { getOpenCodeClient } from "./profile-llm-client.js";
|
|
8
|
+
/**
|
|
9
|
+
* Extract assistant text from an OpenCode session.prompt result.
|
|
10
|
+
* AssistantMessage has no `text` field; content lives in `parts` (#177).
|
|
11
|
+
*/
|
|
12
|
+
export function extractTextFromPromptResult(promptResult) {
|
|
13
|
+
const result = promptResult;
|
|
14
|
+
const info = result?.data?.info ?? result?.info;
|
|
15
|
+
const parts = result?.data?.parts ?? result?.parts ?? [];
|
|
16
|
+
const rawText = parts
|
|
17
|
+
.filter((p) => p.type === "text" && p.text)
|
|
18
|
+
.map((p) => p.text)
|
|
19
|
+
.join("\n")
|
|
20
|
+
.trim();
|
|
21
|
+
return { info, rawText };
|
|
22
|
+
}
|
|
23
|
+
function raceWithTimeout(promise, ms, message) {
|
|
24
|
+
let timer;
|
|
25
|
+
const timeout = new Promise((_, reject) => {
|
|
26
|
+
timer = setTimeout(() => reject(new Error(message)), ms);
|
|
27
|
+
});
|
|
28
|
+
return Promise.race([promise, timeout]).finally(() => {
|
|
29
|
+
if (timer !== undefined)
|
|
30
|
+
clearTimeout(timer);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Plain-text reply through a transient OpenCode session. Only AI profile cleanup
|
|
35
|
+
* calls it, so the session carries the cleanup title the history reader skips.
|
|
36
|
+
*/
|
|
37
|
+
async function completeViaSession(v2Client, hostModel, systemPrompt, userPrompt) {
|
|
38
|
+
const t0 = Date.now();
|
|
39
|
+
// Only "inherit" (or no model configured) needs resolving to the session's recent model.
|
|
40
|
+
const model = hostModel.modelID === "inherit"
|
|
41
|
+
? (await loadOpencodeProvider()).resolveOpencodeModelRef(hostModel)
|
|
42
|
+
: hostModel;
|
|
43
|
+
const created = (await raceWithTimeout(v2Client.session.create({
|
|
44
|
+
title: "omms profile cleanup",
|
|
45
|
+
directory: process.cwd(),
|
|
46
|
+
}), 30000, "session.create timeout"));
|
|
47
|
+
log("AI cleanup: session.create result", {
|
|
48
|
+
rawType: typeof created,
|
|
49
|
+
keys: Object.keys(created || {}),
|
|
50
|
+
hasData: !!created?.data,
|
|
51
|
+
dataId: created?.data?.id,
|
|
52
|
+
});
|
|
53
|
+
const sessionID = created?.data?.id || created?.id || created?.sessionID;
|
|
54
|
+
if (!sessionID)
|
|
55
|
+
throw new Error("session.create returned no session id");
|
|
56
|
+
log("AI cleanup: session created", { sessionID, createMs: Date.now() - t0 });
|
|
57
|
+
try {
|
|
58
|
+
const TIMEOUT_MS = OPENCODE_PROFILE_CLEANUP_TIMEOUT_MS;
|
|
59
|
+
const promptResult = await raceWithTimeout(v2Client.session.prompt({
|
|
60
|
+
sessionID,
|
|
61
|
+
model,
|
|
62
|
+
system: systemPrompt,
|
|
63
|
+
parts: [{ type: "text", text: userPrompt }],
|
|
64
|
+
// `noReply` suppresses assistant generation; cleanup needs the JSON reply (#177).
|
|
65
|
+
noReply: false,
|
|
66
|
+
}), TIMEOUT_MS, `opencodeClient prompt timeout after ${TIMEOUT_MS}ms`);
|
|
67
|
+
log("AI cleanup: session.prompt done", { promptMs: Date.now() - t0 });
|
|
68
|
+
const { info, rawText } = extractTextFromPromptResult(promptResult);
|
|
69
|
+
if (!info)
|
|
70
|
+
throw new Error("prompt response missing info");
|
|
71
|
+
if (info.error)
|
|
72
|
+
throw new Error(`opencode reported ${info.error.name}: ${info.error.data?.message ?? ""}`);
|
|
73
|
+
return rawText;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
try {
|
|
77
|
+
await v2Client.session.delete({ sessionID });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// ignore cleanup failures for ephemeral sessions
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Adapt OpenCode's host model to the shared profile code, as `adaptPiProfileModel` does for Pi. */
|
|
85
|
+
export function adaptOpencodeProfileModel(v2Client, hostModel) {
|
|
86
|
+
return {
|
|
87
|
+
provider: hostModel.providerID,
|
|
88
|
+
modelId: hostModel.modelID,
|
|
89
|
+
complete: (systemPrompt, userPrompt) => completeViaSession(v2Client, hostModel, systemPrompt, userPrompt),
|
|
90
|
+
async completeStructured(systemPrompt, userPrompt, schema) {
|
|
91
|
+
const client = await getOpenCodeClient();
|
|
92
|
+
const { generateStructuredOutput } = await loadOpencodeProvider();
|
|
93
|
+
return generateStructuredOutput({
|
|
94
|
+
client,
|
|
95
|
+
providerID: hostModel.providerID,
|
|
96
|
+
modelID: hostModel.modelID,
|
|
97
|
+
systemPrompt,
|
|
98
|
+
userPrompt,
|
|
99
|
+
schema,
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** Route shared profile calls in this process to OpenCode's host model while one is configured. */
|
|
105
|
+
export function registerOpencodeProfileModel() {
|
|
106
|
+
registerHostProfileModel(async () => {
|
|
107
|
+
const hostModel = resolveOpencodeHostModel(CONFIG);
|
|
108
|
+
if (!hostModel)
|
|
109
|
+
return null;
|
|
110
|
+
const v2Client = (await loadOpencodeProvider()).getV2Client();
|
|
111
|
+
if (!v2Client) {
|
|
112
|
+
log("profile model: opencode client unavailable, falling back to external API");
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
return adaptOpencodeProfileModel(v2Client, hostModel);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { isStructuredSummaryPromptMessage } from "../../core/internal-prompt.js";
|
|
2
|
+
export { isStructuredSummaryPromptMessage };
|
|
2
3
|
/** True when a prompt is omms's own internal traffic and must not be recorded or searched. */
|
|
3
4
|
export declare function isInternalPrompt(sessionID: string, userMessage: string): boolean;
|
|
4
5
|
/**
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isStructuredSummaryPromptMessage } from "../../core/internal-prompt.js";
|
|
2
|
+
import { isInternalStructuredSession } from "./opencode-provider.js";
|
|
2
3
|
import { userPromptManager } from "../../services/user-prompt/user-prompt-manager.js";
|
|
3
|
-
export
|
|
4
|
-
// This is the plugin's own structured-summary or profile-analysis request.
|
|
5
|
-
// OpenCode echoes it through chat.message like a normal user message, but
|
|
6
|
-
// capturing it would create self-referential memories / an infinite learning loop.
|
|
7
|
-
if (userMessage.includes("# User Profile Analysis")) {
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
return userMessage.includes("Analyze this conversation.") && userMessage.includes('type="skip"');
|
|
11
|
-
}
|
|
4
|
+
export { isStructuredSummaryPromptMessage };
|
|
12
5
|
/** True when a prompt is omms's own internal traffic and must not be recorded or searched. */
|
|
13
6
|
export function isInternalPrompt(sessionID, userMessage) {
|
|
14
7
|
return isStructuredSummaryPromptMessage(userMessage) || isInternalStructuredSession(sessionID);
|
|
@@ -3,6 +3,10 @@ import { type PiModelContext } from "./provider.js";
|
|
|
3
3
|
/** Resolve the Pi backfill independently of the manual import's session-model rule. */
|
|
4
4
|
export declare function resolvePiBackfillModels(ctx: PiModelContext, config?: {
|
|
5
5
|
piBackfillModel?: string;
|
|
6
|
+
memoryProvider?: string;
|
|
7
|
+
memoryModel?: string;
|
|
8
|
+
memoryApiUrl?: string;
|
|
9
|
+
memoryApiKey?: string;
|
|
6
10
|
}): Promise<{
|
|
7
11
|
model: string;
|
|
8
12
|
models: HistoryImportModels;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { CONFIG } from "../../config.js";
|
|
2
2
|
import { parseBackfillModel } from "../../importer/backfill-model.js";
|
|
3
|
+
import { resolveExternalBackfillModels } from "../../importer/external-backfill-models.js";
|
|
3
4
|
import { createPiLiveModels } from "./live-model.js";
|
|
4
5
|
import { adaptPiProfileModel } from "./profile.js";
|
|
5
6
|
import { createPiCaptureProvider, resolveImportModel } from "./provider.js";
|
|
6
7
|
/** Resolve the Pi backfill independently of the manual import's session-model rule. */
|
|
7
8
|
export async function resolvePiBackfillModels(ctx, config = CONFIG) {
|
|
8
9
|
const choice = parseBackfillModel(config, "pi");
|
|
10
|
+
if (choice === "external")
|
|
11
|
+
return resolveExternalBackfillModels("pi", config);
|
|
9
12
|
if (choice === "inherit") {
|
|
10
13
|
const live = createPiLiveModels(ctx);
|
|
11
14
|
const profile = live.profile();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CaptureSummaryProvider } from "../../core/host.js";
|
|
2
|
-
import { type PiSessionEntry } from "
|
|
2
|
+
import { type PiSessionEntry } from "../../importer/pi-conversation.js";
|
|
3
3
|
export interface PiCaptureState {
|
|
4
4
|
/** User entry IDs terminally handled by a settled capture for this session. */
|
|
5
5
|
handledUserEntries: Set<string>;
|
|
@@ -2,7 +2,7 @@ import { CONFIG } from "../../config.js";
|
|
|
2
2
|
import { captureConversation } from "../../core/capture.js";
|
|
3
3
|
import { log } from "../../services/logger.js";
|
|
4
4
|
import { memoryClient } from "../../services/client.js";
|
|
5
|
-
import { extractPiConversation } from "
|
|
5
|
+
import { extractPiConversation } from "../../importer/pi-conversation.js";
|
|
6
6
|
export function createPiCaptureState() {
|
|
7
7
|
return { handledUserEntries: new Set(), running: false };
|
|
8
8
|
}
|
|
@@ -111,6 +111,13 @@ export default function ommsPiExtension(pi) {
|
|
|
111
111
|
code: error instanceof Error ? error.name : "unknown",
|
|
112
112
|
}));
|
|
113
113
|
}
|
|
114
|
+
// Run now on a Settings page served by this process can use Pi's signed-in models.
|
|
115
|
+
void Promise.all([
|
|
116
|
+
import("../../importer/backfill-controls.js"),
|
|
117
|
+
import("./backfill-models.js"),
|
|
118
|
+
])
|
|
119
|
+
.then(([{ registerHostBackfillModels }, { resolvePiBackfillModels }]) => registerHostBackfillModels("pi", () => resolvePiBackfillModels(ctx)))
|
|
120
|
+
.catch(() => { });
|
|
114
121
|
const globalScope = globalThis;
|
|
115
122
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
116
123
|
if (isConfigured() &&
|
|
@@ -90,6 +90,7 @@ export function registerPiHistoryImportCommand(pi, getExtensionContext) {
|
|
|
90
90
|
let lastProgressNotify = 0;
|
|
91
91
|
const report = await runHistoryImport("pi", parsed, {
|
|
92
92
|
cwd: ctx.cwd,
|
|
93
|
+
track: { surface: "slash" },
|
|
93
94
|
models: selectedModel && !parsed.dryRun
|
|
94
95
|
? {
|
|
95
96
|
capture: createPiCaptureProvider(() => selectedModel),
|
package/dist/cli/index.js
CHANGED
|
@@ -14,6 +14,9 @@ Commands:
|
|
|
14
14
|
web Start the web app in the foreground
|
|
15
15
|
web install|uninstall|status Manage the web app login item
|
|
16
16
|
|
|
17
|
+
Options:
|
|
18
|
+
--version, -v Print the installed version
|
|
19
|
+
|
|
17
20
|
Import commands take the same options; run one with --help to see them.
|
|
18
21
|
Inside a session, /memory-import-opencode-history and /memory-import-pi-history
|
|
19
22
|
use that session's model instead of an API key.`;
|
|
@@ -28,6 +31,11 @@ export function parseImportArgs(argv) {
|
|
|
28
31
|
return { host, args };
|
|
29
32
|
}
|
|
30
33
|
export async function runCli(argv) {
|
|
34
|
+
if (argv[0] === "--version" || argv[0] === "-v") {
|
|
35
|
+
const { packageVersion } = await import("../services/package-version.js");
|
|
36
|
+
console.log(packageVersion());
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
31
39
|
if (argv.length === 0 || argv[0] === "--help" || argv[0] === "-h") {
|
|
32
40
|
console.log(usage);
|
|
33
41
|
return 0;
|
|
@@ -73,7 +81,11 @@ export async function runCli(argv) {
|
|
|
73
81
|
console.log(`Import model: ${model}`);
|
|
74
82
|
}
|
|
75
83
|
const { formatHistoryImportReport, historyImportFailed, runHistoryImport } = await import("../importer/run-import.js");
|
|
76
|
-
const report = await runHistoryImport(host, args, {
|
|
84
|
+
const report = await runHistoryImport(host, args, {
|
|
85
|
+
cwd: process.cwd(),
|
|
86
|
+
models,
|
|
87
|
+
track: { surface: "cli" },
|
|
88
|
+
});
|
|
77
89
|
console.log(formatHistoryImportReport(host, report, model));
|
|
78
90
|
return historyImportFailed(report) ? 1 : 0;
|
|
79
91
|
}
|