@unblocklabs/unblock-codex-prompt 0.2.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Unblock Codex Prompt
2
2
 
3
3
  A small, explicit prompt-management bridge for dedicated OpenClaw Mac agents.
4
- **Version 0.2.1.** The frozen bridge supports OpenClaw/Codex 2026.9.2 and newer
4
+ **Version 0.2.3.** The frozen bridge supports OpenClaw/Codex 2026.9.2 and newer
5
5
  stable releases with a compatible context-assembly layout.
6
6
 
7
7
  ## What you control
@@ -29,18 +29,25 @@ files use private permissions. Missing identity documents are recorded as absent
29
29
  ## Compatibility
30
30
 
31
31
  - Frozen bridge: **OpenClaw and @openclaw/codex >=2026.9.2**. Reviewed layouts cover
32
- 2026.9.2, 2026.9.3, 2026.9.4 and 2026.9.8, including parent-local context and split
33
- `dist/.setup/` bundles. Bundle names
32
+ 2026.9.2, 2026.9.3, 2026.9.4, 2026.9.8, 2026.10.6 and 2026.10.8, including parent-local context,
33
+ refreshable instructions and split `dist/.setup/` bundles. Bundle names
34
34
  may change; fingerprints of the context-assembly functions must still match.
35
35
  Changed assembly is refused pending review, rather than patched blindly.
36
- - Managed local stdio Codex, agent-scoped home, no custom command/arguments.
36
+ - Local stdio Codex with an OpenClaw agent-scoped home. Operator-selected
37
+ `appServer.command` and `appServer.args` are supported and left unchanged.
38
+ The launcher must honor OpenClaw's `CODEX_HOME` and must not override the
39
+ managed `model_instructions_file`; status cannot attest to launcher behavior.
37
40
  - One target agent per installation. Other agents use upstream behavior. Native
38
41
  children may inherit the parent's frozen context.
39
42
 
40
43
  This bridge uses two private upstream seams because public prompt hooks cannot
41
44
  fix the dropped-context path: one skill-catalog builder and guarded Codex context
42
- carriers. Newer builds also require parent-local suppression and removal of the
43
- separate thread-level skills append. The installer backs up and tracks every
45
+ carriers. Older builds require parent-local suppression and removal of the
46
+ separate thread-level skills append. The 2026.10.6/2026.10.8 recipe suppresses live
47
+ persona, memory and OpenClaw skills at their producers, so initial threads, reuse,
48
+ parent-local inference and compaction all receive the frozen context. Native
49
+ AGENTS and operational context remain live; cron guidance and execution permissions
50
+ are unchanged. The installer backs up and tracks every
44
51
  patched bundle; old single-file receipts remain restorable. Runtime module paths
45
52
  come from the loader's selected entry, never its temporary execution capture.
46
53
  It validates private seams and is not a promise of compatibility with unseen layouts.
@@ -54,7 +61,7 @@ Remote-node-only skills are not added; this is for dedicated local Mac agents.
54
61
  npm ci
55
62
  npm run preflight
56
63
  npm pack
57
- openclaw plugins install ./unblocklabs-unblock-codex-prompt-0.2.1.tgz --force --accept-capabilities
64
+ openclaw plugins install ./unblocklabs-unblock-codex-prompt-0.2.3.tgz --force --accept-capabilities
58
65
  ```
59
66
 
60
67
  Include the plugin in `plugins.allow` if applicable, then configure:
@@ -137,6 +144,14 @@ possible higher-priority Codex config override. An upgrade replacing the adapter
137
144
  removes the guard; startup status is diagnostic, not a global inference gate.
138
145
  Restore before upgrading, then revalidate compatibility before reenrollment.
139
146
 
147
+ If an upgrade already removed the old package generation, do not run restore
148
+ against replacement files or rewrite receipt paths. Verify that every old target
149
+ is absent and each retained original backup matches its receipt hash, privately
150
+ archive the stale receipt, then install the reviewed adapter against the current
151
+ inspected official package. Keep the frozen snapshot and operator-selected launcher intact
152
+ unless a new snapshot was requested. A source update still requires idle activation
153
+ and fresh-session verification; disk status alone does not prove adoption.
154
+
140
155
  ### Daily shared-prompt refresh
141
156
 
142
157
  No additional setting is needed beyond an enrolled `promptUrl`. The service checks
package/dist/adapter.js CHANGED
@@ -5,7 +5,7 @@ import { readOptional, replaceFile, sha256 } from "./manager.js";
5
5
  import { openclawRoot } from "./compiler.js";
6
6
  import { assertSupportedVersion, MINIMUM_OPENCLAW } from "./compatibility.js";
7
7
  const MARKER = "// unblock-codex-prompt bridge v1";
8
- const BUNDLE_NAME = /^thread-(lifecycle|requests)-[\w-]+\.m?js$/u;
8
+ const BUNDLE_NAME = /^(?:thread-(?:lifecycle|requests|policy)|dynamic-tools|attempt-workspace-context|run-attempt)-[\w-]+\.m?js$/u;
9
9
  const HASH = /^[a-f0-9]{64}$/u;
10
10
  // Reviewed context carriers, independent of release-specific bundle names.
11
11
  const CONTEXT_HASHES = {
@@ -16,6 +16,17 @@ const CONTEXT_HASHES = {
16
16
  legacyThread: "703a32a0b18eacd45a9fd3d68c04a13527bdbeaae50a11a6b76768b84b99a9ed",
17
17
  thread: "3aa4c414cb449cdb9167e6ed2df9397559176310ce7cff882652bd669ac8cee8",
18
18
  };
19
+ // Identical context producers/carriers in the published 2026.10.6 and 2026.10.8 bundles.
20
+ const REFRESHABLE_CONTEXT_HASHES = {
21
+ buildDeveloperInstructions: "4b4054cdd5ce65358508182d65e71a4c742b2e9debfa51eb65c5b4e26d1eb0bb",
22
+ buildCodexWorkspaceBootstrapContext: "91bb36eddcf16741957a775af106ab3226ec02dbbe2576b71675ea6db922c0b9",
23
+ renderCodexSkillsInstructions: "b3b53dccaa80cbd80070f69939909ed7947e5503ab128630a42ed6e255c56039",
24
+ prepareCodexAttemptContext: "5e38fc7a339ef241acfce3fa1dfae8d06d7f2c4106ee0e511108e680e262a67a",
25
+ buildCodexParentLocalInstructions: "d68a84359b515949483f5559f3ab4ef6b43f9b5005d5976f5e1559bb9a9f651b",
26
+ buildCodexThreadConfiguration: "e6994e63d7f408b82ea55a9e9c3e1a87967e13c168d6d72a50a4c16466cca093",
27
+ refreshCodexThreadInstructions: "23068afa77e3286acc06885b7440d6a1b44ede4c93a6e322fbbbcd6df8fb8777",
28
+ restoreCodexThreadInstructionsAfterCompaction: "86d6f792175cfbc604f5408016e00b77ed1f7fbefa33b0210d6fbf8d56a2c0a4",
29
+ };
19
30
  function receiptPath(agentDir) { return join(agentDir, "codex-home", ".unblock-codex-prompt-adapter.json"); }
20
31
  function receiptFiles(receipt) { return receipt.schema === 1 ? [receipt] : receipt.files; }
21
32
  function backupPath(agentDir, file, legacy = false) {
@@ -57,6 +68,22 @@ function parseReceipt(source) {
57
68
  return value.schema === 1 ? { schema: 1, packageDir, ...files[0] } : { schema: 2, packageDir, files };
58
69
  }
59
70
  export async function readReceipt(agentDir) { return parseReceipt(await readOptional(receiptPath(agentDir))); }
71
+ function bridgePrefix(agentDir, agentId, runtimePath) {
72
+ return `${MARKER}
73
+ import { createRequire as unblockPromptCreateRequire } from "node:module";
74
+ const unblockPromptRequire = unblockPromptCreateRequire(import.meta.url);
75
+ function unblockFrozenPolicy(params) {
76
+ const plugins = params.config?.plugins;
77
+ const entry = plugins?.entries?.["unblock-codex-prompt"];
78
+ if (plugins?.enabled === false || entry?.enabled !== true || entry.config?.frozenContext !== true ||
79
+ (Array.isArray(plugins.allow) && !plugins.allow.includes("unblock-codex-prompt"))) return null;
80
+ const target = entry.config?.agentId ?? "main";
81
+ if (params.agentId !== target) return null;
82
+ if (target !== ${JSON.stringify(agentId)}) throw new Error("Frozen bridge target changed; restore and reinstall the adapter");
83
+ return unblockPromptRequire(${JSON.stringify(runtimePath)}).readFrozenPolicy(${JSON.stringify(agentDir)});
84
+ }
85
+ `;
86
+ }
60
87
  export function patchAdapterSources(sources, agentDir, agentId, runtimePath) {
61
88
  if (sources.some(source => source.includes(MARKER)))
62
89
  throw new Error("Unsupported Codex adapter shape: already patched");
@@ -68,6 +95,9 @@ export function patchAdapterSources(sources, agentDir, agentId, runtimePath) {
68
95
  throw new Error("Unsupported Codex adapter shape: " + name);
69
96
  return matches[0];
70
97
  };
98
+ if (functions.some(fn => fn.node.id?.name === "buildCodexWorkspaceBootstrapContext")) {
99
+ return patchRefreshableAdapter(sources, find, bridgePrefix(agentDir, agentId, runtimePath));
100
+ }
71
101
  const developer = find("buildDeveloperInstructions");
72
102
  const collaboration = find("buildTurnScopedCollaborationInstructions");
73
103
  const parent = functions.some(fn => fn.node.id?.name === "buildCodexParentLocalInstructions") ? find("buildCodexParentLocalInstructions") : null;
@@ -89,20 +119,7 @@ export function patchAdapterSources(sources, agentDir, agentId, runtimePath) {
89
119
  (thread && thread.bundle !== developer.bundle) ||
90
120
  (split ? !thread || fingerprint(thread) !== CONTEXT_HASHES.thread : thread !== null && fingerprint(thread) !== CONTEXT_HASHES.legacyThread))
91
121
  throw new Error("Unsupported Codex context assembly; review the changed upstream functions before syncing");
92
- const prefix = `${MARKER}
93
- import { createRequire as unblockPromptCreateRequire } from "node:module";
94
- const unblockPromptRequire = unblockPromptCreateRequire(import.meta.url);
95
- function unblockFrozenPolicy(params) {
96
- const plugins = params.config?.plugins;
97
- const entry = plugins?.entries?.["unblock-codex-prompt"];
98
- if (plugins?.enabled === false || entry?.enabled !== true || entry.config?.frozenContext !== true ||
99
- (Array.isArray(plugins.allow) && !plugins.allow.includes("unblock-codex-prompt"))) return null;
100
- const target = entry.config?.agentId ?? "main";
101
- if (params.agentId !== target) return null;
102
- if (target !== ${JSON.stringify(agentId)}) throw new Error("Frozen bridge target changed; restore and reinstall the adapter");
103
- return unblockPromptRequire(${JSON.stringify(runtimePath)}).readFrozenPolicy(${JSON.stringify(agentDir)});
104
- }
105
- `;
122
+ const prefix = bridgePrefix(agentDir, agentId, runtimePath);
106
123
  const insertions = [
107
124
  { fn: developer, text: '\n\tconst unblockPolicy = unblockFrozenPolicy(params);\n\tif (unblockPolicy !== null) return [unblockPolicy, params.gitCoauthorPrompt, params.extraSystemPrompt].filter(section => typeof section === "string" && section.trim()).join("\\n\\n");' },
108
125
  { fn: collaboration, text: '\n\tif (unblockFrozenPolicy(params) !== null) return params.trigger === "cron" ? buildCronCollaborationInstructions() : buildDefaultCollaborationInstructions();' },
@@ -119,6 +136,40 @@ function unblockFrozenPolicy(params) {
119
136
  return edits.length ? prefix + source : source;
120
137
  });
121
138
  }
139
+ function patchRefreshableAdapter(sources, find, prefix) {
140
+ for (const [name, expected] of Object.entries(REFRESHABLE_CONTEXT_HASHES)) {
141
+ const { node, bundle } = find(name);
142
+ if (sha256(sources[bundle].slice(node.start, node.end)) !== expected) {
143
+ throw new Error("Unsupported Codex context assembly: " + name + "; review the changed upstream function before syncing");
144
+ }
145
+ }
146
+ const developer = find("buildDeveloperInstructions");
147
+ const skills = find("renderCodexSkillsInstructions");
148
+ const workspace = find("buildCodexWorkspaceBootstrapContext");
149
+ const originalWorkspace = sources[workspace.bundle].slice(workspace.node.start, workspace.node.end)
150
+ .replace("async function buildCodexWorkspaceBootstrapContext", "async function unblockOriginalWorkspaceContext");
151
+ // Suppress at the producers: initial, warm, fork, parent-local and post-compaction
152
+ // carriers all receive the same empty refreshable section. Native AGENTS stays live.
153
+ const wrapper = `async function buildCodexWorkspaceBootstrapContext(params) {
154
+ const policy = unblockFrozenPolicy(params.params);
155
+ const context = await unblockOriginalWorkspaceContext(params);
156
+ return policy === null ? context : { ...context, personaInstructions: undefined, sharedPersonaInstructions: undefined, memoryInstructions: undefined };
157
+ }
158
+ `;
159
+ const edits = [
160
+ { bundle: developer.bundle, start: developer.node.body.start + 1, end: developer.node.body.start + 1,
161
+ text: '\n\tconst unblockPolicy = unblockFrozenPolicy(params);\n\tif (unblockPolicy !== null) return [unblockPolicy, params.gitCoauthorPrompt, params.extraSystemPrompt].filter(section => typeof section === "string" && section.trim()).join("\\n\\n");' },
162
+ { bundle: skills.bundle, start: skills.node.body.start + 1, end: skills.node.body.start + 1,
163
+ text: '\n\tif (unblockFrozenPolicy(params.attempt) !== null) return;' },
164
+ { bundle: workspace.bundle, start: workspace.node.start, end: workspace.node.end, text: wrapper + originalWorkspace },
165
+ ];
166
+ return sources.map((source, bundle) => {
167
+ const selected = edits.filter(edit => edit.bundle === bundle).sort((a, b) => b.start - a.start);
168
+ for (const edit of selected)
169
+ source = source.slice(0, edit.start) + edit.text + source.slice(edit.end);
170
+ return selected.length ? prefix + source : source;
171
+ });
172
+ }
122
173
  export async function prepareAdapter(agentDir, agentId, suppliedDir, runtimePath) {
123
174
  await openclawRoot();
124
175
  if (!isAbsolute(runtimePath) || await readOptional(runtimePath) === null)
package/dist/index.js CHANGED
@@ -54,13 +54,11 @@ async function resolveTarget(config) {
54
54
  if (!appServer || typeof appServer !== "object" || Array.isArray(appServer))
55
55
  throw new Error("Invalid Codex appServer config");
56
56
  if (("transport" in appServer && appServer.transport !== "stdio") ||
57
- ("homeScope" in appServer && appServer.homeScope !== "agent") ||
58
- ("command" in appServer && appServer.command !== undefined)) {
59
- throw new Error("Only OpenClaw-managed, agent-scoped local stdio Codex is supported");
60
- }
61
- if ("args" in appServer && appServer.args !== undefined) {
62
- throw new Error("Custom Codex appServer.args may override the prompt; remove them or manage the prompt outside this plugin");
57
+ ("homeScope" in appServer && appServer.homeScope !== "agent")) {
58
+ throw new Error("Only agent-scoped local stdio Codex is supported");
63
59
  }
60
+ // OpenClaw owns launching Codex, including operator-selected binaries/args.
61
+ // We own this agent's prompt files, not its executable selection.
64
62
  }
65
63
  return { agentDir: resolveAgentDir(config, settings.agentId), settings, workspaceDir: resolveAgentWorkspaceDir(config, settings.agentId) };
66
64
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "unblock-codex-prompt",
3
3
  "name": "Unblock Codex Prompt",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "description": "Maintain an agent-scoped Codex base prompt and its model_instructions_file pointer.",
6
6
  "activation": { "onStartup": true },
7
7
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unblocklabs/unblock-codex-prompt",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Manage a shared Codex base prompt for an OpenClaw agent.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",