opencode-codex-memory 0.7.1 → 0.7.3
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/git-baseline.js +9 -12
- package/dist/src/phase2.js +5 -4
- package/dist/src/store.d.ts +1 -0
- package/dist/src/store.js +2 -1
- package/dist/src/tui.d.ts +10 -0
- package/dist/src/tui.js +24 -0
- package/dist/tools/control.js +16 -0
- package/package.json +3 -3
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.3"]
|
|
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.3" }],
|
|
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.3", { "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.3",
|
|
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.3", { "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.3",
|
|
353
353
|
{
|
|
354
354
|
"claude_import": {
|
|
355
355
|
"enabled": true,
|
package/dist/src/git-baseline.js
CHANGED
|
@@ -29,7 +29,7 @@ async function ensureInit(dir) {
|
|
|
29
29
|
const gitDir = path.join(dir, ".git");
|
|
30
30
|
let recreate = false;
|
|
31
31
|
try {
|
|
32
|
-
recreate =
|
|
32
|
+
recreate = gitMetadataUnusable(gitDir);
|
|
33
33
|
}
|
|
34
34
|
catch (err) {
|
|
35
35
|
if (err.code !== "ENOENT")
|
|
@@ -41,17 +41,14 @@ async function ensureInit(dir) {
|
|
|
41
41
|
await isogit.init({ fs, dir });
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return true;
|
|
53
|
-
}
|
|
54
|
-
return false;
|
|
44
|
+
/**
|
|
45
|
+
* Only the `.git` entry itself. A recursive walk of git objects hangs on
|
|
46
|
+
* Windows junctions / symlink cycles and is not needed: we only refuse a
|
|
47
|
+
* `.git` that is a symlink or a non-directory.
|
|
48
|
+
*/
|
|
49
|
+
function gitMetadataUnusable(gitDir) {
|
|
50
|
+
const st = fs.lstatSync(gitDir);
|
|
51
|
+
return st.isSymbolicLink() || !st.isDirectory();
|
|
55
52
|
}
|
|
56
53
|
// statusMatrix rows are [filepath, head, workdir, stage]; head !== workdir
|
|
57
54
|
// means the working tree differs from HEAD (added, modified, or deleted).
|
package/dist/src/phase2.js
CHANGED
|
@@ -2,7 +2,7 @@ import { checkRateLimit, isProviderCapacityError, noteProviderCapacityExhausted
|
|
|
2
2
|
import { ensureLayout, rebuildRawMemories, writeRolloutSummaries, pruneExtensionResources, writeWorkspaceDiff, validateConsolidationArtifacts, removeMemorySymlinks, } from "./workspace.js";
|
|
3
3
|
import { ensureBaseline, captureWorkspaceDiff, resetBaseline, DIFF_ARTIFACT } from "./git-baseline.js";
|
|
4
4
|
import { consolidateViaSubagent, getPluginInput, SubagentCancelledError, SubagentShutdownError, } from "./llm.js";
|
|
5
|
-
import { hostSessionLiveness } from "./host-client.js";
|
|
5
|
+
import { hostSessionLiveness, withHostTimeout } from "./host-client.js";
|
|
6
6
|
import { invalidateCache } from "./source.js";
|
|
7
7
|
import { memoryRoot } from "./paths.js";
|
|
8
8
|
import { abortPhase2Consolidation, beginPhase2AbortScope, endPhase2AbortScope, isPluginShuttingDown, } from "./lifecycle.js";
|
|
@@ -16,6 +16,7 @@ export const DEFAULT_PHASE2_OPTIONS = {
|
|
|
16
16
|
// Export runs only after a successful phase 2 (fresh, validated artifacts) and
|
|
17
17
|
// must never fail the run — Codex's workspace is best-effort foreign territory.
|
|
18
18
|
const PHASE2_LIVE_CHECK_CONCURRENCY = 8;
|
|
19
|
+
const GIT_TIMEOUT_MS = 120_000;
|
|
19
20
|
/**
|
|
20
21
|
* Codex get_phase2_input_selection re-validates each row against the live
|
|
21
22
|
* threads table. We only drop a row on a confirmed 404 (same as session.deleted);
|
|
@@ -132,7 +133,7 @@ export async function runPhase2(store, opts = DEFAULT_PHASE2_OPTIONS) {
|
|
|
132
133
|
// ad-hoc notes added since then reach consolidation. Stale stage-1
|
|
133
134
|
// output pruning happens in phase 1, before the rate gate (codex
|
|
134
135
|
// start.rs ordering).
|
|
135
|
-
if (!await ensureBaseline()) {
|
|
136
|
+
if (!await withHostTimeout(ensureBaseline(), GIT_TIMEOUT_MS, "ensureBaseline")) {
|
|
136
137
|
store.markPhase2Failed(claim.ownershipToken, "git baseline failed");
|
|
137
138
|
return { status: "baseline_failed" };
|
|
138
139
|
}
|
|
@@ -171,7 +172,7 @@ export async function runPhase2(store, opts = DEFAULT_PHASE2_OPTIONS) {
|
|
|
171
172
|
console.warn("[opencode-codex-memory] claude import sync failed:", err);
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
|
-
const diff = await captureWorkspaceDiff();
|
|
175
|
+
const diff = await withHostTimeout(captureWorkspaceDiff(), GIT_TIMEOUT_MS, "captureWorkspaceDiff");
|
|
175
176
|
if (releaseIfShuttingDown(store, claim.ownershipToken)) {
|
|
176
177
|
return { status: "shutting_down" };
|
|
177
178
|
}
|
|
@@ -273,7 +274,7 @@ export async function runPhase2(store, opts = DEFAULT_PHASE2_OPTIONS) {
|
|
|
273
274
|
store.markPhase2Failed(claim.ownershipToken, `failed_invalid_artifacts: ${artifacts.reason}`);
|
|
274
275
|
return { status: "failed_invalid_artifacts" };
|
|
275
276
|
}
|
|
276
|
-
if (!await resetBaseline()) {
|
|
277
|
+
if (!await withHostTimeout(resetBaseline(), GIT_TIMEOUT_MS, "resetBaseline")) {
|
|
277
278
|
store.markPhase2Failed(claim.ownershipToken, "baseline reset failed");
|
|
278
279
|
return { status: "baseline_reset_failed" };
|
|
279
280
|
}
|
package/dist/src/store.d.ts
CHANGED
package/dist/src/store.js
CHANGED
|
@@ -434,7 +434,7 @@ export class MemoryStore {
|
|
|
434
434
|
*/
|
|
435
435
|
phase2JobSnapshot() {
|
|
436
436
|
const row = this.db
|
|
437
|
-
.prepare(`SELECT status, finished_at, last_error, retry_at, lease_until, last_success_watermark FROM memory_jobs
|
|
437
|
+
.prepare(`SELECT status, started_at, finished_at, last_error, retry_at, lease_until, last_success_watermark FROM memory_jobs
|
|
438
438
|
WHERE kind='memory_consolidate_global' AND job_key='global'`)
|
|
439
439
|
.get();
|
|
440
440
|
if (!row)
|
|
@@ -452,6 +452,7 @@ export class MemoryStore {
|
|
|
452
452
|
return {
|
|
453
453
|
status: row.status,
|
|
454
454
|
last_error: row.last_error,
|
|
455
|
+
started_at: row.started_at,
|
|
455
456
|
finished_at: row.finished_at,
|
|
456
457
|
retry_at: row.retry_at,
|
|
457
458
|
lease_until: row.lease_until,
|
|
@@ -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/tools/control.js
CHANGED
|
@@ -192,6 +192,16 @@ export const memory_reset = tool({
|
|
|
192
192
|
function fmtUnixSec(sec) {
|
|
193
193
|
return sec ? new Date(sec * 1000).toISOString() : "none";
|
|
194
194
|
}
|
|
195
|
+
function fmtElapsedSec(startedAtSec) {
|
|
196
|
+
if (!startedAtSec)
|
|
197
|
+
return "unknown";
|
|
198
|
+
const sec = Math.max(0, Math.floor(Date.now() / 1000) - startedAtSec);
|
|
199
|
+
if (sec < 60)
|
|
200
|
+
return `${sec}s`;
|
|
201
|
+
if (sec < 3600)
|
|
202
|
+
return `${Math.floor(sec / 60)}m ${sec % 60}s`;
|
|
203
|
+
return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;
|
|
204
|
+
}
|
|
195
205
|
function fmtWatermarkMs(ms) {
|
|
196
206
|
if (ms === 0)
|
|
197
207
|
return "0 (no consumed inputs)";
|
|
@@ -229,6 +239,9 @@ export const memory_inspect = tool({
|
|
|
229
239
|
? [
|
|
230
240
|
`phase2_status: ${phase2.status}`,
|
|
231
241
|
`phase2_last_error: ${phase2.last_error ?? "none"}`,
|
|
242
|
+
`phase2_started_at: ${fmtUnixSec(phase2.started_at)}`,
|
|
243
|
+
`phase2_running_for: ${phase2.status === "running" ? fmtElapsedSec(phase2.started_at) : "n/a"}`,
|
|
244
|
+
`phase2_lease_until: ${fmtUnixSec(phase2.lease_until)}`,
|
|
232
245
|
`phase2_retry_at: ${fmtUnixSec(phase2.retry_at)}`,
|
|
233
246
|
`phase2_last_attempt_finished_at: ${fmtUnixSec(phase2.finished_at)}`,
|
|
234
247
|
`phase2_last_success_watermark: ${fmtWatermarkMs(phase2.last_success_watermark)}`,
|
|
@@ -238,6 +251,9 @@ export const memory_inspect = tool({
|
|
|
238
251
|
: [
|
|
239
252
|
"phase2_status: none",
|
|
240
253
|
"phase2_last_error: none",
|
|
254
|
+
"phase2_started_at: none",
|
|
255
|
+
"phase2_running_for: n/a",
|
|
256
|
+
"phase2_lease_until: none",
|
|
241
257
|
"phase2_retry_at: none",
|
|
242
258
|
"phase2_last_attempt_finished_at: none",
|
|
243
259
|
"phase2_last_success_watermark: none",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-codex-memory",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
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",
|