@herbertgao/pi-extensions 2026.8.9 → 2026.8.11

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 (194) hide show
  1. package/README.md +4 -2
  2. package/node_modules/@herbertgao/resume-from/LICENSE +21 -0
  3. package/node_modules/@herbertgao/resume-from/README.md +163 -0
  4. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/adapter.d.ts +25 -0
  5. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/adapter.js +60 -0
  6. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/contract.d.ts +8 -0
  7. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/contract.js +4 -0
  8. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/entries.d.ts +30 -0
  9. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/entries.js +107 -0
  10. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/index.d.ts +3 -0
  11. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/index.js +2 -0
  12. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/layout.d.ts +29 -0
  13. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/layout.js +80 -0
  14. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/readback.d.ts +9 -0
  15. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/readback.js +39 -0
  16. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/reader.d.ts +37 -0
  17. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/reader.js +424 -0
  18. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/redaction.d.ts +26 -0
  19. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/redaction.js +132 -0
  20. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/validation.d.ts +4 -0
  21. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/validation.js +39 -0
  22. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/writer.d.ts +23 -0
  23. package/node_modules/@herbertgao/resume-from/dist/adapters/claude-code/writer.js +306 -0
  24. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/contract.d.ts +15 -0
  25. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/contract.js +4 -0
  26. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/index.d.ts +6 -0
  27. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/index.js +44 -0
  28. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/read.d.ts +28 -0
  29. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/read.js +270 -0
  30. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/readback.d.ts +9 -0
  31. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/readback.js +47 -0
  32. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/redaction.d.ts +26 -0
  33. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/redaction.js +132 -0
  34. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/rollout.d.ts +75 -0
  35. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/rollout.js +185 -0
  36. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/validation.d.ts +7 -0
  37. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/validation.js +78 -0
  38. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/write.d.ts +17 -0
  39. package/node_modules/@herbertgao/resume-from/dist/adapters/codex/write.js +141 -0
  40. package/node_modules/@herbertgao/resume-from/dist/adapters/contract.d.ts +74 -0
  41. package/node_modules/@herbertgao/resume-from/dist/adapters/contract.js +4 -0
  42. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/adapter.d.ts +15 -0
  43. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/adapter.js +209 -0
  44. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/contract.d.ts +24 -0
  45. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/contract.js +4 -0
  46. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/format.d.ts +162 -0
  47. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/format.js +223 -0
  48. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/index.d.ts +7 -0
  49. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/index.js +10 -0
  50. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/parse.d.ts +41 -0
  51. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/parse.js +371 -0
  52. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/redaction.d.ts +26 -0
  53. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/redaction.js +132 -0
  54. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/serialize.d.ts +22 -0
  55. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/serialize.js +87 -0
  56. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/validate.d.ts +14 -0
  57. package/node_modules/@herbertgao/resume-from/dist/adapters/pi/validate.js +134 -0
  58. package/node_modules/@herbertgao/resume-from/dist/bin.d.ts +35 -0
  59. package/node_modules/@herbertgao/resume-from/dist/bin.js +135 -0
  60. package/node_modules/@herbertgao/resume-from/dist/contract.d.ts +10 -0
  61. package/node_modules/@herbertgao/resume-from/dist/contract.js +4 -0
  62. package/node_modules/@herbertgao/resume-from/dist/host/agents.d.ts +29 -0
  63. package/node_modules/@herbertgao/resume-from/dist/host/agents.js +20 -0
  64. package/node_modules/@herbertgao/resume-from/dist/host/cli/args.d.ts +21 -0
  65. package/node_modules/@herbertgao/resume-from/dist/host/cli/args.js +187 -0
  66. package/node_modules/@herbertgao/resume-from/dist/host/cli/contract.d.ts +27 -0
  67. package/node_modules/@herbertgao/resume-from/dist/host/cli/contract.js +4 -0
  68. package/node_modules/@herbertgao/resume-from/dist/host/cli/index.d.ts +2 -0
  69. package/node_modules/@herbertgao/resume-from/dist/host/cli/index.js +1 -0
  70. package/node_modules/@herbertgao/resume-from/dist/host/cli/presentation.d.ts +2 -0
  71. package/node_modules/@herbertgao/resume-from/dist/host/cli/presentation.js +11 -0
  72. package/node_modules/@herbertgao/resume-from/dist/host/cli/render.d.ts +3 -0
  73. package/node_modules/@herbertgao/resume-from/dist/host/cli/render.js +70 -0
  74. package/node_modules/@herbertgao/resume-from/dist/host/cli/runner.d.ts +8 -0
  75. package/node_modules/@herbertgao/resume-from/dist/host/cli/runner.js +125 -0
  76. package/node_modules/@herbertgao/resume-from/dist/host/contract.d.ts +42 -0
  77. package/node_modules/@herbertgao/resume-from/dist/host/contract.js +4 -0
  78. package/node_modules/@herbertgao/resume-from/dist/host/entry.d.ts +52 -0
  79. package/node_modules/@herbertgao/resume-from/dist/host/entry.js +91 -0
  80. package/node_modules/@herbertgao/resume-from/dist/host/index.d.ts +11 -0
  81. package/node_modules/@herbertgao/resume-from/dist/host/index.js +10 -0
  82. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/command.d.ts +18 -0
  83. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/command.js +103 -0
  84. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/contract.d.ts +34 -0
  85. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/contract.js +4 -0
  86. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/index.d.ts +6 -0
  87. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/index.js +4 -0
  88. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/picker.d.ts +16 -0
  89. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/picker.js +35 -0
  90. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/presentation.d.ts +2 -0
  91. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/presentation.js +11 -0
  92. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/register.d.ts +25 -0
  93. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/register.js +11 -0
  94. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/ui.d.ts +20 -0
  95. package/node_modules/@herbertgao/resume-from/dist/host/pi-extension/ui.js +1 -0
  96. package/node_modules/@herbertgao/resume-from/dist/host/profile.d.ts +8 -0
  97. package/node_modules/@herbertgao/resume-from/dist/host/profile.js +31 -0
  98. package/node_modules/@herbertgao/resume-from/dist/host/registry.d.ts +8 -0
  99. package/node_modules/@herbertgao/resume-from/dist/host/registry.js +33 -0
  100. package/node_modules/@herbertgao/resume-from/dist/host/wiring.d.ts +41 -0
  101. package/node_modules/@herbertgao/resume-from/dist/host/wiring.js +67 -0
  102. package/node_modules/@herbertgao/resume-from/dist/import/confirmation.d.ts +6 -0
  103. package/node_modules/@herbertgao/resume-from/dist/import/confirmation.js +27 -0
  104. package/node_modules/@herbertgao/resume-from/dist/import/contract.d.ts +41 -0
  105. package/node_modules/@herbertgao/resume-from/dist/import/contract.js +4 -0
  106. package/node_modules/@herbertgao/resume-from/dist/import/discovery/contract.d.ts +58 -0
  107. package/node_modules/@herbertgao/resume-from/dist/import/discovery/contract.js +4 -0
  108. package/node_modules/@herbertgao/resume-from/dist/import/discovery/errors.d.ts +9 -0
  109. package/node_modules/@herbertgao/resume-from/dist/import/discovery/errors.js +12 -0
  110. package/node_modules/@herbertgao/resume-from/dist/import/discovery/finder.d.ts +10 -0
  111. package/node_modules/@herbertgao/resume-from/dist/import/discovery/finder.js +129 -0
  112. package/node_modules/@herbertgao/resume-from/dist/import/discovery/homes.d.ts +23 -0
  113. package/node_modules/@herbertgao/resume-from/dist/import/discovery/homes.js +83 -0
  114. package/node_modules/@herbertgao/resume-from/dist/import/discovery/index.d.ts +2 -0
  115. package/node_modules/@herbertgao/resume-from/dist/import/discovery/index.js +2 -0
  116. package/node_modules/@herbertgao/resume-from/dist/import/discovery/ordering.d.ts +3 -0
  117. package/node_modules/@herbertgao/resume-from/dist/import/discovery/ordering.js +21 -0
  118. package/node_modules/@herbertgao/resume-from/dist/import/errors.d.ts +22 -0
  119. package/node_modules/@herbertgao/resume-from/dist/import/errors.js +20 -0
  120. package/node_modules/@herbertgao/resume-from/dist/import/index.d.ts +3 -0
  121. package/node_modules/@herbertgao/resume-from/dist/import/index.js +5 -0
  122. package/node_modules/@herbertgao/resume-from/dist/import/landing/contract.d.ts +44 -0
  123. package/node_modules/@herbertgao/resume-from/dist/import/landing/contract.js +4 -0
  124. package/node_modules/@herbertgao/resume-from/dist/import/landing/errors.d.ts +15 -0
  125. package/node_modules/@herbertgao/resume-from/dist/import/landing/errors.js +15 -0
  126. package/node_modules/@herbertgao/resume-from/dist/import/landing/handover.d.ts +5 -0
  127. package/node_modules/@herbertgao/resume-from/dist/import/landing/handover.js +12 -0
  128. package/node_modules/@herbertgao/resume-from/dist/import/landing/index.d.ts +4 -0
  129. package/node_modules/@herbertgao/resume-from/dist/import/landing/index.js +4 -0
  130. package/node_modules/@herbertgao/resume-from/dist/import/landing/lander.d.ts +7 -0
  131. package/node_modules/@herbertgao/resume-from/dist/import/landing/lander.js +133 -0
  132. package/node_modules/@herbertgao/resume-from/dist/import/landing/marker.d.ts +9 -0
  133. package/node_modules/@herbertgao/resume-from/dist/import/landing/marker.js +46 -0
  134. package/node_modules/@herbertgao/resume-from/dist/import/pipeline.d.ts +17 -0
  135. package/node_modules/@herbertgao/resume-from/dist/import/pipeline.js +134 -0
  136. package/node_modules/@herbertgao/resume-from/dist/import/preview/builder.d.ts +6 -0
  137. package/node_modules/@herbertgao/resume-from/dist/import/preview/builder.js +76 -0
  138. package/node_modules/@herbertgao/resume-from/dist/import/preview/contract.d.ts +39 -0
  139. package/node_modules/@herbertgao/resume-from/dist/import/preview/contract.js +4 -0
  140. package/node_modules/@herbertgao/resume-from/dist/import/preview/format.d.ts +19 -0
  141. package/node_modules/@herbertgao/resume-from/dist/import/preview/format.js +45 -0
  142. package/node_modules/@herbertgao/resume-from/dist/import/preview/index.d.ts +2 -0
  143. package/node_modules/@herbertgao/resume-from/dist/import/preview/index.js +1 -0
  144. package/node_modules/@herbertgao/resume-from/dist/import/preview/warnings.d.ts +11 -0
  145. package/node_modules/@herbertgao/resume-from/dist/import/preview/warnings.js +80 -0
  146. package/node_modules/@herbertgao/resume-from/dist/import/transfer/contract.d.ts +48 -0
  147. package/node_modules/@herbertgao/resume-from/dist/import/transfer/contract.js +4 -0
  148. package/node_modules/@herbertgao/resume-from/dist/import/transfer/index.d.ts +2 -0
  149. package/node_modules/@herbertgao/resume-from/dist/import/transfer/index.js +1 -0
  150. package/node_modules/@herbertgao/resume-from/dist/import/transfer/rules.d.ts +3 -0
  151. package/node_modules/@herbertgao/resume-from/dist/import/transfer/rules.js +228 -0
  152. package/node_modules/@herbertgao/resume-from/dist/import/wiring.d.ts +24 -0
  153. package/node_modules/@herbertgao/resume-from/dist/import/wiring.js +27 -0
  154. package/node_modules/@herbertgao/resume-from/dist/index.d.ts +36 -0
  155. package/node_modules/@herbertgao/resume-from/dist/index.js +36 -0
  156. package/node_modules/@herbertgao/resume-from/dist/platform/config/contract.d.ts +35 -0
  157. package/node_modules/@herbertgao/resume-from/dist/platform/config/contract.js +4 -0
  158. package/node_modules/@herbertgao/resume-from/dist/platform/config/defaults.d.ts +16 -0
  159. package/node_modules/@herbertgao/resume-from/dist/platform/config/defaults.js +22 -0
  160. package/node_modules/@herbertgao/resume-from/dist/platform/config/errors.d.ts +9 -0
  161. package/node_modules/@herbertgao/resume-from/dist/platform/config/errors.js +12 -0
  162. package/node_modules/@herbertgao/resume-from/dist/platform/config/index.d.ts +5 -0
  163. package/node_modules/@herbertgao/resume-from/dist/platform/config/index.js +6 -0
  164. package/node_modules/@herbertgao/resume-from/dist/platform/config/loader.d.ts +10 -0
  165. package/node_modules/@herbertgao/resume-from/dist/platform/config/loader.js +47 -0
  166. package/node_modules/@herbertgao/resume-from/dist/platform/config/paths.d.ts +13 -0
  167. package/node_modules/@herbertgao/resume-from/dist/platform/config/paths.js +37 -0
  168. package/node_modules/@herbertgao/resume-from/dist/platform/config/validate.d.ts +9 -0
  169. package/node_modules/@herbertgao/resume-from/dist/platform/config/validate.js +117 -0
  170. package/node_modules/@herbertgao/resume-from/dist/platform/repo/contract.d.ts +30 -0
  171. package/node_modules/@herbertgao/resume-from/dist/platform/repo/contract.js +4 -0
  172. package/node_modules/@herbertgao/resume-from/dist/platform/repo/git.d.ts +21 -0
  173. package/node_modules/@herbertgao/resume-from/dist/platform/repo/git.js +74 -0
  174. package/node_modules/@herbertgao/resume-from/dist/platform/repo/index.d.ts +2 -0
  175. package/node_modules/@herbertgao/resume-from/dist/platform/repo/index.js +1 -0
  176. package/node_modules/@herbertgao/resume-from/dist/platform/repo/reader.d.ts +8 -0
  177. package/node_modules/@herbertgao/resume-from/dist/platform/repo/reader.js +130 -0
  178. package/node_modules/@herbertgao/resume-from/dist/platform/store/contract.d.ts +31 -0
  179. package/node_modules/@herbertgao/resume-from/dist/platform/store/contract.js +4 -0
  180. package/node_modules/@herbertgao/resume-from/dist/platform/store/file-committer.d.ts +3 -0
  181. package/node_modules/@herbertgao/resume-from/dist/platform/store/file-committer.js +391 -0
  182. package/node_modules/@herbertgao/resume-from/dist/platform/store/index.d.ts +2 -0
  183. package/node_modules/@herbertgao/resume-from/dist/platform/store/index.js +2 -0
  184. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/contract.d.ts +12 -0
  185. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/contract.js +4 -0
  186. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/estimator.d.ts +12 -0
  187. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/estimator.js +111 -0
  188. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/index.d.ts +2 -0
  189. package/node_modules/@herbertgao/resume-from/dist/platform/tokens/index.js +1 -0
  190. package/node_modules/@herbertgao/resume-from/dist/session/contract.d.ts +109 -0
  191. package/node_modules/@herbertgao/resume-from/dist/session/contract.js +4 -0
  192. package/node_modules/@herbertgao/resume-from/package.json +94 -0
  193. package/node_modules/@herbertgao/resume-from/shims/pi/extensions/resume-from.js +118 -0
  194. package/package.json +6 -2
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Read-back is mandatory, not an optimisation. Codex stores what it is given without validating
3
+ * it and drops an item type it does not know without a word (C-6), so the only evidence of what
4
+ * was stored is what can be read back off disk (FR-52).
5
+ */
6
+ import type { AgentRuntime, HomePath, SessionId, StoredSessionFacts, SwitchOutcome } from "./contract.js";
7
+ export declare function readBackCodex(home: HomePath, sessionId: SessionId): Promise<StoredSessionFacts>;
8
+ /** Codex declares "create-only" (C-2). The landing hands the command back instead (FR-45). */
9
+ export declare function switchToCodex(home: HomePath, sessionId: SessionId, _runtime: AgentRuntime): Promise<SwitchOutcome>;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Read-back is mandatory, not an optimisation. Codex stores what it is given without validating
3
+ * it and drops an item type it does not know without a word (C-6), so the only evidence of what
4
+ * was stored is what can be read back off disk (FR-52).
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ import { basename } from "node:path";
8
+ import { isNotFoundError, listRolloutFiles, sessionIdFromRolloutFileName, sessionsRoot, } from "./rollout.js";
9
+ import { inspectCodexRollout } from "./validation.js";
10
+ export async function readBackCodex(home, sessionId) {
11
+ const absent = {
12
+ sessionId,
13
+ itemCount: 0,
14
+ openable: false,
15
+ };
16
+ const filePath = await findRollout(sessionsRoot(home), sessionId);
17
+ if (filePath === null)
18
+ return absent;
19
+ let text;
20
+ try {
21
+ text = await readFile(filePath, "utf8");
22
+ }
23
+ catch (error) {
24
+ if (isNotFoundError(error))
25
+ return absent;
26
+ throw error;
27
+ }
28
+ const inspection = inspectCodexRollout(text, sessionId);
29
+ return {
30
+ sessionId,
31
+ itemCount: inspection.itemCount,
32
+ openable: inspection.defects.length === 0,
33
+ };
34
+ }
35
+ /** Codex declares "create-only" (C-2). The landing hands the command back instead (FR-45). */
36
+ export async function switchToCodex(home, sessionId, _runtime) {
37
+ throw new Error(`Codex landing is "create-only": this adapter cannot move the user into a session. ` +
38
+ `Run: CODEX_HOME=${home} codex resume ${sessionId}`);
39
+ }
40
+ /** FR-51 is a fact, not an exception: a thread that is not there reports as not openable. */
41
+ async function findRollout(root, sessionId) {
42
+ for (const filePath of await listRolloutFiles(root)) {
43
+ if (sessionIdFromRolloutFileName(basename(filePath)) === sessionId)
44
+ return filePath;
45
+ }
46
+ return null;
47
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Credential redaction — shared implementation kept in three adapter subfolders.
3
+ *
4
+ * Files:
5
+ * src/adapters/claude-code/redaction.ts
6
+ * src/adapters/codex/redaction.ts
7
+ * src/adapters/pi/redaction.ts
8
+ *
9
+ * These three files are byte-identical by design. A fix in one MUST be applied to all three.
10
+ * src/adapters/boundary.test.ts asserts byte equality and will fail if they drift.
11
+ *
12
+ * Why not a single shared module: src/adapters/contract.ts is types-only (no behaviour). A
13
+ * platform service would be imported as behaviour, and T-ROO-7 requires adapter-to-platform
14
+ * cross-module imports to go through contract.js — which can only export types, not functions.
15
+ */
16
+ /** Stable replacement used for credentials that must not cross an adapter boundary. */
17
+ export declare const REDACTED_VALUE = "[REDACTED]";
18
+ /**
19
+ * Redact credentials in JSON-like tool inputs without changing the caller's value.
20
+ * Adapter-specific parsing stays in each adapter; this function only handles secret values.
21
+ */
22
+ export declare function redactSensitiveStructure(value: unknown): unknown;
23
+ /** Redact recognizable credentials from shell commands, headers, and unstructured arguments. */
24
+ export declare function redactSensitiveText(text: string): string;
25
+ /** Redact a source-recorded argument string, preserving valid JSON when it is JSON. */
26
+ export declare function redactSensitiveArgumentsText(text: string): string;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Credential redaction — shared implementation kept in three adapter subfolders.
3
+ *
4
+ * Files:
5
+ * src/adapters/claude-code/redaction.ts
6
+ * src/adapters/codex/redaction.ts
7
+ * src/adapters/pi/redaction.ts
8
+ *
9
+ * These three files are byte-identical by design. A fix in one MUST be applied to all three.
10
+ * src/adapters/boundary.test.ts asserts byte equality and will fail if they drift.
11
+ *
12
+ * Why not a single shared module: src/adapters/contract.ts is types-only (no behaviour). A
13
+ * platform service would be imported as behaviour, and T-ROO-7 requires adapter-to-platform
14
+ * cross-module imports to go through contract.js — which can only export types, not functions.
15
+ */
16
+ /** Stable replacement used for credentials that must not cross an adapter boundary. */
17
+ export const REDACTED_VALUE = "[REDACTED]";
18
+ const ENVIRONMENT_KEYS = new Set(["env", "environment", "environmentvariables"]);
19
+ const SENSITIVE_KEYS = new Set([
20
+ "authorization",
21
+ "clientsecret",
22
+ "cookie",
23
+ "credentials",
24
+ "password",
25
+ "passwd",
26
+ "privatekey",
27
+ "proxyauthorization",
28
+ "secret",
29
+ "setcookie",
30
+ "signingkey",
31
+ "token",
32
+ ]);
33
+ const PRIVATE_KEY_PATTERN = /-----BEGIN ([A-Z0-9 ]*PRIVATE KEY)-----[\s\S]*?-----END \1-----/g;
34
+ const AUTHORIZATION_PATTERN = /(\b(?:authorization|proxy-authorization)\s*:\s*)(?:Bearer|Basic)\s+[^\s,;]+/gi;
35
+ const API_HEADER_PATTERN = /(\b(?:x-api-key|api-key|x-auth-token)\s*:\s*)[^\s,;]+/gi;
36
+ const CREDENTIAL_PATTERN = /\b(?:sk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{12,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{12,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{30,})\b/g;
37
+ const ASSIGNMENT_PATTERN = /\b([A-Za-z_][A-Za-z0-9_]*)(\s*=\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s;&|]+)/g;
38
+ const FLAG_PATTERN = /(--(?:api[-_]?key|access[-_]?token|auth[-_]?token|token|password|passwd|secret|client[-_]?secret|private[-_]?key))(=|\s+)("[^"\r\n]*"|'[^'\r\n]*'|[^\s;&|]+)/gi;
39
+ const USER_CREDENTIAL_PATTERN = /(^|[\s;&|])(-u|--user)(=|\s+)("[^"\r\n]*"|'[^'\r\n]*'|[^\s;&|]+)/g;
40
+ const URI_USERINFO_PATTERN = /\b([a-z][a-z0-9+.-]*:\/\/)([^\s/:@]+):([^\s/@]+)@/gi;
41
+ // Unquoted value requires a structural delimiter or line/string end following it, OR a digit
42
+ // somewhere in the token (C-RED-1: "token: expired" is plain English and stays, while
43
+ // "password: hunter2 # prod" carries a credential that must not cross even mid-line).
44
+ const JSON_LIKE_PATTERN = /(["']?)([A-Za-z_][A-Za-z0-9_-]*)\1(\s*:\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;}]+(?=[,;}]|[\r\n]|$)|[^\s,;}]*\d[^\s,;}]*)/g;
45
+ function normalizedKey(key) {
46
+ return key.toLowerCase().replaceAll(/[^a-z0-9]/g, "");
47
+ }
48
+ function isSensitiveKey(key) {
49
+ const normalized = normalizedKey(key);
50
+ return (SENSITIVE_KEYS.has(normalized) ||
51
+ normalized.endsWith("apikey") ||
52
+ normalized.endsWith("accesskey") ||
53
+ normalized.endsWith("password") ||
54
+ normalized.endsWith("privatekey") ||
55
+ normalized.endsWith("secret") ||
56
+ normalized.endsWith("token"));
57
+ }
58
+ function redactEnvironmentValue(value) {
59
+ if (Array.isArray(value))
60
+ return value.map(redactEnvironmentValue);
61
+ if (typeof value === "object" && value !== null) {
62
+ return Object.fromEntries(Object.keys(value).map((key) => [
63
+ key,
64
+ redactEnvironmentValue(value[key]),
65
+ ]));
66
+ }
67
+ return value === undefined ? undefined : REDACTED_VALUE;
68
+ }
69
+ function redactSensitiveArray(values) {
70
+ const redacted = values.map(redactSensitiveStructure);
71
+ for (let index = 0; index < values.length - 1; index += 1) {
72
+ const flag = values[index];
73
+ if (typeof flag === "string" &&
74
+ (flag.trim() === "-u" || flag.trim() === "--user") &&
75
+ typeof values[index + 1] === "string") {
76
+ redacted[index + 1] = REDACTED_VALUE;
77
+ }
78
+ }
79
+ return redacted;
80
+ }
81
+ /**
82
+ * Redact credentials in JSON-like tool inputs without changing the caller's value.
83
+ * Adapter-specific parsing stays in each adapter; this function only handles secret values.
84
+ */
85
+ export function redactSensitiveStructure(value) {
86
+ if (typeof value === "string")
87
+ return redactSensitiveText(value);
88
+ if (Array.isArray(value))
89
+ return redactSensitiveArray(value);
90
+ if (typeof value !== "object" || value === null)
91
+ return value;
92
+ const redacted = {};
93
+ for (const [key, child] of Object.entries(value)) {
94
+ if (isSensitiveKey(key)) {
95
+ redacted[key] = REDACTED_VALUE;
96
+ }
97
+ else if ((normalizedKey(key) === "user" || normalizedKey(key) === "username") &&
98
+ typeof child === "string" &&
99
+ child.includes(":")) {
100
+ redacted[key] = REDACTED_VALUE;
101
+ }
102
+ else if (ENVIRONMENT_KEYS.has(normalizedKey(key))) {
103
+ redacted[key] = redactEnvironmentValue(child);
104
+ }
105
+ else {
106
+ redacted[key] = redactSensitiveStructure(child);
107
+ }
108
+ }
109
+ return redacted;
110
+ }
111
+ /** Redact recognizable credentials from shell commands, headers, and unstructured arguments. */
112
+ export function redactSensitiveText(text) {
113
+ return text
114
+ .replace(PRIVATE_KEY_PATTERN, REDACTED_VALUE)
115
+ .replace(AUTHORIZATION_PATTERN, `$1${REDACTED_VALUE}`)
116
+ .replace(API_HEADER_PATTERN, `$1${REDACTED_VALUE}`)
117
+ .replace(FLAG_PATTERN, `$1$2${REDACTED_VALUE}`)
118
+ .replace(USER_CREDENTIAL_PATTERN, `$1$2$3${REDACTED_VALUE}`)
119
+ .replace(URI_USERINFO_PATTERN, `$1$2:${REDACTED_VALUE}@`)
120
+ .replace(ASSIGNMENT_PATTERN, (match, key, separator) => isSensitiveKey(key) ? `${key}${separator}${REDACTED_VALUE}` : match)
121
+ .replace(JSON_LIKE_PATTERN, (match, quote, key, separator) => isSensitiveKey(key) ? `${quote}${key}${quote}${separator}${REDACTED_VALUE}` : match)
122
+ .replace(CREDENTIAL_PATTERN, REDACTED_VALUE);
123
+ }
124
+ /** Redact a source-recorded argument string, preserving valid JSON when it is JSON. */
125
+ export function redactSensitiveArgumentsText(text) {
126
+ try {
127
+ return JSON.stringify(redactSensitiveStructure(JSON.parse(text)));
128
+ }
129
+ catch {
130
+ return redactSensitiveText(text);
131
+ }
132
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * What a Codex rollout file is: where it lives, how it is named, and which entries it holds.
3
+ *
4
+ * Every fact here was confirmed against codex-cli 0.146.0 (Constraints: "Facts about Codex are
5
+ * verified against the installed version, never assumed"). The entry names below are the ones
6
+ * C-7 and C-8 measured: `event_msg` drives the visible history and the picker preview,
7
+ * `response_item` drives the model history only.
8
+ */
9
+ import type { HomePath, SessionId } from "./contract.js";
10
+ /** Codex reads this variable to find its home; so does this adapter (FR-2, FR-3). */
11
+ export declare const CODEX_HOME_ENV = "CODEX_HOME";
12
+ /** The version this module's format knowledge was verified against. */
13
+ export declare const CODEX_CLI_VERSION = "0.146.0";
14
+ /** Recorded in the rollout so a thread imported by this tool is identifiable. */
15
+ export declare const CODEX_ORIGINATOR = "resume-from";
16
+ /** `thread/list`'s default filter keeps interactive sources; "cli" is one of them (C-7). */
17
+ export declare const CODEX_SOURCE_CLI = "cli";
18
+ export declare const CODEX_ENTRY_SESSION_META = "session_meta";
19
+ export declare const CODEX_ENTRY_EVENT_MSG = "event_msg";
20
+ export declare const CODEX_ENTRY_RESPONSE_ITEM = "response_item";
21
+ export declare const CODEX_ENTRY_COMPACTED = "compacted";
22
+ export declare const CODEX_EVENT_USER_MESSAGE = "user_message";
23
+ export declare const CODEX_EVENT_AGENT_MESSAGE = "agent_message";
24
+ export declare const CODEX_ITEM_FUNCTION_CALL = "function_call";
25
+ export declare const CODEX_ITEM_FUNCTION_CALL_OUTPUT = "function_call_output";
26
+ export declare const CODEX_ITEM_CUSTOM_TOOL_CALL = "custom_tool_call";
27
+ export declare const CODEX_ITEM_CUSTOM_TOOL_CALL_OUTPUT = "custom_tool_call_output";
28
+ /**
29
+ * Top-level entry types codex-cli 0.146.0 writes. An entry outside this set is an entry this
30
+ * module does not understand: it is skipped and counted, never guessed at (C-6).
31
+ */
32
+ export declare const KNOWN_ENTRY_TYPES: ReadonlySet<string>;
33
+ /** One JSONL line of a rollout file. */
34
+ export interface RolloutEntry {
35
+ timestamp: string;
36
+ type: string;
37
+ payload: Record<string, unknown>;
38
+ }
39
+ /** The `session_meta` payload, in the fields this module uses. */
40
+ export interface CodexSessionMeta {
41
+ id: string;
42
+ timestamp: string | null;
43
+ cwd: string | null;
44
+ commit: string | null;
45
+ branch: string | null;
46
+ }
47
+ /** Always absolute: a HomePath is an absolute path, whatever the environment holds (FR-2). */
48
+ export declare function defaultCodexHome(): HomePath;
49
+ export declare function sessionsRoot(home: HomePath): string;
50
+ /** `<home>/sessions/YYYY/MM/DD/rollout-YYYY-MM-DDTHH-mm-ss-<id>.jsonl`, as Codex lays them out. */
51
+ export declare function rolloutFilePath(home: HomePath, sessionId: SessionId, at: Date): string;
52
+ /** True when the file name is a rollout the picker would consider. */
53
+ export declare function isRolloutFileName(name: string): boolean;
54
+ /** The exact thread id encoded after Codex's fixed timestamp prefix. */
55
+ export declare function sessionIdFromRolloutFileName(name: string): string | null;
56
+ /** Rollouts below one sessions root, without following symlinks or hiding I/O failures. */
57
+ export declare function listRolloutFiles(root: string): Promise<string[]>;
58
+ export declare function parseEntry(line: string): RolloutEntry | null;
59
+ /**
60
+ * A rollout that cannot be parsed line for line is truncated: Codex writes one whole JSON
61
+ * object per line, so a line that is not one means the file was cut mid-entry.
62
+ */
63
+ export declare function parseRolloutText(text: string): {
64
+ entries: RolloutEntry[];
65
+ truncated: boolean;
66
+ };
67
+ export declare function stringifyRollout(entries: RolloutEntry[]): string;
68
+ export declare function payloadType(entry: RolloutEntry): string;
69
+ /** The entries Codex renders as history, and the only ones this module writes (C-7, C-8). */
70
+ export declare function isMessageEvent(entry: RolloutEntry): boolean;
71
+ export declare function messageTextOf(entry: RolloutEntry): string;
72
+ export declare function readSessionMeta(entry: RolloutEntry): CodexSessionMeta | null;
73
+ export declare function isNotFoundError(error: unknown): boolean;
74
+ /** ISO-8601 UTC, or null when the source recorded nothing usable. */
75
+ export declare function toIsoUtc(value: string | null): string | null;
@@ -0,0 +1,185 @@
1
+ /**
2
+ * What a Codex rollout file is: where it lives, how it is named, and which entries it holds.
3
+ *
4
+ * Every fact here was confirmed against codex-cli 0.146.0 (Constraints: "Facts about Codex are
5
+ * verified against the installed version, never assumed"). The entry names below are the ones
6
+ * C-7 and C-8 measured: `event_msg` drives the visible history and the picker preview,
7
+ * `response_item` drives the model history only.
8
+ */
9
+ import { lstat, readdir } from "node:fs/promises";
10
+ import { homedir } from "node:os";
11
+ import { join, resolve } from "node:path";
12
+ /** Codex reads this variable to find its home; so does this adapter (FR-2, FR-3). */
13
+ export const CODEX_HOME_ENV = "CODEX_HOME";
14
+ /** The version this module's format knowledge was verified against. */
15
+ export const CODEX_CLI_VERSION = "0.146.0";
16
+ /** Recorded in the rollout so a thread imported by this tool is identifiable. */
17
+ export const CODEX_ORIGINATOR = "resume-from";
18
+ /** `thread/list`'s default filter keeps interactive sources; "cli" is one of them (C-7). */
19
+ export const CODEX_SOURCE_CLI = "cli";
20
+ export const CODEX_ENTRY_SESSION_META = "session_meta";
21
+ export const CODEX_ENTRY_EVENT_MSG = "event_msg";
22
+ export const CODEX_ENTRY_RESPONSE_ITEM = "response_item";
23
+ export const CODEX_ENTRY_COMPACTED = "compacted";
24
+ export const CODEX_EVENT_USER_MESSAGE = "user_message";
25
+ export const CODEX_EVENT_AGENT_MESSAGE = "agent_message";
26
+ export const CODEX_ITEM_FUNCTION_CALL = "function_call";
27
+ export const CODEX_ITEM_FUNCTION_CALL_OUTPUT = "function_call_output";
28
+ export const CODEX_ITEM_CUSTOM_TOOL_CALL = "custom_tool_call";
29
+ export const CODEX_ITEM_CUSTOM_TOOL_CALL_OUTPUT = "custom_tool_call_output";
30
+ /**
31
+ * Top-level entry types codex-cli 0.146.0 writes. An entry outside this set is an entry this
32
+ * module does not understand: it is skipped and counted, never guessed at (C-6).
33
+ */
34
+ export const KNOWN_ENTRY_TYPES = new Set([
35
+ CODEX_ENTRY_SESSION_META,
36
+ CODEX_ENTRY_EVENT_MSG,
37
+ CODEX_ENTRY_RESPONSE_ITEM,
38
+ "turn_context",
39
+ "world_state",
40
+ CODEX_ENTRY_COMPACTED,
41
+ "inter_agent_communication_metadata",
42
+ ]);
43
+ /** Always absolute: a HomePath is an absolute path, whatever the environment holds (FR-2). */
44
+ export function defaultCodexHome() {
45
+ const configured = process.env[CODEX_HOME_ENV];
46
+ return configured !== undefined && configured !== ""
47
+ ? resolve(configured)
48
+ : join(homedir(), ".codex");
49
+ }
50
+ export function sessionsRoot(home) {
51
+ return join(home, "sessions");
52
+ }
53
+ /** `<home>/sessions/YYYY/MM/DD/rollout-YYYY-MM-DDTHH-mm-ss-<id>.jsonl`, as Codex lays them out. */
54
+ export function rolloutFilePath(home, sessionId, at) {
55
+ const iso = at.toISOString();
56
+ const [date = "", time = ""] = iso.slice(0, 19).split("T");
57
+ const [year = "", month = "", day = ""] = date.split("-");
58
+ const stamp = `${date}T${time.replaceAll(":", "-")}`;
59
+ return join(sessionsRoot(home), year, month, day, `rollout-${stamp}-${sessionId}.jsonl`);
60
+ }
61
+ /** True when the file name is a rollout the picker would consider. */
62
+ export function isRolloutFileName(name) {
63
+ return name.startsWith("rollout-") && name.endsWith(".jsonl");
64
+ }
65
+ /** The exact thread id encoded after Codex's fixed timestamp prefix. */
66
+ export function sessionIdFromRolloutFileName(name) {
67
+ const match = /^rollout-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-(.+)\.jsonl$/.exec(name);
68
+ return match?.[1] ?? null;
69
+ }
70
+ /** Rollouts below one sessions root, without following symlinks or hiding I/O failures. */
71
+ export async function listRolloutFiles(root) {
72
+ const rootInfo = await lstat(root).catch((error) => {
73
+ if (isNotFoundError(error))
74
+ return null;
75
+ throw error;
76
+ });
77
+ if (rootInfo === null)
78
+ return [];
79
+ if (rootInfo.isSymbolicLink())
80
+ return [];
81
+ if (!rootInfo.isDirectory())
82
+ throw new Error(`Codex sessions root is not a directory: ${root}`);
83
+ const found = [];
84
+ const walk = async (directory) => {
85
+ for (const entry of (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name))) {
86
+ const full = join(directory, entry.name);
87
+ if (entry.isSymbolicLink())
88
+ continue;
89
+ if (entry.isDirectory())
90
+ await walk(full);
91
+ else if (entry.isFile() && isRolloutFileName(entry.name))
92
+ found.push(full);
93
+ }
94
+ };
95
+ await walk(root);
96
+ return found;
97
+ }
98
+ export function parseEntry(line) {
99
+ let value;
100
+ try {
101
+ value = JSON.parse(line);
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ if (typeof value !== "object" || value === null)
107
+ return null;
108
+ const record = value;
109
+ if (typeof record.type !== "string")
110
+ return null;
111
+ const payload = record.payload;
112
+ return {
113
+ timestamp: typeof record.timestamp === "string" ? record.timestamp : "",
114
+ type: record.type,
115
+ payload: typeof payload === "object" && payload !== null ? payload : {},
116
+ };
117
+ }
118
+ /**
119
+ * A rollout that cannot be parsed line for line is truncated: Codex writes one whole JSON
120
+ * object per line, so a line that is not one means the file was cut mid-entry.
121
+ */
122
+ export function parseRolloutText(text) {
123
+ const entries = [];
124
+ let truncated = false;
125
+ for (const line of text.split("\n")) {
126
+ if (line.trim() === "")
127
+ continue;
128
+ const entry = parseEntry(line);
129
+ if (entry === null)
130
+ truncated = true;
131
+ else
132
+ entries.push(entry);
133
+ }
134
+ return { entries, truncated };
135
+ }
136
+ export function stringifyRollout(entries) {
137
+ return entries.map((entry) => `${JSON.stringify(entry)}\n`).join("");
138
+ }
139
+ export function payloadType(entry) {
140
+ const type = entry.payload.type;
141
+ return typeof type === "string" ? type : "";
142
+ }
143
+ /** The entries Codex renders as history, and the only ones this module writes (C-7, C-8). */
144
+ export function isMessageEvent(entry) {
145
+ if (entry.type !== CODEX_ENTRY_EVENT_MSG)
146
+ return false;
147
+ const type = payloadType(entry);
148
+ return type === CODEX_EVENT_USER_MESSAGE || type === CODEX_EVENT_AGENT_MESSAGE;
149
+ }
150
+ export function messageTextOf(entry) {
151
+ const message = entry.payload.message;
152
+ return typeof message === "string" ? message : "";
153
+ }
154
+ export function readSessionMeta(entry) {
155
+ if (entry.type !== CODEX_ENTRY_SESSION_META)
156
+ return null;
157
+ const id = entry.payload.id ?? entry.payload.session_id;
158
+ if (typeof id !== "string" || id === "")
159
+ return null;
160
+ const git = entry.payload.git;
161
+ const gitRecord = typeof git === "object" && git !== null ? git : {};
162
+ return {
163
+ id,
164
+ timestamp: stringOrNull(entry.payload.timestamp) ?? stringOrNull(entry.timestamp),
165
+ cwd: stringOrNull(entry.payload.cwd),
166
+ commit: stringOrNull(gitRecord.commit_hash),
167
+ branch: stringOrNull(gitRecord.branch),
168
+ };
169
+ }
170
+ function stringOrNull(value) {
171
+ return typeof value === "string" && value !== "" ? value : null;
172
+ }
173
+ export function isNotFoundError(error) {
174
+ return (typeof error === "object" &&
175
+ error !== null &&
176
+ "code" in error &&
177
+ error.code === "ENOENT");
178
+ }
179
+ /** ISO-8601 UTC, or null when the source recorded nothing usable. */
180
+ export function toIsoUtc(value) {
181
+ if (value === null)
182
+ return null;
183
+ const parsed = Date.parse(value);
184
+ return Number.isNaN(parsed) ? null : new Date(parsed).toISOString();
185
+ }
@@ -0,0 +1,7 @@
1
+ import type { ValidationDefect } from "./contract.js";
2
+ export interface CodexRolloutInspection {
3
+ itemCount: number;
4
+ defects: ValidationDefect[];
5
+ }
6
+ /** The single file-level check used both before placement and during read-back. */
7
+ export declare function inspectCodexRollout(text: string, expectedSessionId: string): CodexRolloutInspection;
@@ -0,0 +1,78 @@
1
+ import { isAbsolute } from "node:path";
2
+ import { CODEX_ENTRY_RESPONSE_ITEM, CODEX_ENTRY_SESSION_META, CODEX_EVENT_USER_MESSAGE, isMessageEvent, messageTextOf, parseRolloutText, payloadType, readSessionMeta, } from "./rollout.js";
3
+ /** The single file-level check used both before placement and during read-back. */
4
+ export function inspectCodexRollout(text, expectedSessionId) {
5
+ const defects = [];
6
+ const { entries, truncated } = parseRolloutText(text);
7
+ if (truncated) {
8
+ defects.push({
9
+ path: "files/0/bytes",
10
+ message: "a rollout line is not one whole JSON object",
11
+ });
12
+ }
13
+ const metadataEntries = entries.filter((entry) => entry.type === CODEX_ENTRY_SESSION_META);
14
+ const metaEntry = entries[0];
15
+ if (metaEntry === undefined || metaEntry.type !== CODEX_ENTRY_SESSION_META) {
16
+ defects.push({
17
+ path: "items/0",
18
+ message: "the first entry must be session metadata, or the picker cannot list the thread",
19
+ });
20
+ }
21
+ else {
22
+ const meta = readSessionMeta(metaEntry);
23
+ if (meta === null ||
24
+ meta.id !== expectedSessionId ||
25
+ metaEntry.payload.id !== expectedSessionId ||
26
+ metaEntry.payload.session_id !== expectedSessionId) {
27
+ defects.push({
28
+ path: "items/0/payload/id",
29
+ message: "session metadata names a different session",
30
+ });
31
+ }
32
+ if (meta === null || meta.cwd === null || !isAbsolute(meta.cwd)) {
33
+ defects.push({
34
+ path: "items/0/payload/cwd",
35
+ message: "session metadata needs an absolute cwd",
36
+ });
37
+ }
38
+ for (const field of ["originator", "cli_version"]) {
39
+ if (typeof metaEntry.payload[field] !== "string" || metaEntry.payload[field] === "") {
40
+ defects.push({
41
+ path: `items/0/payload/${field}`,
42
+ message: `session metadata needs ${field}`,
43
+ });
44
+ }
45
+ }
46
+ if (Number.isNaN(Date.parse(String(metaEntry.payload.timestamp)))) {
47
+ defects.push({
48
+ path: "items/0/payload/timestamp",
49
+ message: "session metadata needs an ISO timestamp",
50
+ });
51
+ }
52
+ }
53
+ if (metadataEntries.length !== 1) {
54
+ defects.push({
55
+ path: "items",
56
+ message: `a rollout needs exactly one session metadata entry, not ${metadataEntries.length}`,
57
+ });
58
+ }
59
+ const firstUser = entries.find((entry) => payloadType(entry) === CODEX_EVENT_USER_MESSAGE && isMessageEvent(entry));
60
+ if (firstUser === undefined || messageTextOf(firstUser).trim() === "") {
61
+ defects.push({
62
+ path: "items",
63
+ message: "no non-empty user_message entry, so the preview would be empty and thread/list would not show the thread",
64
+ });
65
+ }
66
+ for (const [index, entry] of entries.entries()) {
67
+ if (entry.type === CODEX_ENTRY_RESPONSE_ITEM) {
68
+ defects.push({
69
+ path: `items/${index}`,
70
+ message: "response_item entries fill the model history only — C-7 measured that as invisible",
71
+ });
72
+ }
73
+ }
74
+ return {
75
+ itemCount: entries.filter(isMessageEvent).length,
76
+ defects,
77
+ };
78
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The target role: a canonical session becomes a new Codex thread file.
3
+ *
4
+ * C-7 measured what does not work: the injection API writes `response_item` entries, and the
5
+ * resulting thread is absent from `thread/list` and shows zero turns on resume. C-8 measured what
6
+ * does: a file with session metadata and one `event_msg` entry per turn is listed, previewed and
7
+ * resumed with native turns. This module writes that file, and never calls that API.
8
+ */
9
+ import type { CanonicalSession, ProvenanceMarker, SerializedSession, TargetProfile, ValidationDefect } from "./contract.js";
10
+ export type { CodexSerializeDeps } from "./contract.js";
11
+ import type { CodexSerializeDeps } from "./contract.js";
12
+ export declare function serializeCodex(session: CanonicalSession, target: TargetProfile, marker: ProvenanceMarker, deps: CodexSerializeDeps): SerializedSession;
13
+ /**
14
+ * FR-50, read against C-7's symptoms: a thread without metadata or without a non-empty first
15
+ * user message is invisible to the picker, and a `response_item` entry is the shape C-8 replaced.
16
+ */
17
+ export declare function validateCodex(serialized: SerializedSession): ValidationDefect[];