@remnic/plugin-codex 9.51.0 → 9.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "remnic",
3
3
  "description": "Universal memory for AI agents — automatic recall, observation, and cross-agent knowledge sharing",
4
- "version": "9.51.0",
4
+ "version": "9.51.1",
5
5
  "author": {
6
6
  "name": "Joshua Warren",
7
7
  "url": "https://github.com/joshuaswarren"
package/README.md CHANGED
@@ -31,7 +31,7 @@ The package is **data + one small runtime materializer** (no runtime JS beyond t
31
31
  | File / dir | Purpose |
32
32
  |---|---|
33
33
  | `.codex-plugin/plugin.json` | Plugin manifest |
34
- | `hooks/hooks.json` + `hooks/bin/remnic-codex-hook.{cjs,sh,ps1}` | Codex session-lifecycle hooks (recall, observe, session-end, **pre-compact LCM flush**). A single cross-platform Node.js runner (`.cjs`) with thin POSIX (`.sh`) and Windows PowerShell (`.ps1`) launchers; `hooks.json` wires each event with both `command` and `commandWindows` so hooks work on macOS, Linux, and Windows. |
34
+ | `hooks/hooks.json` + `hooks/bin/remnic-codex-hook.{cjs,sh,ps1}` | Codex session hooks for recall, observe, session end, and pre-compact LCM flush. `hooks.json` invokes the POSIX launcher through `sh`, so hooks still run when an install path does not preserve its executable mode. Windows uses the PowerShell launcher. |
35
35
  | `skills/` | `remnic-recall`, `remnic-remember`, `remnic-search`, `remnic-status`, `remnic-entities`, `remnic-memory-workflow` — invocable from Codex chats |
36
36
  | `memories_extensions/remnic/` | Codex phase-2 consolidation instructions — tells the Codex compactor sub-agent to treat Remnic's on-disk Markdown as an authoritative local memory source when it builds `MEMORY.md`. Local-only (no MCP, no network); runtime recall/observe still flow through the hooks above. |
37
37
  | `.mcp.json` | MCP server config pointing Codex at `http://localhost:4318/mcp` |
@@ -132,10 +132,10 @@ the foreground path reads it.
132
132
 
133
133
  Codex does not run non-managed command hooks until a human reviews and trusts
134
134
  them. The first time these hooks are enabled, Codex prints a startup warning
135
- directing you to run `/hooks` in the CLI; review the four Remnic entries
136
- (SessionStart / UserPromptSubmit / Stop / PreCompact) and approve them. Codex
137
- records trust against the current hook hash, so updates to `hooks.json` will
138
- re-trigger a review.
135
+ directing you to run `/hooks` in the CLI. Review the five Remnic entries
136
+ (SessionStart, PostToolUse, UserPromptSubmit, Stop, and PreCompact), then
137
+ approve them. Codex records trust against the current hook hash, so updates
138
+ to `hooks.json` will trigger a new review.
139
139
 
140
140
  For fleet / headless automation there is no non-interactive trust CLI today;
141
141
  pick one of:
@@ -351,26 +351,25 @@ test("unknown event fails open with continue", async () => {
351
351
 
352
352
  // ── #1443 review fixes ──────────────────────────────────────────────────────
353
353
 
354
- test("hooks.json: every event resolves via ${PLUGIN_ROOT} and uses powershell (#1443 review)", () => {
354
+ const POSIX_HOOK_EVENTS = Object.freeze({
355
+ SessionStart: "session-start",
356
+ PostToolUse: "post-tool-observe",
357
+ UserPromptSubmit: "user-prompt-recall",
358
+ Stop: "session-end",
359
+ PreCompact: "pre-compact",
360
+ });
361
+
362
+ test("hooks.json: every POSIX event invokes the correct launcher event through sh", () => {
355
363
  const cfg = JSON.parse(
356
364
  fs.readFileSync(path.join(__dirname, "..", "hooks.json"), "utf8"),
357
365
  );
358
- for (const event of ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop", "PreCompact"]) {
366
+ for (const [event, runnerEvent] of Object.entries(POSIX_HOOK_EVENTS)) {
359
367
  for (const matcher of cfg.hooks[event]) {
360
368
  for (const hook of matcher.hooks) {
361
- // Codex runs plugin hooks from the session cwd via sh -lc / cmd /C and
362
- // substitutes ${PLUGIN_ROOT} (openai/codex discovery.rs + command_runner.rs),
363
- // so the path must be PLUGIN_ROOT-relative AND quoted — an unquoted path
364
- // would word-split on a plugin root containing spaces (e.g.
365
- // C:\Users\Jane Doe).
366
- assert.ok(
367
- hook.command.startsWith('"${PLUGIN_ROOT}/hooks/bin/'),
368
- `${event}.command must resolve via a quoted \${PLUGIN_ROOT}, got: ${hook.command}`,
369
- );
370
- assert.match(
369
+ assert.equal(
371
370
  hook.command,
372
- /^"\$\{PLUGIN_ROOT\}\/hooks\/bin\/remnic-codex-hook\.sh"\s/,
373
- `${event}.command path must be wrapped in double quotes`,
371
+ `sh "\${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh" ${runnerEvent}`,
372
+ `${event}.command must invoke the matching runner event through sh`,
374
373
  );
375
374
  assert.ok(hook.commandWindows, `${event} must declare commandWindows`);
376
375
  assert.match(
@@ -378,8 +377,6 @@ test("hooks.json: every event resolves via ${PLUGIN_ROOT} and uses powershell (#
378
377
  /-File "\$\{PLUGIN_ROOT\}\\hooks\\bin\\remnic-codex-hook\.ps1"\s/,
379
378
  `${event}.commandWindows must pass a quoted \${PLUGIN_ROOT} -File path`,
380
379
  );
381
- // Use `powershell` not `pwsh` so stock Windows 10/11 works without
382
- // PowerShell 7 installed (#1443 review).
383
380
  assert.match(
384
381
  hook.commandWindows,
385
382
  /^powershell\b/,
@@ -390,6 +387,67 @@ test("hooks.json: every event resolves via ${PLUGIN_ROOT} and uses powershell (#
390
387
  }
391
388
  });
392
389
 
390
+ test(
391
+ "hooks.json: every POSIX command runs a packaged launcher without executable mode",
392
+ { skip: process.platform === "win32" },
393
+ async () => {
394
+ const home = mkHome();
395
+ try {
396
+ const pluginRoot = path.join(home, "plugin root");
397
+ const binDir = path.join(pluginRoot, "hooks", "bin");
398
+ fs.mkdirSync(binDir, { recursive: true });
399
+ for (const file of ["remnic-codex-hook.sh", "remnic-codex-hook.cjs"]) {
400
+ fs.copyFileSync(path.join(__dirname, file), path.join(binDir, file));
401
+ }
402
+ fs.chmodSync(path.join(binDir, "remnic-codex-hook.sh"), 0o644);
403
+
404
+ const cfg = JSON.parse(
405
+ fs.readFileSync(path.join(__dirname, "..", "hooks.json"), "utf8"),
406
+ );
407
+ const runCommand = (command, sessionId) => new Promise((resolve, reject) => {
408
+ const child = spawn("sh", ["-c", command], {
409
+ env: {
410
+ ...process.env,
411
+ HOME: home,
412
+ USERPROFILE: home,
413
+ XDG_STATE_HOME: path.join(home, "state"),
414
+ PLUGIN_ROOT: pluginRoot,
415
+ REMNIC_CODEX_MATERIALIZE: "0",
416
+ OPENCLAW_REMNIC_ACCESS_TOKEN: "",
417
+ OPENCLAW_ENGRAM_ACCESS_TOKEN: "",
418
+ REMNIC_AUTH_TOKEN: "",
419
+ ENGRAM_AUTH_TOKEN: "",
420
+ REMNIC_HOOK_TOKEN: "",
421
+ REMNIC_DAEMON_URL: "",
422
+ ENGRAM_DAEMON_URL: "",
423
+ },
424
+ stdio: ["pipe", "pipe", "pipe"],
425
+ });
426
+ let stdout = "";
427
+ let stderr = "";
428
+ child.stdout.on("data", (data) => {
429
+ stdout += data;
430
+ });
431
+ child.stderr.on("data", (data) => {
432
+ stderr += data;
433
+ });
434
+ child.stdin.end(JSON.stringify({ session_id: sessionId, cwd: home }));
435
+ child.on("error", reject);
436
+ child.on("close", (exitCode) => resolve({ exitCode, stdout, stderr }));
437
+ });
438
+
439
+ for (const [event, runnerEvent] of Object.entries(POSIX_HOOK_EVENTS)) {
440
+ const command = cfg.hooks[event][0].hooks[0].command;
441
+ const result = await runCommand(command, `${runnerEvent}-mode-test`);
442
+ assert.equal(result.exitCode, 0, `${event}: ${result.stderr}`);
443
+ assert.equal(JSON.parse(result.stdout).continue, true, event);
444
+ }
445
+ } finally {
446
+ fs.rmSync(home, { recursive: true, force: true });
447
+ }
448
+ },
449
+ );
450
+
393
451
  test("runner source: remnic→engram fallthrough is PATH-gated and Windows-shim aware (#1443 review)", () => {
394
452
  const src = fs.readFileSync(path.join(__dirname, "remnic-codex-hook.cjs"), "utf8");
395
453
  // Both the migration and daemon-start loops pre-check PATH with onPath()
package/hooks/hooks.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
9
- "command": "\"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" session-start",
9
+ "command": "sh \"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" session-start",
10
10
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\hooks\\bin\\remnic-codex-hook.ps1\" session-start",
11
11
  "timeout": 45
12
12
  }
@@ -19,7 +19,7 @@
19
19
  "hooks": [
20
20
  {
21
21
  "type": "command",
22
- "command": "\"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" post-tool-observe",
22
+ "command": "sh \"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" post-tool-observe",
23
23
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\hooks\\bin\\remnic-codex-hook.ps1\" post-tool-observe",
24
24
  "timeout": 10
25
25
  }
@@ -32,7 +32,7 @@
32
32
  "hooks": [
33
33
  {
34
34
  "type": "command",
35
- "command": "\"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" user-prompt-recall",
35
+ "command": "sh \"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" user-prompt-recall",
36
36
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\hooks\\bin\\remnic-codex-hook.ps1\" user-prompt-recall",
37
37
  "timeout": 20
38
38
  }
@@ -45,7 +45,7 @@
45
45
  "hooks": [
46
46
  {
47
47
  "type": "command",
48
- "command": "\"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" session-end",
48
+ "command": "sh \"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" session-end",
49
49
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\hooks\\bin\\remnic-codex-hook.ps1\" session-end",
50
50
  "timeout": 30
51
51
  }
@@ -58,7 +58,7 @@
58
58
  "hooks": [
59
59
  {
60
60
  "type": "command",
61
- "command": "\"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" pre-compact",
61
+ "command": "sh \"${PLUGIN_ROOT}/hooks/bin/remnic-codex-hook.sh\" pre-compact",
62
62
  "commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\hooks\\bin\\remnic-codex-hook.ps1\" pre-compact",
63
63
  "timeout": 90
64
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/plugin-codex",
3
- "version": "9.51.0",
3
+ "version": "9.51.1",
4
4
  "description": "Remnic memory plugin for Codex CLI — hooks, skills, MCP integration",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,6 @@
30
30
  ".mcp.json"
31
31
  ],
32
32
  "dependencies": {
33
- "@remnic/core": "^9.51.0"
33
+ "@remnic/core": "^9.51.1"
34
34
  }
35
35
  }