create-principles-disciple 1.132.2 → 1.132.4

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.
Files changed (229) hide show
  1. package/codex-adapter/dist/codec/index.d.ts +6 -0
  2. package/codex-adapter/dist/codec/index.js +5 -0
  3. package/codex-adapter/dist/codec/input-decoder.d.ts +13 -0
  4. package/codex-adapter/dist/codec/input-decoder.js +103 -0
  5. package/codex-adapter/dist/codec/output-encoder.d.ts +32 -0
  6. package/codex-adapter/dist/codec/output-encoder.js +51 -0
  7. package/codex-adapter/dist/host-adapter.d.ts +30 -0
  8. package/codex-adapter/dist/host-adapter.js +28 -0
  9. package/codex-adapter/dist/index.d.ts +25 -0
  10. package/codex-adapter/dist/index.js +19 -0
  11. package/codex-adapter/dist/ingestion/admission.d.ts +54 -0
  12. package/codex-adapter/dist/ingestion/admission.js +137 -0
  13. package/codex-adapter/dist/ingestion/catch-up.d.ts +41 -0
  14. package/codex-adapter/dist/ingestion/catch-up.js +94 -0
  15. package/codex-adapter/dist/ingestion/codex-home.d.ts +19 -0
  16. package/codex-adapter/dist/ingestion/codex-home.js +43 -0
  17. package/codex-adapter/dist/ingestion/codex-version.d.ts +30 -0
  18. package/codex-adapter/dist/ingestion/codex-version.js +53 -0
  19. package/codex-adapter/dist/ingestion/ingestion-fields.d.ts +17 -0
  20. package/codex-adapter/dist/ingestion/ingestion-fields.js +28 -0
  21. package/codex-adapter/dist/ingestion/ingestion.d.ts +71 -0
  22. package/codex-adapter/dist/ingestion/ingestion.js +248 -0
  23. package/codex-adapter/dist/ingestion/transcript-decoder.d.ts +72 -0
  24. package/codex-adapter/dist/ingestion/transcript-decoder.js +485 -0
  25. package/codex-adapter/dist/ingestion/transcript-locate.d.ts +13 -0
  26. package/codex-adapter/dist/ingestion/transcript-locate.js +75 -0
  27. package/codex-adapter/dist/ingestion/transcript-path.d.ts +25 -0
  28. package/codex-adapter/dist/ingestion/transcript-path.js +87 -0
  29. package/codex-adapter/dist/pd-hook.d.ts +9 -0
  30. package/codex-adapter/dist/pd-hook.js +159 -0
  31. package/codex-adapter/dist/tool-semantics.d.ts +20 -0
  32. package/codex-adapter/dist/tool-semantics.js +27 -0
  33. package/codex-adapter/dist/worker/workspace-worker.d.ts +59 -0
  34. package/codex-adapter/dist/worker/workspace-worker.js +270 -0
  35. package/codex-adapter/package.json +52 -0
  36. package/console/dist/server/routes/update.js +187 -19
  37. package/console/dist/server/update/mutation-controller.d.ts +18 -0
  38. package/console/dist/server/update/mutation-controller.js +29 -0
  39. package/console/dist/server/utils/installed-layout.d.ts +1 -0
  40. package/console/dist/server/utils/installed-layout.js +2 -0
  41. package/console/package.json +1 -0
  42. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +15 -0
  43. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  44. package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts +2 -0
  45. package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js +71 -0
  47. package/core/dist/runtime-v2/adapter/__tests__/pd-stream-simple.test.js.map +1 -0
  48. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts +2 -0
  49. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.d.ts.map +1 -0
  50. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js +219 -0
  51. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-http-transport.test.js.map +1 -0
  52. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.d.ts.map +1 -1
  53. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js +2 -3
  54. package/core/dist/runtime-v2/adapter/artificer-l2-adapter.js.map +1 -1
  55. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts +12 -1
  56. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.d.ts.map +1 -1
  57. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js +15 -2
  58. package/core/dist/runtime-v2/adapter/l2-agent-loop-adapter.js.map +1 -1
  59. package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts +50 -0
  60. package/core/dist/runtime-v2/adapter/pi-ai-http-transport.d.ts.map +1 -0
  61. package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js +93 -0
  62. package/core/dist/runtime-v2/adapter/pi-ai-http-transport.js.map +1 -0
  63. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +5 -0
  65. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  66. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +152 -0
  67. package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -1
  68. package/core/dist/runtime-v2/internalization/owner-review.d.ts +7 -1
  69. package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -1
  70. package/core/dist/runtime-v2/internalization/owner-review.js +22 -3
  71. package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -1
  72. package/core/package.json +3 -2
  73. package/dist/installer.d.ts +32 -0
  74. package/dist/installer.d.ts.map +1 -1
  75. package/dist/installer.js +198 -2
  76. package/dist/installer.js.map +1 -1
  77. package/dist/installers/codex-host-installer.d.ts.map +1 -1
  78. package/dist/installers/codex-host-installer.js +5 -3
  79. package/dist/installers/codex-host-installer.js.map +1 -1
  80. package/dist/update/release-manager-authority.d.ts +69 -0
  81. package/dist/update/release-manager-authority.d.ts.map +1 -0
  82. package/dist/update/release-manager-authority.js +125 -0
  83. package/dist/update/release-manager-authority.js.map +1 -0
  84. package/dist/utils/workspace-divergence.d.ts +36 -0
  85. package/dist/utils/workspace-divergence.d.ts.map +1 -0
  86. package/dist/utils/workspace-divergence.js +87 -0
  87. package/dist/utils/workspace-divergence.js.map +1 -0
  88. package/install-layout/dist/index.d.ts +5 -0
  89. package/install-layout/dist/index.js +1 -0
  90. package/install-layout/package.json +1 -1
  91. package/package.json +4 -2
  92. package/plugin/dist/bundle.js +870 -734
  93. package/plugin/dist/governance-audit.js +381 -247
  94. package/plugin/dist/openclaw-sdk.d.ts +3 -0
  95. package/plugin/dist/rulehost-evidence.js +420 -286
  96. package/plugin/dist/service/subagent-workflow/types.d.ts +2 -2
  97. package/plugin/dist/utils/retry.d.ts +1 -1
  98. package/plugin/dist/utils/workspace-resolver.d.ts +35 -4
  99. package/plugin/openclaw.plugin.json +1 -1
  100. package/plugin/package.json +1 -1
  101. package/release-manager/dist/i18n.d.ts +6 -0
  102. package/release-manager/dist/i18n.d.ts.map +1 -0
  103. package/release-manager/dist/i18n.js +234 -0
  104. package/release-manager/dist/i18n.js.map +1 -0
  105. package/release-manager/dist/index.d.ts +7 -0
  106. package/release-manager/dist/index.d.ts.map +1 -0
  107. package/release-manager/dist/index.js +474 -0
  108. package/release-manager/dist/index.js.map +1 -0
  109. package/release-manager/dist/installer.d.ts +217 -0
  110. package/release-manager/dist/installer.d.ts.map +1 -0
  111. package/release-manager/dist/installer.js +2451 -0
  112. package/release-manager/dist/installer.js.map +1 -0
  113. package/release-manager/dist/installers/codex-host-installer.d.ts +29 -0
  114. package/release-manager/dist/installers/codex-host-installer.d.ts.map +1 -0
  115. package/release-manager/dist/installers/codex-host-installer.js +496 -0
  116. package/release-manager/dist/installers/codex-host-installer.js.map +1 -0
  117. package/release-manager/dist/installers/index.d.ts +40 -0
  118. package/release-manager/dist/installers/index.d.ts.map +1 -0
  119. package/release-manager/dist/installers/index.js +35 -0
  120. package/release-manager/dist/installers/index.js.map +1 -0
  121. package/release-manager/dist/installers/openclaw-host-installer.d.ts +18 -0
  122. package/release-manager/dist/installers/openclaw-host-installer.d.ts.map +1 -0
  123. package/release-manager/dist/installers/openclaw-host-installer.js +414 -0
  124. package/release-manager/dist/installers/openclaw-host-installer.js.map +1 -0
  125. package/release-manager/dist/mvp-config.d.ts +197 -0
  126. package/release-manager/dist/mvp-config.d.ts.map +1 -0
  127. package/release-manager/dist/mvp-config.js +509 -0
  128. package/release-manager/dist/mvp-config.js.map +1 -0
  129. package/release-manager/dist/prompts.d.ts +30 -0
  130. package/release-manager/dist/prompts.d.ts.map +1 -0
  131. package/release-manager/dist/prompts.js +231 -0
  132. package/release-manager/dist/prompts.js.map +1 -0
  133. package/release-manager/dist/skill-language.d.ts +32 -0
  134. package/release-manager/dist/skill-language.d.ts.map +1 -0
  135. package/release-manager/dist/skill-language.js +107 -0
  136. package/release-manager/dist/skill-language.js.map +1 -0
  137. package/release-manager/dist/uninstaller.d.ts +55 -0
  138. package/release-manager/dist/uninstaller.d.ts.map +1 -0
  139. package/release-manager/dist/uninstaller.js +409 -0
  140. package/release-manager/dist/uninstaller.js.map +1 -0
  141. package/release-manager/dist/update/atomic-file.d.ts +35 -0
  142. package/release-manager/dist/update/atomic-file.d.ts.map +1 -0
  143. package/release-manager/dist/update/atomic-file.js +120 -0
  144. package/release-manager/dist/update/atomic-file.js.map +1 -0
  145. package/release-manager/dist/update/bootstrap-protocol.d.ts +55 -0
  146. package/release-manager/dist/update/bootstrap-protocol.d.ts.map +1 -0
  147. package/release-manager/dist/update/bootstrap-protocol.js +132 -0
  148. package/release-manager/dist/update/bootstrap-protocol.js.map +1 -0
  149. package/release-manager/dist/update/channel-metadata.d.ts +28 -0
  150. package/release-manager/dist/update/channel-metadata.d.ts.map +1 -0
  151. package/release-manager/dist/update/channel-metadata.js +90 -0
  152. package/release-manager/dist/update/channel-metadata.js.map +1 -0
  153. package/release-manager/dist/update/channel-promotion.d.ts +39 -0
  154. package/release-manager/dist/update/channel-promotion.d.ts.map +1 -0
  155. package/release-manager/dist/update/channel-promotion.js +55 -0
  156. package/release-manager/dist/update/channel-promotion.js.map +1 -0
  157. package/release-manager/dist/update/data-compatibility.d.ts +35 -0
  158. package/release-manager/dist/update/data-compatibility.d.ts.map +1 -0
  159. package/release-manager/dist/update/data-compatibility.js +45 -0
  160. package/release-manager/dist/update/data-compatibility.js.map +1 -0
  161. package/release-manager/dist/update/install-layout.d.ts +59 -0
  162. package/release-manager/dist/update/install-layout.d.ts.map +1 -0
  163. package/release-manager/dist/update/install-layout.js +127 -0
  164. package/release-manager/dist/update/install-layout.js.map +1 -0
  165. package/release-manager/dist/update/legacy-migration.d.ts +56 -0
  166. package/release-manager/dist/update/legacy-migration.d.ts.map +1 -0
  167. package/release-manager/dist/update/legacy-migration.js +254 -0
  168. package/release-manager/dist/update/legacy-migration.js.map +1 -0
  169. package/release-manager/dist/update/product-identity.d.ts +50 -0
  170. package/release-manager/dist/update/product-identity.d.ts.map +1 -0
  171. package/release-manager/dist/update/product-identity.js +63 -0
  172. package/release-manager/dist/update/product-identity.js.map +1 -0
  173. package/release-manager/dist/update/release-asset-manifest.d.ts +31 -0
  174. package/release-manager/dist/update/release-asset-manifest.d.ts.map +1 -0
  175. package/release-manager/dist/update/release-asset-manifest.js +161 -0
  176. package/release-manager/dist/update/release-asset-manifest.js.map +1 -0
  177. package/release-manager/dist/update/release-identity.d.ts +33 -0
  178. package/release-manager/dist/update/release-identity.d.ts.map +1 -0
  179. package/release-manager/dist/update/release-identity.js +108 -0
  180. package/release-manager/dist/update/release-identity.js.map +1 -0
  181. package/release-manager/dist/update/release-manager-authority.d.ts +69 -0
  182. package/release-manager/dist/update/release-manager-authority.d.ts.map +1 -0
  183. package/release-manager/dist/update/release-manager-authority.js +125 -0
  184. package/release-manager/dist/update/release-manager-authority.js.map +1 -0
  185. package/release-manager/dist/update/release-manager.d.ts +96 -0
  186. package/release-manager/dist/update/release-manager.d.ts.map +1 -0
  187. package/release-manager/dist/update/release-manager.js +253 -0
  188. package/release-manager/dist/update/release-manager.js.map +1 -0
  189. package/release-manager/dist/update/release-metadata.d.ts +57 -0
  190. package/release-manager/dist/update/release-metadata.d.ts.map +1 -0
  191. package/release-manager/dist/update/release-metadata.js +200 -0
  192. package/release-manager/dist/update/release-metadata.js.map +1 -0
  193. package/release-manager/dist/update/release-policy.d.ts +51 -0
  194. package/release-manager/dist/update/release-policy.d.ts.map +1 -0
  195. package/release-manager/dist/update/release-policy.js +103 -0
  196. package/release-manager/dist/update/release-policy.js.map +1 -0
  197. package/release-manager/dist/update/rollback-policy.d.ts +56 -0
  198. package/release-manager/dist/update/rollback-policy.d.ts.map +1 -0
  199. package/release-manager/dist/update/rollback-policy.js +61 -0
  200. package/release-manager/dist/update/rollback-policy.js.map +1 -0
  201. package/release-manager/dist/update/transaction-journal.d.ts +131 -0
  202. package/release-manager/dist/update/transaction-journal.d.ts.map +1 -0
  203. package/release-manager/dist/update/transaction-journal.js +346 -0
  204. package/release-manager/dist/update/transaction-journal.js.map +1 -0
  205. package/release-manager/dist/update/trust-metadata.d.ts +55 -0
  206. package/release-manager/dist/update/trust-metadata.d.ts.map +1 -0
  207. package/release-manager/dist/update/trust-metadata.js +170 -0
  208. package/release-manager/dist/update/trust-metadata.js.map +1 -0
  209. package/release-manager/dist/update/update-history.d.ts +46 -0
  210. package/release-manager/dist/update/update-history.d.ts.map +1 -0
  211. package/release-manager/dist/update/update-history.js +123 -0
  212. package/release-manager/dist/update/update-history.js.map +1 -0
  213. package/release-manager/dist/utils/config-file-io.d.ts +26 -0
  214. package/release-manager/dist/utils/config-file-io.d.ts.map +1 -0
  215. package/release-manager/dist/utils/config-file-io.js +128 -0
  216. package/release-manager/dist/utils/config-file-io.js.map +1 -0
  217. package/release-manager/dist/utils/env.d.ts +60 -0
  218. package/release-manager/dist/utils/env.d.ts.map +1 -0
  219. package/release-manager/dist/utils/env.js +308 -0
  220. package/release-manager/dist/utils/env.js.map +1 -0
  221. package/release-manager/dist/utils/logger.d.ts +15 -0
  222. package/release-manager/dist/utils/logger.d.ts.map +1 -0
  223. package/release-manager/dist/utils/logger.js +30 -0
  224. package/release-manager/dist/utils/logger.js.map +1 -0
  225. package/release-manager/dist/utils/workspace-divergence.d.ts +36 -0
  226. package/release-manager/dist/utils/workspace-divergence.d.ts.map +1 -0
  227. package/release-manager/dist/utils/workspace-divergence.js +87 -0
  228. package/release-manager/dist/utils/workspace-divergence.js.map +1 -0
  229. package/release-manager/package.json +79 -0
@@ -0,0 +1,485 @@
1
+ /**
2
+ * Codex rollout transcript incremental decoder (Codex Governance Closure
3
+ * Slice A; SPEC rev 2 §9, G1 probe report §6).
4
+ *
5
+ * Decodes a bounded byte window from the durable checkpoint offset and
6
+ * projects ONLY governance-relevant visible facts (SPEC §12 privacy
7
+ * boundary): genuine visible user messages (`content_item_kinds[0] ===
8
+ * "user.text"` — host-injected context arrives as user-role records with
9
+ * other kinds), visible assistant commentary/final messages, and tool
10
+ * execution facts anchored on the `event_msg item_completed
11
+ * CommandExecution` record whose `item.id` IS the hook `tool_use_id` id
12
+ * space (the G1-verified bridge; the model-level `call_id` is a different
13
+ * space). Hidden reasoning, system/developer prompts, world_state,
14
+ * host-injected context, and unknown record bodies are identified by shape
15
+ * and dropped before persistence — never projected, logged, or emitted.
16
+ *
17
+ * Failure classes are distinct (SPEC §14): an incomplete final line (no
18
+ * trailing newline at EOF) is a transient append/flush boundary — the
19
+ * checkpoint does not advance past it and the next ingestion retries; a
20
+ * complete line that fails JSON parsing or envelope validation is a stable
21
+ * malformed record — decoding stops at it without advancing, with a
22
+ * structured reason. Unknown-but-well-formed record types are skipped with
23
+ * a bounded warning and DO advance.
24
+ */
25
+ import fs from 'node:fs';
26
+ export const CODEX_INGESTION_MAX_BATCH_BYTES = 1024 * 1024;
27
+ export const CODEX_INGESTION_MAX_BATCH_RECORDS = 256;
28
+ /** Thrown by the port when the opened object is not the validated one (replacement/TOCTOU). */
29
+ export class TranscriptReplacedError extends Error {
30
+ constructor() {
31
+ super('transcript_replaced_after_validation');
32
+ this.name = 'TranscriptReplacedError';
33
+ }
34
+ }
35
+ function identityMatches(opened, expected) {
36
+ // Size+mtime fingerprint must ALWAYS hold. When the platform reports a
37
+ // Number-safe inode on both sides, the inode must match too (it is what
38
+ // catches delete+recreate replacements whose inode got reused on Linux CI
39
+ // with a same-ms write; a same-size same-ms recreate remains outside the
40
+ // threat model for an append-only host-owned file). On platforms whose
41
+ // ino exceeds Number.MAX_SAFE_INTEGER (Windows file IDs), the inode is not
42
+ // reliably comparable and the fingerprint alone decides. A false refusal
43
+ // is the safe direction: the hook degrades observably and the next Stop
44
+ // revalidates and retries.
45
+ if (opened.size !== expected.size || opened.mtimeMs !== expected.mtimeMs)
46
+ return false;
47
+ const comparableIno = expected.ino !== 0 && expected.dev !== 0
48
+ && opened.ino !== 0 && Number.isSafeInteger(expected.ino) && Number.isSafeInteger(opened.ino);
49
+ if (comparableIno)
50
+ return opened.ino === expected.ino && opened.dev === expected.dev;
51
+ return true;
52
+ }
53
+ export function createNodeTranscriptPort() {
54
+ return {
55
+ read({ canonicalPath, offset, maxBytes, expectedIdentity }) {
56
+ const fd = fs.openSync(canonicalPath, 'r');
57
+ try {
58
+ // Post-open revalidation (G1 §7 / SPEC §9): fstat the OPEN OBJECT —
59
+ // not the path — and re-prove it is the regular file the validator
60
+ // approved, still at the expected identity. A path swapped between
61
+ // validation and open (replacement/symlink TOCTOU) is refused before
62
+ // a single byte is read.
63
+ const stats = fs.fstatSync(fd);
64
+ if (!stats.isFile())
65
+ throw new Error('transcript_is_not_regular_file');
66
+ if (expectedIdentity !== undefined && !identityMatches(stats, expectedIdentity)) {
67
+ throw new TranscriptReplacedError();
68
+ }
69
+ const length = Math.max(0, Math.min(maxBytes, stats.size - offset));
70
+ if (length === 0)
71
+ return { bytes: Buffer.alloc(0), fileSize: stats.size };
72
+ const buffer = Buffer.alloc(length);
73
+ const bytesRead = fs.readSync(fd, buffer, 0, length, offset);
74
+ // Post-read byte bound re-verification (EP-08): never trust the
75
+ // pre-read stat for the enforced cap.
76
+ const actual = bytesRead < 0 ? 0 : bytesRead;
77
+ if (actual > maxBytes)
78
+ throw new Error('transcript_read_exceeded_bound');
79
+ return { bytes: buffer.subarray(0, actual), fileSize: stats.size };
80
+ }
81
+ finally {
82
+ fs.closeSync(fd);
83
+ }
84
+ },
85
+ };
86
+ }
87
+ function isRecord(value) {
88
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
89
+ }
90
+ function own(value, key) {
91
+ return Object.hasOwn(value, key) ? Object.getOwnPropertyDescriptor(value, key)?.value : undefined;
92
+ }
93
+ function metadataTurnId(payload) {
94
+ const passthrough = own(payload, 'internal_chat_message_metadata_passthrough');
95
+ if (!isRecord(passthrough))
96
+ return null;
97
+ const turnId = own(passthrough, 'turn_id');
98
+ return typeof turnId === 'string' && turnId.length > 0 ? turnId : null;
99
+ }
100
+ function contentItemKinds(payload) {
101
+ const passthrough = own(payload, 'internal_chat_message_metadata_passthrough');
102
+ if (!isRecord(passthrough))
103
+ return [];
104
+ const kinds = own(passthrough, 'content_item_kinds');
105
+ if (!Array.isArray(kinds))
106
+ return [];
107
+ return kinds.filter((kind) => typeof kind === 'string');
108
+ }
109
+ function joinContentText(payload, type) {
110
+ const content = own(payload, 'content');
111
+ if (!Array.isArray(content))
112
+ return null;
113
+ const parts = [];
114
+ for (const item of content) {
115
+ if (!isRecord(item))
116
+ continue;
117
+ if (own(item, 'type') === type && typeof own(item, 'text') === 'string')
118
+ parts.push(own(item, 'text'));
119
+ }
120
+ return parts.length > 0 ? parts.join('') : null;
121
+ }
122
+ function warnBounded(context, warning) {
123
+ if (context.warnings.length < 16)
124
+ context.warnings.push(warning);
125
+ }
126
+ function applySessionMeta(context, payload) {
127
+ const sessionId = own(payload, 'session_id');
128
+ if (typeof sessionId === 'string' && sessionId.length > 0 && context.meta.rootSessionId === null) {
129
+ // Root-session lineage only. For subagent rollouts this value is the
130
+ // PARENT thread id (G1 §4 collision trap) — rollout identity comes from
131
+ // the file uuid, never from session_meta.session_id.
132
+ context.meta.rootSessionId = sessionId;
133
+ }
134
+ const cliVersion = own(payload, 'cli_version');
135
+ if (typeof cliVersion === 'string' && cliVersion.length > 0 && context.meta.cliVersion === null) {
136
+ context.meta.cliVersion = cliVersion;
137
+ }
138
+ const source = own(payload, 'source');
139
+ const spawn = isRecord(source) ? own(source, 'subagent') : undefined;
140
+ const threadSpawn = isRecord(spawn) ? own(spawn, 'thread_spawn') : undefined;
141
+ if (isRecord(threadSpawn)) {
142
+ const parentThreadId = own(threadSpawn, 'parent_thread_id');
143
+ if (typeof parentThreadId === 'string' && parentThreadId.length > 0)
144
+ context.meta.parentRolloutIdentity = parentThreadId;
145
+ const nickname = own(threadSpawn, 'agent_nickname');
146
+ const agentPath = own(threadSpawn, 'agent_path');
147
+ const identity = typeof nickname === 'string' && nickname.length > 0 ? nickname : typeof agentPath === 'string' && agentPath.length > 0 ? agentPath : null;
148
+ if (identity !== null)
149
+ context.meta.agentIdentity = identity;
150
+ const depth = own(threadSpawn, 'depth');
151
+ if (typeof depth === 'number' && Number.isInteger(depth) && depth >= 0)
152
+ context.meta.agentDepth = depth;
153
+ }
154
+ else {
155
+ const forkedFrom = own(payload, 'forked_from_id');
156
+ if (typeof forkedFrom === 'string' && forkedFrom.length > 0 && context.meta.parentRolloutIdentity === null) {
157
+ context.meta.parentRolloutIdentity = forkedFrom;
158
+ }
159
+ }
160
+ }
161
+ function joinItemText(item) {
162
+ const content = own(item, 'content');
163
+ if (!Array.isArray(content))
164
+ return null;
165
+ const parts = [];
166
+ for (const entry of content) {
167
+ if (!isRecord(entry))
168
+ continue;
169
+ // UserMessage items use "text", AgentMessage items use "Text".
170
+ const type = own(entry, 'type');
171
+ if ((type === 'text' || type === 'Text') && typeof own(entry, 'text') === 'string') {
172
+ parts.push(own(entry, 'text'));
173
+ }
174
+ }
175
+ return parts.length > 0 ? parts.join('') : null;
176
+ }
177
+ function observationBase(context, turnId) {
178
+ return {
179
+ hostKind: 'codex',
180
+ rolloutIdentity: context.rolloutIdentity,
181
+ rootSessionId: context.meta.rootSessionId ?? context.fallbackRootSessionId ?? turnId,
182
+ hostTurnId: turnId,
183
+ };
184
+ }
185
+ function projectRecord(context, record) {
186
+ const { type, payload, ordinal, recordByteStart, timestamp } = record;
187
+ const observedAt = timestamp ?? context.nowIso;
188
+ if (type === 'session_meta') {
189
+ applySessionMeta(context, payload);
190
+ return;
191
+ }
192
+ if (type === 'compacted') {
193
+ // Marker: replacement_history becomes the logical history going forward;
194
+ // replaced records must not be re-imported as new turns (G1 §6). The
195
+ // store tombstones prior unpromoted observations at ingest time.
196
+ context.compactionTimestamp = observedAt;
197
+ return;
198
+ }
199
+ if (type === 'world_state' || type === 'inter_agent_communication_metadata') {
200
+ // Environment/host snapshots — identified and dropped (privacy boundary).
201
+ return;
202
+ }
203
+ if (type === 'turn_context') {
204
+ // Per-turn host configuration (sandbox/permissions/model) — known type,
205
+ // no governance projection, no warning.
206
+ return;
207
+ }
208
+ if (type === 'response_item') {
209
+ const payloadType = own(payload, 'type');
210
+ if (payloadType === 'reasoning')
211
+ return; // hidden/encrypted reasoning — never projected
212
+ if (payloadType === 'custom_tool_call' || payloadType === 'function_call') {
213
+ const turnId = metadataTurnId(payload);
214
+ const callId = own(payload, 'call_id');
215
+ const name = own(payload, 'name');
216
+ if (turnId !== null) {
217
+ const queue = context.modelCallsByTurn.get(turnId) ?? [];
218
+ queue.push({ callId: typeof callId === 'string' ? callId : null, name: typeof name === 'string' ? name : null });
219
+ context.modelCallsByTurn.set(turnId, queue);
220
+ }
221
+ return;
222
+ }
223
+ if (payloadType === 'message' && own(payload, 'role') === 'user') {
224
+ // 0.150.1 marks genuine visible user input with content_item_kinds[0]
225
+ // === "user.text"; host-injected context carries other kinds. 0.148.0
226
+ // does not emit content_item_kinds at all, so this channel is used ONLY
227
+ // when the discriminator exists — the authoritative cross-version user
228
+ // channel is the item_completed UserMessage record below.
229
+ const kinds = contentItemKinds(payload);
230
+ if (kinds.length === 0 || kinds[0] !== 'user.text')
231
+ return;
232
+ const turnId = metadataTurnId(payload);
233
+ if (turnId === null) {
234
+ warnBounded(context, `observation_skipped_missing_identity:${ordinal}`);
235
+ return;
236
+ }
237
+ const text = joinContentText(payload, 'input_text');
238
+ context.observations.push({
239
+ ...observationBase(context, turnId),
240
+ kind: 'user_turn',
241
+ logicalObservationKey: `codex|${context.rolloutIdentity}|${turnId}|user`,
242
+ transcriptRecordKey: `codex|${context.rolloutIdentity}|${ordinal}`,
243
+ recordOrdinal: ordinal,
244
+ recordByteStart,
245
+ visibleText: text ?? undefined,
246
+ source: 'transcript',
247
+ completeness: text !== null ? 'complete' : 'partial',
248
+ observedAt,
249
+ });
250
+ return;
251
+ }
252
+ // Assistant/developer response_item messages are not projected here: the
253
+ // item_completed AgentMessage channel is the cross-version visible
254
+ // assistant source (0.148.0 lacks content_item_kinds discriminators).
255
+ return;
256
+ }
257
+ if (type === 'event_msg') {
258
+ const payloadType = own(payload, 'type');
259
+ if (payloadType === 'item_completed') {
260
+ const item = own(payload, 'item');
261
+ if (!isRecord(item))
262
+ return;
263
+ const itemType = own(item, 'type');
264
+ const turnId = own(payload, 'turn_id');
265
+ if (itemType === 'UserMessage') {
266
+ // Only genuine visible user turns produce UserMessage completions —
267
+ // host-injected context never does (both supported versions).
268
+ if (typeof turnId !== 'string' || turnId.length === 0) {
269
+ warnBounded(context, `observation_skipped_missing_identity:${ordinal}`);
270
+ return;
271
+ }
272
+ const text = joinItemText(item);
273
+ context.observations.push({
274
+ ...observationBase(context, turnId),
275
+ kind: 'user_turn',
276
+ logicalObservationKey: `codex|${context.rolloutIdentity}|${turnId}|user`,
277
+ transcriptRecordKey: `codex|${context.rolloutIdentity}|${ordinal}`,
278
+ recordOrdinal: ordinal,
279
+ recordByteStart,
280
+ visibleText: text ?? undefined,
281
+ source: 'transcript',
282
+ completeness: text !== null ? 'complete' : 'partial',
283
+ observedAt,
284
+ });
285
+ return;
286
+ }
287
+ if (itemType === 'AgentMessage') {
288
+ if (typeof turnId !== 'string' || turnId.length === 0) {
289
+ warnBounded(context, `observation_skipped_missing_identity:${ordinal}`);
290
+ return;
291
+ }
292
+ const itemId = own(item, 'id');
293
+ if (typeof itemId !== 'string' || itemId.length === 0) {
294
+ warnBounded(context, `observation_skipped_missing_identity:${ordinal}`);
295
+ return;
296
+ }
297
+ const text = joinItemText(item);
298
+ // AgentMessage phase sits on the item (fixture-verified; keep the
299
+ // payload sibling as a tolerated alternative).
300
+ const phase = own(item, 'phase') ?? own(payload, 'phase');
301
+ context.observations.push({
302
+ ...observationBase(context, turnId),
303
+ kind: 'assistant_turn',
304
+ logicalObservationKey: `codex|${context.rolloutIdentity}|${turnId}|${itemId}`,
305
+ transcriptRecordKey: `codex|${context.rolloutIdentity}|${ordinal}`,
306
+ recordOrdinal: ordinal,
307
+ recordByteStart,
308
+ assistantItemId: itemId,
309
+ phase: typeof phase === 'string' ? phase : undefined,
310
+ visibleText: text ?? undefined,
311
+ source: 'transcript',
312
+ completeness: text !== null ? 'complete' : 'partial',
313
+ observedAt,
314
+ });
315
+ return;
316
+ }
317
+ if (itemType === 'CommandExecution') {
318
+ const toolUseId = own(item, 'id');
319
+ if (typeof toolUseId !== 'string' || toolUseId.length === 0 || typeof turnId !== 'string' || turnId.length === 0) {
320
+ warnBounded(context, `observation_skipped_missing_identity:${ordinal}`);
321
+ return;
322
+ }
323
+ // FIFO-pair with the model-level call in the same turn for the tool
324
+ // name / call_id enrichment (execution order; G1 bridge fixture).
325
+ const queue = context.modelCallsByTurn.get(turnId);
326
+ const paired = queue !== undefined && queue.length > 0 ? queue.shift() : undefined;
327
+ if (queue !== undefined && queue.length === 0)
328
+ context.modelCallsByTurn.delete(turnId);
329
+ const exitCode = own(item, 'exit_code');
330
+ const command = own(item, 'command');
331
+ const stdout = own(item, 'stdout');
332
+ const stderr = own(item, 'stderr');
333
+ context.observations.push({
334
+ ...observationBase(context, turnId),
335
+ kind: 'tool_call',
336
+ logicalObservationKey: `codex|${context.rolloutIdentity}|${toolUseId}`,
337
+ transcriptRecordKey: `codex|${context.rolloutIdentity}|${ordinal}`,
338
+ recordOrdinal: ordinal,
339
+ recordByteStart,
340
+ toolUseId,
341
+ transcriptToolCallId: paired?.callId ?? undefined,
342
+ toolFacts: {
343
+ toolName: paired?.name ?? null,
344
+ exitCode: typeof exitCode === 'number' ? exitCode : null,
345
+ command: Array.isArray(command) ? command.filter((part) => typeof part === 'string').slice(0, 8) : null,
346
+ stdout: typeof stdout === 'string' ? stdout : null,
347
+ stderr: typeof stderr === 'string' ? stderr : null,
348
+ },
349
+ source: 'transcript',
350
+ completeness: 'complete',
351
+ observedAt,
352
+ });
353
+ return;
354
+ }
355
+ return;
356
+ }
357
+ if (payloadType === 'thread_rolled_back') {
358
+ // Documented rule (G1 runtime contract, thread_rollout_truncation.rs):
359
+ // rollback appends a marker carrying num_turns; physical records remain
360
+ // while effective logical history is truncated.
361
+ const numTurns = own(payload, 'num_turns');
362
+ if (typeof numTurns === 'number' && Number.isInteger(numTurns) && numTurns > 0) {
363
+ context.rollbackTurns.push(numTurns);
364
+ }
365
+ return;
366
+ }
367
+ // task_started / turn_context / token_count / task_complete /
368
+ // thread_settings_applied: known turn bookkeeping — no projection.
369
+ return;
370
+ }
371
+ warnBounded(context, `unknown_record_type_skipped:${type}`);
372
+ }
373
+ function decodeLine(lineBytes) {
374
+ const text = lineBytes.toString('utf8');
375
+ let parsed;
376
+ try {
377
+ parsed = JSON.parse(text);
378
+ }
379
+ catch {
380
+ return { record: null, type: null, ordinal: null, timestamp: null, payload: null };
381
+ }
382
+ if (!isRecord(parsed))
383
+ return { record: null, type: null, ordinal: null, timestamp: null, payload: null };
384
+ const type = own(parsed, 'type');
385
+ const ordinal = own(parsed, 'ordinal');
386
+ const timestamp = own(parsed, 'timestamp');
387
+ const payload = own(parsed, 'payload');
388
+ const validEnvelope = typeof type === 'string' && type.length > 0
389
+ && typeof ordinal === 'number' && Number.isInteger(ordinal)
390
+ && (payload === undefined || isRecord(payload));
391
+ if (!validEnvelope) {
392
+ return { record: parsed, type: null, ordinal: typeof ordinal === 'number' && Number.isInteger(ordinal) ? ordinal : null, timestamp: null, payload: null };
393
+ }
394
+ return {
395
+ record: parsed,
396
+ type,
397
+ ordinal,
398
+ timestamp: typeof timestamp === 'string' ? timestamp : null,
399
+ payload: payload === undefined ? {} : payload,
400
+ };
401
+ }
402
+ function finish(context, nextByteOffset, lastOrdinal) {
403
+ return {
404
+ observations: context.observations,
405
+ rolloutMeta: context.meta,
406
+ compactionTimestamp: context.compactionTimestamp,
407
+ rollbackTurns: context.rollbackTurns,
408
+ nextByteOffset,
409
+ lastOrdinal,
410
+ warnings: context.warnings,
411
+ };
412
+ }
413
+ export function decodeTranscriptWindow(input) {
414
+ const context = {
415
+ rolloutIdentity: input.rolloutIdentity,
416
+ fallbackRootSessionId: input.fallbackRootSessionId,
417
+ nowIso: input.nowIso,
418
+ observations: [],
419
+ meta: { rootSessionId: null, cliVersion: null, parentRolloutIdentity: null, agentIdentity: null, agentDepth: null },
420
+ modelCallsByTurn: new Map(),
421
+ compactionTimestamp: null,
422
+ rollbackTurns: [],
423
+ warnings: [],
424
+ };
425
+ const { bytes } = input;
426
+ if (bytes.length === 0) {
427
+ return { ...finish(context, input.fileOffset, -1), stop: { kind: 'eof' } };
428
+ }
429
+ // UTF-8 multi-byte sequences never contain 0x0A, so splitting the byte
430
+ // window at newline bytes is safe and keeps the checkpoint byte-exact.
431
+ const lastNewline = bytes.lastIndexOf(0x0a);
432
+ const completeEnd = lastNewline + 1; // exclusive byte end of the newline-terminated region
433
+ const tailBytes = bytes.subarray(completeEnd); // bytes after the final newline (no terminator)
434
+ let cursor = input.fileOffset;
435
+ let lastOrdinal = -1;
436
+ let recordsConsumed = 0;
437
+ let lineStart = 0;
438
+ while (lineStart < completeEnd) {
439
+ const lineEnd = bytes.indexOf(0x0a, lineStart);
440
+ if (lineEnd === -1 || lineEnd >= completeEnd)
441
+ break;
442
+ const lineBytes = bytes.subarray(lineStart, lineEnd);
443
+ const recordByteStart = cursor;
444
+ cursor = input.fileOffset + lineEnd + 1;
445
+ lineStart = lineEnd + 1;
446
+ if (lineBytes.length === 0 || lineBytes.toString('utf8').trim().length === 0)
447
+ continue;
448
+ if (recordsConsumed >= CODEX_INGESTION_MAX_BATCH_RECORDS) {
449
+ return { ...finish(context, recordByteStart, lastOrdinal), stop: { kind: 'byte_bound' } };
450
+ }
451
+ const line = decodeLine(lineBytes);
452
+ if (line.record === null || line.type === null) {
453
+ // A newline-terminated line that does not parse (or fails the record
454
+ // envelope) is a stable malformed record: stop here without advancing
455
+ // past it (SPEC §14.2).
456
+ return { ...finish(context, recordByteStart, lastOrdinal), stop: { kind: 'malformed', ordinal: line.ordinal } };
457
+ }
458
+ recordsConsumed += 1;
459
+ lastOrdinal = line.ordinal;
460
+ projectRecord(context, { type: line.type, payload: line.payload ?? {}, ordinal: line.ordinal, recordByteStart, timestamp: line.timestamp });
461
+ }
462
+ if (tailBytes.length > 0) {
463
+ const tailStart = cursor; // byte offset where the unterminated tail begins
464
+ if (input.byteBoundReached) {
465
+ // The window ended mid-line because of the byte bound — the line is not
466
+ // known-incomplete; leave it wholly unread and report bounded lag.
467
+ if (lastNewline === -1) {
468
+ return { ...finish(context, input.fileOffset, lastOrdinal), stop: { kind: 'oversized_record' } };
469
+ }
470
+ return { ...finish(context, tailStart, lastOrdinal), stop: { kind: 'byte_bound' } };
471
+ }
472
+ const line = decodeLine(tailBytes);
473
+ if (line.record !== null && line.type !== null) {
474
+ // A parseable final line without a trailing newline is a complete
475
+ // record; consume it and advance the checkpoint to EOF.
476
+ lastOrdinal = line.ordinal;
477
+ projectRecord(context, { type: line.type, payload: line.payload ?? {}, ordinal: line.ordinal, recordByteStart: tailStart, timestamp: line.timestamp });
478
+ return { ...finish(context, tailStart + tailBytes.length, lastOrdinal), stop: { kind: 'eof' } };
479
+ }
480
+ // Incomplete final line: transient append/flush boundary — do not
481
+ // advance past the previous record; the next ingestion retries (SPEC §14.1).
482
+ return { ...finish(context, tailStart, lastOrdinal), stop: { kind: 'incomplete_tail' } };
483
+ }
484
+ return { ...finish(context, cursor, lastOrdinal), stop: input.byteBoundReached ? { kind: 'byte_bound' } : { kind: 'eof' } };
485
+ }
@@ -0,0 +1,13 @@
1
+ export type CodexTranscriptLookup = {
2
+ ok: true;
3
+ transcriptPath: string;
4
+ } | {
5
+ ok: false;
6
+ reason: 'catch_up_rollout_identity_invalid' | 'catch_up_sessions_root_missing' | 'catch_up_transcript_missing' | 'catch_up_transcript_ambiguous' | 'catch_up_lookup_exhausted';
7
+ nextAction: string;
8
+ };
9
+ /**
10
+ * Resolve one previously-authenticated rollout identity to its transcript
11
+ * path by exact-uuid filename match under `<codexHome>/sessions`.
12
+ */
13
+ export declare function locateCodexTranscriptByRolloutIdentity(codexHome: string, rolloutIdentity: string): CodexTranscriptLookup;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Codex transcript locator for catch-up (PRI-624 Slice C).
3
+ *
4
+ * The durable checkpoint stores only the rollout uuid (SPEC §18 scenario 9
5
+ * forbids raw paths in the DB), so catch-up must resolve a checkpointed
6
+ * rollout back to its transcript file. This is NOT session discovery: the
7
+ * lookup searches for the EXACT rollout uuid of a rollout the authenticated
8
+ * Workspace hook previously delivered (only hooks write checkpoints). It
9
+ * never guesses a "latest session", never returns a partial match, and
10
+ * refuses ambiguities (ADR-0020 §11.2 / SPEC §9).
11
+ */
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { parseRolloutFileName } from './transcript-path.js';
15
+ /** Bounded walk: hard cap on visited directory entries so a pathological sessions tree cannot stall the worker. */
16
+ const MAX_LOOKUP_ENTRIES = 5000;
17
+ const ROLLOUT_IDENTITY_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
18
+ /**
19
+ * Resolve one previously-authenticated rollout identity to its transcript
20
+ * path by exact-uuid filename match under `<codexHome>/sessions`.
21
+ */
22
+ export function locateCodexTranscriptByRolloutIdentity(codexHome, rolloutIdentity) {
23
+ if (!ROLLOUT_IDENTITY_PATTERN.test(rolloutIdentity)) {
24
+ return { ok: false, reason: 'catch_up_rollout_identity_invalid', nextAction: 'the checkpointed rollout identity is not a rollout uuid; inspect the workspace trajectory database.' };
25
+ }
26
+ const sessionsRoot = path.join(codexHome, 'sessions');
27
+ let rootStats;
28
+ try {
29
+ rootStats = fs.statSync(sessionsRoot);
30
+ }
31
+ catch {
32
+ return { ok: false, reason: 'catch_up_sessions_root_missing', nextAction: 'the configured CODEX_HOME has no sessions root; verify the Codex home used by the hook and by catch-up matches.' };
33
+ }
34
+ if (!rootStats.isDirectory()) {
35
+ return { ok: false, reason: 'catch_up_sessions_root_missing', nextAction: 'the configured CODEX_HOME sessions path is not a directory; verify the Codex home configuration.' };
36
+ }
37
+ const matches = [];
38
+ let visited = 0;
39
+ const stack = [sessionsRoot];
40
+ while (stack.length > 0 && matches.length < 2) {
41
+ const dir = stack.pop();
42
+ if (dir === undefined)
43
+ break;
44
+ let entries;
45
+ try {
46
+ entries = fs.readdirSync(dir, { withFileTypes: true });
47
+ }
48
+ catch {
49
+ continue; // unreadable subtree — other subtrees may still hold the rollout
50
+ }
51
+ for (const entry of entries) {
52
+ visited += 1;
53
+ if (visited > MAX_LOOKUP_ENTRIES) {
54
+ return { ok: false, reason: 'catch_up_lookup_exhausted', nextAction: 'the sessions tree exceeded the bounded catch-up lookup; keep CODEX_HOME/sessions pruned or catch up rollouts manually.' };
55
+ }
56
+ if (entry.isDirectory()) {
57
+ stack.push(path.join(dir, entry.name));
58
+ }
59
+ else if (entry.isFile() && entry.name.endsWith('.jsonl')) {
60
+ if (parseRolloutFileName(entry.name) === rolloutIdentity) {
61
+ matches.push(path.join(dir, entry.name));
62
+ if (matches.length >= 2)
63
+ break;
64
+ }
65
+ }
66
+ }
67
+ }
68
+ if (matches.length === 0) {
69
+ return { ok: false, reason: 'catch_up_transcript_missing', nextAction: 'the checkpointed rollout has no transcript under the Codex sessions root (rotated or cleaned by Codex); its committed observations remain, the pending lag cannot be recovered.' };
70
+ }
71
+ if (matches.length > 1) {
72
+ return { ok: false, reason: 'catch_up_transcript_ambiguous', nextAction: 'multiple transcripts match the rollout identity; refuse to guess — inspect the Codex sessions tree.' };
73
+ }
74
+ return { ok: true, transcriptPath: matches[0] };
75
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * File identity captured at validation time. The open/read seam re-proves
3
+ * the opened object still has this identity BEFORE any byte is read — the
4
+ * G1 §7 / SPEC §9 post-open revalidation that closes the validate→open
5
+ * TOCTOU window (path replacement, symlink swap).
6
+ */
7
+ export interface TranscriptFileIdentity {
8
+ readonly dev: number;
9
+ readonly ino: number;
10
+ readonly size: number;
11
+ readonly mtimeMs: number;
12
+ }
13
+ export type TranscriptPathValidation = {
14
+ ok: true;
15
+ canonicalPath: string;
16
+ rolloutIdentity: string;
17
+ identity: TranscriptFileIdentity;
18
+ } | {
19
+ ok: false;
20
+ reason: 'transcript_path_invalid' | 'transcript_path_outside_codex_home';
21
+ nextAction: string;
22
+ };
23
+ /** rollout-<timestamp>-<uuid>.jsonl — returns the rollout uuid, or null when the name is off-contract. */
24
+ export declare function parseRolloutFileName(fileName: string): string | null;
25
+ export declare function validateCodexTranscriptPath(transcriptPath: string, codexHome: string): TranscriptPathValidation;