opencode-codex-memory 0.7.0 → 0.7.2
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 +6 -6
- package/dist/src/tui.d.ts +10 -0
- package/dist/src/tui.js +24 -0
- package/dist/src/v2/service.d.ts +10 -6
- package/dist/src/v2/service.js +46 -24
- package/dist/src/v2/shim.js +16 -6
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ If you want the mental model — learning, remembering, forgetting — see
|
|
|
52
52
|
|
|
53
53
|
```json
|
|
54
54
|
{
|
|
55
|
-
"plugin": ["opencode-codex-memory@0.7.
|
|
55
|
+
"plugin": ["opencode-codex-memory@0.7.2"]
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -72,7 +72,7 @@ V2 plugin syntax:
|
|
|
72
72
|
|
|
73
73
|
```jsonc
|
|
74
74
|
{
|
|
75
|
-
"plugins": [{ "package": "opencode-codex-memory@0.7.
|
|
75
|
+
"plugins": [{ "package": "opencode-codex-memory@0.7.2" }],
|
|
76
76
|
}
|
|
77
77
|
```
|
|
78
78
|
|
|
@@ -198,7 +198,7 @@ To set options, turn the plugin entry into a `[name, options]` pair:
|
|
|
198
198
|
```json
|
|
199
199
|
{
|
|
200
200
|
"plugin": [
|
|
201
|
-
["opencode-codex-memory@0.7.
|
|
201
|
+
["opencode-codex-memory@0.7.2", { "disable_on_external_context": true, "min_rollout_idle_hours": 2 }]
|
|
202
202
|
]
|
|
203
203
|
}
|
|
204
204
|
```
|
|
@@ -265,7 +265,7 @@ Off by default; no changes to Codex's own config are required.
|
|
|
265
265
|
{
|
|
266
266
|
"plugin": [
|
|
267
267
|
[
|
|
268
|
-
"opencode-codex-memory@0.7.
|
|
268
|
+
"opencode-codex-memory@0.7.2",
|
|
269
269
|
{ "codex_interop": { "import": true, "export": true } }
|
|
270
270
|
]
|
|
271
271
|
]
|
|
@@ -322,7 +322,7 @@ from the project memories Claude already keeps on your machine. **One-way only**
|
|
|
322
322
|
```json
|
|
323
323
|
{
|
|
324
324
|
"plugin": [
|
|
325
|
-
["opencode-codex-memory@0.7.
|
|
325
|
+
["opencode-codex-memory@0.7.2", { "claude_import": { "enabled": true } }]
|
|
326
326
|
]
|
|
327
327
|
}
|
|
328
328
|
```
|
|
@@ -349,7 +349,7 @@ Claude names each project with an opaque id (a folder under
|
|
|
349
349
|
{
|
|
350
350
|
"plugin": [
|
|
351
351
|
[
|
|
352
|
-
"opencode-codex-memory@0.7.
|
|
352
|
+
"opencode-codex-memory@0.7.2",
|
|
353
353
|
{
|
|
354
354
|
"claude_import": {
|
|
355
355
|
"enabled": true,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type PluginSetup = (() => void | Promise<void>) | void;
|
|
2
|
+
declare function setup(ctx: unknown): Promise<PluginSetup>;
|
|
3
|
+
/** OpenCode 1.x TUI host: keep the package loadable. No sidebar on 1.x. */
|
|
4
|
+
declare function tui(): Promise<void>;
|
|
5
|
+
declare const _default: {
|
|
6
|
+
id: string;
|
|
7
|
+
setup: typeof setup;
|
|
8
|
+
tui: typeof tui;
|
|
9
|
+
};
|
|
10
|
+
export default _default;
|
package/dist/src/tui.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dual-host `./tui` entry.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode 1.18.29+ (and 1.18.30) resolves `exports["./tui"]` and requires
|
|
5
|
+
* `default.tui()` — `readV1Plugin(..., "tui")` in strict mode. The Memory
|
|
6
|
+
* sidebar is OpenCode 2-only (`src/v2/tui.tsx`) and must not load until a
|
|
7
|
+
* V2 host calls setup(): the V2 TUI SDK is an optional peer and is missing
|
|
8
|
+
* on 1.x, so a static import there is "failed to load plugin".
|
|
9
|
+
*/
|
|
10
|
+
const PLUGIN_ID = "opencode-codex-memory.tui";
|
|
11
|
+
async function setup(ctx) {
|
|
12
|
+
const { default: plugin } = await import("./v2/tui.js");
|
|
13
|
+
const fn = plugin.setup;
|
|
14
|
+
if (typeof fn !== "function")
|
|
15
|
+
return;
|
|
16
|
+
return fn(ctx);
|
|
17
|
+
}
|
|
18
|
+
/** OpenCode 1.x TUI host: keep the package loadable. No sidebar on 1.x. */
|
|
19
|
+
async function tui() { }
|
|
20
|
+
export default {
|
|
21
|
+
id: PLUGIN_ID,
|
|
22
|
+
setup,
|
|
23
|
+
tui,
|
|
24
|
+
};
|
package/dist/src/v2/service.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The supported OpenCode 2 connection boundary.
|
|
3
3
|
*
|
|
4
|
-
* Server plugins do not receive the complete public client.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Server plugins do not receive the complete public client. `ctx` is the
|
|
5
|
+
* documented plugin API; global session list is missing there, so we talk
|
|
6
|
+
* HTTP via `@opencode/client` (a runtime dependency so the plugin cache
|
|
7
|
+
* actually installs it). Discovery reads XDG `service.json` (never
|
|
8
|
+
* Service.ensure() / Service.discover() — those still probe /api/health,
|
|
9
|
+
* which 2.0.5 404s). GET /api/status pid must match this process.
|
|
10
10
|
*/
|
|
11
11
|
export interface V2ServiceEndpoint {
|
|
12
12
|
url: string;
|
|
@@ -57,6 +57,10 @@ export interface V2ServiceDependencies {
|
|
|
57
57
|
export declare function setV2ServiceDependenciesForTest(dependencies: V2ServiceDependencies | null): void;
|
|
58
58
|
/** Forget a cached endpoint after a service restart or failed request. */
|
|
59
59
|
export declare function invalidateOwnService(): void;
|
|
60
|
+
/** Last discoverOwnService failure, if ownServiceClient returned null. */
|
|
61
|
+
export declare function lastServiceFailure(): string | null;
|
|
62
|
+
/** Auth headers for the registered local service. */
|
|
63
|
+
export declare function serviceHeaders(endpoint: V2ServiceEndpoint): Record<string, string> | undefined;
|
|
60
64
|
export declare function parseReadyStatus(body: unknown): V2ServiceStatus | null;
|
|
61
65
|
export declare function readRegisteredEndpoint(file?: string): Promise<V2ServiceEndpoint | undefined>;
|
|
62
66
|
export declare function fetchServiceStatus(endpoint: V2ServiceEndpoint, headers: Record<string, string> | undefined, signal?: AbortSignal): Promise<V2ServiceStatus>;
|
package/dist/src/v2/service.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The supported OpenCode 2 connection boundary.
|
|
3
3
|
*
|
|
4
|
-
* Server plugins do not receive the complete public client.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Server plugins do not receive the complete public client. `ctx` is the
|
|
5
|
+
* documented plugin API; global session list is missing there, so we talk
|
|
6
|
+
* HTTP via `@opencode/client` (a runtime dependency so the plugin cache
|
|
7
|
+
* actually installs it). Discovery reads XDG `service.json` (never
|
|
8
|
+
* Service.ensure() / Service.discover() — those still probe /api/health,
|
|
9
|
+
* which 2.0.5 404s). GET /api/status pid must match this process.
|
|
10
10
|
*/
|
|
11
11
|
import { readFile } from "node:fs/promises";
|
|
12
12
|
import { homedir } from "node:os";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
+
import { OpenCode } from "@opencode/client";
|
|
15
|
+
import { Service } from "@opencode/client/service";
|
|
14
16
|
let testDependencies = null;
|
|
15
17
|
let clientPromise = null;
|
|
16
|
-
|
|
18
|
+
let lastFailure = null;
|
|
19
|
+
const SERVICE_REQUEST_TIMEOUT_MS = 3_000;
|
|
17
20
|
/** Test seam: replace discovery without changing the production connection path. */
|
|
18
21
|
export function setV2ServiceDependenciesForTest(dependencies) {
|
|
19
22
|
testDependencies = dependencies;
|
|
@@ -22,6 +25,15 @@ export function setV2ServiceDependenciesForTest(dependencies) {
|
|
|
22
25
|
/** Forget a cached endpoint after a service restart or failed request. */
|
|
23
26
|
export function invalidateOwnService() {
|
|
24
27
|
clientPromise = null;
|
|
28
|
+
lastFailure = null;
|
|
29
|
+
}
|
|
30
|
+
/** Last discoverOwnService failure, if ownServiceClient returned null. */
|
|
31
|
+
export function lastServiceFailure() {
|
|
32
|
+
return lastFailure;
|
|
33
|
+
}
|
|
34
|
+
/** Auth headers for the registered local service. */
|
|
35
|
+
export function serviceHeaders(endpoint) {
|
|
36
|
+
return Service.headers(endpoint);
|
|
25
37
|
}
|
|
26
38
|
export function parseReadyStatus(body) {
|
|
27
39
|
const record = unwrapStatusRecord(body);
|
|
@@ -94,36 +106,40 @@ export async function readRegisteredEndpoint(file = registrationPath()) {
|
|
|
94
106
|
async function fetchJson(url, headers, signal) {
|
|
95
107
|
const response = await fetch(url, { headers, signal });
|
|
96
108
|
const text = await response.text();
|
|
109
|
+
let body;
|
|
97
110
|
if (!text)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
body = undefined;
|
|
112
|
+
else {
|
|
113
|
+
try {
|
|
114
|
+
body = JSON.parse(text);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
body = text;
|
|
118
|
+
}
|
|
104
119
|
}
|
|
120
|
+
return { ok: response.ok, status: response.status, body };
|
|
105
121
|
}
|
|
106
122
|
export async function fetchServiceStatus(endpoint, headers, signal) {
|
|
107
|
-
const
|
|
108
|
-
const fromStatus = parseReadyStatus(
|
|
123
|
+
const statusRes = await fetchJson(new URL("/api/status", endpoint.url), headers, signal);
|
|
124
|
+
const fromStatus = parseReadyStatus(statusRes.body);
|
|
109
125
|
if (fromStatus)
|
|
110
126
|
return fromStatus;
|
|
111
|
-
|
|
112
|
-
|
|
127
|
+
if (!statusRes.ok)
|
|
128
|
+
throw new Error(`GET /api/status ${String(statusRes.status)}`);
|
|
129
|
+
const healthRes = await fetchJson(new URL("/api/health", endpoint.url), headers, signal);
|
|
130
|
+
const fromHealth = parseReadyStatus(healthRes.body);
|
|
113
131
|
if (fromHealth)
|
|
114
132
|
return fromHealth;
|
|
115
133
|
throw new Error("registered OpenCode service is not healthy");
|
|
116
134
|
}
|
|
117
|
-
|
|
118
|
-
const { Service } = await import("@opencode/client/service");
|
|
119
|
-
const { OpenCode } = await import("@opencode/client");
|
|
135
|
+
function productionDependencies() {
|
|
120
136
|
return {
|
|
121
137
|
service: {
|
|
122
138
|
discover: () => readRegisteredEndpoint(),
|
|
123
|
-
headers:
|
|
139
|
+
headers: serviceHeaders,
|
|
124
140
|
},
|
|
125
141
|
make: (options) => OpenCode.make(options),
|
|
126
|
-
probe: (endpoint, signal) => fetchServiceStatus(endpoint,
|
|
142
|
+
probe: (endpoint, signal) => fetchServiceStatus(endpoint, serviceHeaders(endpoint), signal),
|
|
127
143
|
};
|
|
128
144
|
}
|
|
129
145
|
async function probeEndpoint(deps, endpoint, client, signal) {
|
|
@@ -144,7 +160,7 @@ async function probeEndpoint(deps, endpoint, client, signal) {
|
|
|
144
160
|
* safety failure because it would make global memory operate on another host.
|
|
145
161
|
*/
|
|
146
162
|
export async function discoverOwnService(dependencies, timeoutMs = SERVICE_REQUEST_TIMEOUT_MS) {
|
|
147
|
-
const deps = dependencies ?? testDependencies ??
|
|
163
|
+
const deps = dependencies ?? testDependencies ?? productionDependencies();
|
|
148
164
|
const endpoint = await withServiceTimeout(deps.service.discover(), timeoutMs);
|
|
149
165
|
if (!endpoint)
|
|
150
166
|
return null;
|
|
@@ -159,7 +175,13 @@ export async function discoverOwnService(dependencies, timeoutMs = SERVICE_REQUE
|
|
|
159
175
|
/** Resolve the registered client once per live service; never start a service. */
|
|
160
176
|
export async function ownServiceClient() {
|
|
161
177
|
if (!clientPromise) {
|
|
162
|
-
const request = discoverOwnService()
|
|
178
|
+
const request = discoverOwnService()
|
|
179
|
+
.then((found) => {
|
|
180
|
+
lastFailure = found ? null : "no registered OpenCode 2 service.json";
|
|
181
|
+
return found?.client ?? null;
|
|
182
|
+
})
|
|
183
|
+
.catch((err) => {
|
|
184
|
+
lastFailure = err instanceof Error ? err.message : String(err);
|
|
163
185
|
console.warn("[opencode-codex-memory] registered OpenCode service unavailable:", err);
|
|
164
186
|
return null;
|
|
165
187
|
});
|
package/dist/src/v2/shim.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memoryRoot } from "../paths.js";
|
|
2
|
-
import { invalidateOwnService, ownServiceClient } from "./service.js";
|
|
2
|
+
import { invalidateOwnService, lastServiceFailure, ownServiceClient } from "./service.js";
|
|
3
3
|
let v2ctx = null;
|
|
4
4
|
export function setV2Context(ctx) {
|
|
5
5
|
v2ctx = ctx;
|
|
@@ -315,19 +315,19 @@ async function v2promptWithWait(sessionID, body, signal) {
|
|
|
315
315
|
export function buildV1ClientShim() {
|
|
316
316
|
async function serviceOrThrow() {
|
|
317
317
|
const client = await ownServiceClient();
|
|
318
|
-
if (!client)
|
|
319
|
-
throw new Error("no healthy registered OpenCode 2 service for global memory operations");
|
|
318
|
+
if (!client) {
|
|
319
|
+
throw new Error(lastServiceFailure() ?? "no healthy registered OpenCode 2 service for global memory operations");
|
|
320
|
+
}
|
|
320
321
|
return client;
|
|
321
322
|
}
|
|
322
|
-
async function
|
|
323
|
-
const client = await serviceOrThrow();
|
|
323
|
+
async function paginateSessionList(listFn, limit, cursor, search) {
|
|
324
324
|
const pageSize = Math.min(Math.max(limit, 1), 5000);
|
|
325
325
|
const out = [];
|
|
326
326
|
const seenCursors = new Set();
|
|
327
327
|
const timestampCursor = typeof cursor === "number" ? cursor : undefined;
|
|
328
328
|
let next = typeof cursor === "string" ? cursor : undefined;
|
|
329
329
|
while (out.length < limit) {
|
|
330
|
-
const response = (await
|
|
330
|
+
const response = (await listFn({
|
|
331
331
|
limit: pageSize,
|
|
332
332
|
order: "desc",
|
|
333
333
|
parentID: null,
|
|
@@ -362,6 +362,16 @@ export function buildV1ClientShim() {
|
|
|
362
362
|
}
|
|
363
363
|
return { data: out.slice(0, limit) };
|
|
364
364
|
}
|
|
365
|
+
async function listGlobalSessions(limit, cursor, search) {
|
|
366
|
+
const localList = v2ctx ? v2ctx.session.list : undefined;
|
|
367
|
+
if (typeof localList === "function") {
|
|
368
|
+
return paginateSessionList((input) => localList(input), limit, cursor, search);
|
|
369
|
+
}
|
|
370
|
+
const client = await serviceOrThrow();
|
|
371
|
+
if (typeof client.session.list !== "function")
|
|
372
|
+
throw new Error("registered service does not support session.list");
|
|
373
|
+
return paginateSessionList((input) => client.session.list(input), limit, cursor, search);
|
|
374
|
+
}
|
|
365
375
|
const session = {
|
|
366
376
|
create: async (opts) => {
|
|
367
377
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-codex-memory",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Persistent memory plugin for opencode — ports codex's two-phase memory system (extraction → consolidation → injection → citation feedback)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"types": "./dist/src/v2/index.d.ts"
|
|
19
19
|
},
|
|
20
20
|
"./tui": {
|
|
21
|
-
"types": "./dist/src/
|
|
22
|
-
"import": "./dist/src/
|
|
21
|
+
"types": "./dist/src/tui.d.ts",
|
|
22
|
+
"import": "./dist/src/tui.js"
|
|
23
23
|
},
|
|
24
24
|
"./tools/*": {
|
|
25
25
|
"import": "./dist/tools/*.js",
|
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
"license": "Apache-2.0",
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@opencode-ai/plugin": "^1.18.0",
|
|
65
|
+
"@opencode/client": "2.0.5",
|
|
65
66
|
"diff": "^9.0.0",
|
|
66
67
|
"isomorphic-git": "^1.38.6",
|
|
67
68
|
"xdg-basedir": "^5.1.0"
|
|
68
69
|
},
|
|
69
70
|
"devDependencies": {
|
|
70
|
-
"@opencode/client": "2.0.3",
|
|
71
71
|
"@opencode/plugin": "2.0.3",
|
|
72
72
|
"zod": "4.1.8",
|
|
73
73
|
"@opencode/theme": "2.0.3",
|