@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,141 @@
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 { basename, isAbsolute } from "node:path";
10
+ import { CODEX_CLI_VERSION, CODEX_ENTRY_EVENT_MSG, CODEX_ENTRY_SESSION_META, CODEX_EVENT_AGENT_MESSAGE, CODEX_EVENT_USER_MESSAGE, CODEX_ORIGINATOR, CODEX_SOURCE_CLI, isMessageEvent, rolloutFilePath, sessionIdFromRolloutFileName, stringifyRollout, } from "./rollout.js";
11
+ import { inspectCodexRollout } from "./validation.js";
12
+ export function serializeCodex(session, target, marker, deps) {
13
+ const importedAt = resolveStamp(marker.importedAt, session.provenance);
14
+ const sessionId = deps.newSessionId();
15
+ const stamp = importedAt.toISOString();
16
+ const entries = [sessionMetaEntry(sessionId, stamp, deps.cwd(), session)];
17
+ // Codex has no verified durable, out-of-context transcript entry. Do not encode
18
+ // provenance as an agent message: that would make imported metadata look like
19
+ // conversation and could send it back to the model on resume.
20
+ for (const turn of session.turns) {
21
+ const entry = turnEntry(turn, stamp);
22
+ if (entry !== null)
23
+ entries.push(entry);
24
+ }
25
+ return {
26
+ sessionId,
27
+ files: [
28
+ {
29
+ absolutePath: rolloutFilePath(target.home, sessionId, importedAt),
30
+ bytes: Buffer.from(stringifyRollout(entries), "utf8"),
31
+ },
32
+ ],
33
+ itemCount: entries.filter(isMessageEvent).length,
34
+ };
35
+ }
36
+ /**
37
+ * FR-50, read against C-7's symptoms: a thread without metadata or without a non-empty first
38
+ * user message is invisible to the picker, and a `response_item` entry is the shape C-8 replaced.
39
+ */
40
+ export function validateCodex(serialized) {
41
+ const defects = [];
42
+ const file = serialized.files[0];
43
+ if (serialized.files.length !== 1 || file === undefined) {
44
+ defects.push({
45
+ path: "files",
46
+ message: "a Codex thread is exactly one rollout file",
47
+ });
48
+ return defects;
49
+ }
50
+ if (!isAbsolute(file.absolutePath) || !file.absolutePath.endsWith(".jsonl")) {
51
+ defects.push({
52
+ path: "files/0/absolutePath",
53
+ message: `Codex reads rollouts as absolute .jsonl paths, not ${file.absolutePath}`,
54
+ });
55
+ }
56
+ if (sessionIdFromRolloutFileName(basename(file.absolutePath)) !== serialized.sessionId) {
57
+ defects.push({
58
+ path: "files/0/absolutePath",
59
+ message: "the rollout filename names a different session",
60
+ });
61
+ }
62
+ const inspection = inspectCodexRollout(file.bytes.toString("utf8"), serialized.sessionId);
63
+ defects.push(...inspection.defects);
64
+ const written = inspection.itemCount;
65
+ if (written !== serialized.itemCount) {
66
+ defects.push({
67
+ path: "itemCount",
68
+ message: `itemCount says ${serialized.itemCount}, the rollout holds ${written} items`,
69
+ });
70
+ }
71
+ return defects;
72
+ }
73
+ function sessionMetaEntry(sessionId, stamp, cwd, session) {
74
+ const repo = session.provenance.repo;
75
+ const git = repo.commit !== null || repo.branch !== null
76
+ ? { git: { commit_hash: repo.commit, branch: repo.branch } }
77
+ : {};
78
+ return {
79
+ timestamp: stamp,
80
+ type: CODEX_ENTRY_SESSION_META,
81
+ payload: {
82
+ id: sessionId,
83
+ session_id: sessionId,
84
+ timestamp: stamp,
85
+ // Where the user is now. `codex resume` filters the picker by cwd by default.
86
+ cwd,
87
+ originator: CODEX_ORIGINATOR,
88
+ cli_version: CODEX_CLI_VERSION,
89
+ source: CODEX_SOURCE_CLI,
90
+ thread_source: "user",
91
+ model_provider: "openai",
92
+ ...git,
93
+ },
94
+ };
95
+ }
96
+ function turnEntry(turn, fallbackStamp) {
97
+ const text = turn.kind === "tool-call" ? (turn.toolCall?.outcomeLine ?? "") : turn.text;
98
+ if (text.trim() === "")
99
+ return null;
100
+ const stamp = turn.timestamp ?? fallbackStamp;
101
+ return turn.role === "user" ? userMessageEntry(text, stamp) : agentMessageEntry(text, stamp);
102
+ }
103
+ function userMessageEntry(message, stamp) {
104
+ return {
105
+ timestamp: stamp,
106
+ type: CODEX_ENTRY_EVENT_MSG,
107
+ payload: {
108
+ type: CODEX_EVENT_USER_MESSAGE,
109
+ message,
110
+ images: [],
111
+ local_images: [],
112
+ text_elements: [],
113
+ },
114
+ };
115
+ }
116
+ function agentMessageEntry(message, stamp) {
117
+ return {
118
+ timestamp: stamp,
119
+ type: CODEX_ENTRY_EVENT_MSG,
120
+ payload: {
121
+ type: CODEX_EVENT_AGENT_MESSAGE,
122
+ message,
123
+ phase: "commentary",
124
+ memory_citation: null,
125
+ },
126
+ };
127
+ }
128
+ /**
129
+ * Resolve the stamp for the serialized file without reading the clock.
130
+ * Chain: marker.importedAt → session.provenance.updatedAt → session.provenance.startedAt.
131
+ * If none parse, fall back to the Unix epoch so serialize stays deterministic (write design
132
+ * constraint: the preview and the commit of one request must agree, and no ambient state is read).
133
+ */
134
+ function resolveStamp(importedAt, provenance) {
135
+ for (const candidate of [importedAt, provenance.updatedAt, provenance.startedAt]) {
136
+ const t = Date.parse(candidate);
137
+ if (!Number.isNaN(t))
138
+ return new Date(t);
139
+ }
140
+ return new Date(0);
141
+ }
@@ -0,0 +1,74 @@
1
+ import type { Bytes, PendingFile } from "../platform/store/contract.js";
2
+ export type { Bytes, PendingFile };
3
+ import type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole } from "../session/contract.js";
4
+ export type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole, };
5
+ /** How the agent lets the user choose a source session (FR-9, FR-10, FR-58). */
6
+ export type SelectionLevel = "interactive-picker" | "numbered-list";
7
+ /** How far the adapter can take the landing (FR-42). */
8
+ export type LandingLevel = "create-and-switch" | "create-only";
9
+ /** Which roles the adapter fills (FR-59). */
10
+ export type AdapterRole = "source" | "target";
11
+ /** How the agent can show the marker outside the model context (FR-47, FR-48). */
12
+ export type ProvenanceSupport = "out-of-context-entry" | "host-output-only";
13
+ /** Everything the rest of the system may know about one agent (FR-58). */
14
+ export interface AgentCapabilities {
15
+ agent: AgentId;
16
+ roles: AdapterRole[];
17
+ selection: SelectionLevel;
18
+ landing: LandingLevel;
19
+ provenance: ProvenanceSupport;
20
+ /** Home used when the user names none (FR-3). */
21
+ defaultHome: HomePath;
22
+ /** Context window assumed for this agent when configuration overrides none (FR-18). */
23
+ defaultWindowTokens: number;
24
+ }
25
+ /**
26
+ * An opaque handle supplied by the host of the same agent, for example Pi's
27
+ * command context. Nothing outside the adapter of that agent inspects it.
28
+ */
29
+ export type AgentRuntime = unknown;
30
+ /** A structural defect found before placement (FR-50). */
31
+ export interface ValidationDefect {
32
+ /** Pointer into the offending item, for example "items/3/usage". */
33
+ path: string;
34
+ /** What is missing or malformed, and why the agent would fail on it. */
35
+ message: string;
36
+ }
37
+ /** What serializing a canonical session into the target format produced. */
38
+ export interface SerializedSession {
39
+ sessionId: SessionId;
40
+ /** The files to create. The adapter never writes them itself (FR-49, FR-53). */
41
+ files: PendingFile[];
42
+ /** Items the adapter expects the target to store (FR-52). */
43
+ itemCount: number;
44
+ }
45
+ /** What the target actually holds, read back after the commit (FR-51, FR-52). */
46
+ export interface StoredSessionFacts {
47
+ sessionId: SessionId;
48
+ /** Items the target stored. A difference from itemCount is an error (FR-52). */
49
+ itemCount: number;
50
+ /** True when the target's own commands can open the session (FR-51). */
51
+ openable: boolean;
52
+ }
53
+ /** The outcome of asking the agent to move the user into the new session (FR-44). */
54
+ export interface SwitchOutcome {
55
+ switched: boolean;
56
+ /** True when the agent asked the user and the user declined. */
57
+ cancelled: boolean;
58
+ }
59
+ /** What every agent adapter provides. One folder per agent implements it (FR-57). */
60
+ export interface AgentAdapter {
61
+ capabilities(): AgentCapabilities;
62
+ /** Source role. Opens source files for reading only (FR-8, NG-1, AC-4). */
63
+ listSessions(home: HomePath): Promise<SessionDescriptor[]>;
64
+ /** Source role. Reads one session into the neutral vocabulary. Drops every result body. */
65
+ loadSession(descriptor: SessionDescriptor): Promise<CanonicalSession>;
66
+ /** Target role. Produces bytes only. It never creates a file (FR-49, FR-53). */
67
+ serialize(session: CanonicalSession, target: TargetProfile, marker: ProvenanceMarker): SerializedSession;
68
+ /** Target role. Checks the structure before placement. Empty means valid (FR-50). */
69
+ validate(serialized: SerializedSession): ValidationDefect[];
70
+ /** Target role. Reads the committed session back, to compare item counts (FR-51, FR-52). */
71
+ readBack(home: HomePath, sessionId: SessionId): Promise<StoredSessionFacts>;
72
+ /** Target role, only when capabilities().landing is "create-and-switch" (FR-43, FR-44). */
73
+ switchTo(home: HomePath, sessionId: SessionId, runtime: AgentRuntime): Promise<SwitchOutcome>;
74
+ }
@@ -0,0 +1,4 @@
1
+ // GENERATED from src/adapters/module.md — the Public Contract section is the normative home.
2
+ // Declarations only: no behaviour, no defaults. If this file and module.md disagree,
3
+ // the document wins and this file is corrected.
4
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The Pi adapter. It fills both roles of the port: it reads Pi sessions into the canonical
3
+ * vocabulary, and it writes canonical sessions back into files Pi can open.
4
+ *
5
+ * It never writes a file — `serialize` hands `PendingFile` values to `src/import/landing/`
6
+ * (FR-49, FR-53) — never opens a source session for writing (NG-1, AC-4), never calls Pi's
7
+ * model, and never opens a network connection (FR-8).
8
+ */
9
+ import type { AgentAdapter } from "./contract.js";
10
+ import { type PiSerializeDeps } from "./serialize.js";
11
+ export interface PiAdapterDeps extends PiSerializeDeps {
12
+ /** Pi's own default agent directory. Overridden only by tests. */
13
+ defaultHome(): string;
14
+ }
15
+ export declare function createPiAdapter(overrides?: Partial<PiAdapterDeps>): AgentAdapter;
@@ -0,0 +1,209 @@
1
+ /**
2
+ * The Pi adapter. It fills both roles of the port: it reads Pi sessions into the canonical
3
+ * vocabulary, and it writes canonical sessions back into files Pi can open.
4
+ *
5
+ * It never writes a file — `serialize` hands `PendingFile` values to `src/import/landing/`
6
+ * (FR-49, FR-53) — never opens a source session for writing (NG-1, AC-4), never calls Pi's
7
+ * model, and never opens a network connection (FR-8).
8
+ */
9
+ import { randomUUID } from "node:crypto";
10
+ import { readdir, readFile, stat } from "node:fs/promises";
11
+ import { basename, join } from "node:path";
12
+ import { DEFAULT_WINDOW_TOKENS, defaultPiHome, sessionIdFromFileName, sessionsRoot, shortTitle, toIsoUtc, UNREADABLE_TITLE_PREFIX, } from "./format.js";
13
+ import { changedPathsFromEntries, entriesToTurns, lastTimestamp, parseSessionText, resolveActiveEntries, titleFromEntries, } from "./parse.js";
14
+ import { serializeSession } from "./serialize.js";
15
+ import { validateSerialized } from "./validate.js";
16
+ const DEFAULT_DEPS = {
17
+ cwd: () => process.cwd(),
18
+ now: () => new Date(),
19
+ newSessionId: () => randomUUID(),
20
+ newEntryId: () => randomUUID().slice(0, 8),
21
+ defaultHome: defaultPiHome,
22
+ };
23
+ /** Every `.jsonl` below `<home>/sessions/`. Pi keeps one directory per repository. */
24
+ async function sessionFilesIn(home) {
25
+ const root = sessionsRoot(home);
26
+ const files = [];
27
+ const walk = async (dir, depth) => {
28
+ let names;
29
+ try {
30
+ names = await readdir(dir);
31
+ }
32
+ catch {
33
+ return;
34
+ }
35
+ for (const name of names) {
36
+ const full = join(dir, name);
37
+ let isDirectory;
38
+ try {
39
+ isDirectory = (await stat(full)).isDirectory();
40
+ }
41
+ catch {
42
+ continue;
43
+ }
44
+ if (isDirectory) {
45
+ if (depth > 0)
46
+ await walk(full, depth - 1);
47
+ continue;
48
+ }
49
+ if (name.endsWith(".jsonl"))
50
+ files.push(full);
51
+ }
52
+ };
53
+ await walk(root, 2);
54
+ return files;
55
+ }
56
+ async function findSessionFile(home, sessionId) {
57
+ const suffix = `_${sessionId}.jsonl`;
58
+ for (const file of await sessionFilesIn(home)) {
59
+ const name = basename(file);
60
+ if (name.endsWith(suffix) || sessionIdFromFileName(name) === sessionId)
61
+ return file;
62
+ }
63
+ return null;
64
+ }
65
+ function describe(home, filePath, text, fileTime) {
66
+ const parsed = parseSessionText(text);
67
+ const id = parsed.header?.id ?? sessionIdFromFileName(basename(filePath)) ?? basename(filePath);
68
+ const ref = { agent: "pi", home, id };
69
+ const startedAt = toIsoUtc(parsed.header?.timestamp) ?? fileTime;
70
+ const resolved = resolveActiveEntries(parsed.entries);
71
+ if (!parsed.header || parsed.truncated || resolved.unreadable !== null) {
72
+ return {
73
+ ref,
74
+ title: `${UNREADABLE_TITLE_PREFIX}${basename(filePath)}`,
75
+ startedAt,
76
+ updatedAt: lastTimestamp(resolved.activePath) ?? fileTime,
77
+ turnCount: 0,
78
+ repoPath: null,
79
+ filePath,
80
+ };
81
+ }
82
+ const loaded = entriesToTurns(parsed.entries);
83
+ const title = titleFromEntries(resolved.activePath);
84
+ return {
85
+ ref,
86
+ title: title ? shortTitle(title) : `${basename(filePath)}`,
87
+ startedAt,
88
+ updatedAt: lastTimestamp(resolved.activePath) ?? startedAt,
89
+ turnCount: loaded.turns.length,
90
+ repoPath: parsed.header.cwd.length > 0 ? parsed.header.cwd : null,
91
+ filePath,
92
+ };
93
+ }
94
+ /** The part of a Pi command context this module uses, and nothing else (C-10). */
95
+ function asSwitchContext(runtime) {
96
+ if (typeof runtime !== "object" || runtime === null) {
97
+ throw new TypeError("the Pi adapter needs a PiSwitchContext with a switchSession function, and got " +
98
+ `${runtime === null ? "null" : typeof runtime}`);
99
+ }
100
+ const candidate = runtime;
101
+ if (typeof candidate.switchSession !== "function") {
102
+ throw new TypeError("the Pi adapter needs a PiSwitchContext with a switchSession function, and the runtime handle has none");
103
+ }
104
+ return runtime;
105
+ }
106
+ export function createPiAdapter(overrides = {}) {
107
+ const deps = { ...DEFAULT_DEPS, ...overrides };
108
+ return {
109
+ capabilities() {
110
+ return {
111
+ agent: "pi",
112
+ // Pi is the only agent of the three that can move the user itself (C-10).
113
+ roles: ["source", "target"],
114
+ selection: "interactive-picker",
115
+ landing: "create-and-switch",
116
+ provenance: "out-of-context-entry",
117
+ defaultHome: deps.defaultHome(),
118
+ defaultWindowTokens: DEFAULT_WINDOW_TOKENS,
119
+ };
120
+ },
121
+ async listSessions(home) {
122
+ const descriptors = [];
123
+ // Whole-file read and full parse per file: title, turn count, and updatedAt all require
124
+ // parsing the entire JSONL (FR-11). Same approach as the claude-code adapter (not
125
+ // separately benchmarked for Pi files); revisit if a home with thousands of sessions
126
+ // makes the listing noticeably slow to users.
127
+ for (const filePath of await sessionFilesIn(home)) {
128
+ let text;
129
+ let fileTime;
130
+ try {
131
+ text = await readFile(filePath, "utf8");
132
+ fileTime = new Date((await stat(filePath)).mtimeMs).toISOString();
133
+ }
134
+ catch {
135
+ continue;
136
+ }
137
+ descriptors.push(describe(home, filePath, text, fileTime));
138
+ }
139
+ return descriptors.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
140
+ },
141
+ async loadSession(descriptor) {
142
+ const text = await readFile(descriptor.filePath, "utf8");
143
+ const parsed = parseSessionText(text);
144
+ const resolved = resolveActiveEntries(parsed.entries);
145
+ if (!parsed.header || parsed.truncated || resolved.unreadable !== null) {
146
+ throw new Error(`the Pi session file ${descriptor.filePath} cannot be read: it is truncated or not a Pi session`);
147
+ }
148
+ const loaded = entriesToTurns(parsed.entries);
149
+ const startedAt = toIsoUtc(parsed.header.timestamp) ?? descriptor.startedAt;
150
+ return {
151
+ provenance: {
152
+ ref: { agent: "pi", home: descriptor.ref.home, id: parsed.header.id },
153
+ title: descriptor.title,
154
+ startedAt,
155
+ updatedAt: lastTimestamp(resolved.activePath) ?? startedAt,
156
+ repo: {
157
+ // Pi records neither the commit nor the branch of a session.
158
+ commit: null,
159
+ branch: null,
160
+ changedPaths: changedPathsFromEntries(resolved.activePath),
161
+ },
162
+ },
163
+ turns: loaded.turns,
164
+ };
165
+ },
166
+ serialize(session, target, marker) {
167
+ return serializeSession(session, target, marker, deps);
168
+ },
169
+ validate(serialized) {
170
+ return validateSerialized(serialized);
171
+ },
172
+ async readBack(home, sessionId) {
173
+ const filePath = await findSessionFile(home, sessionId);
174
+ if (!filePath)
175
+ return { sessionId, itemCount: 0, openable: false };
176
+ let parsed;
177
+ try {
178
+ parsed = parseSessionText(await readFile(filePath, "utf8"));
179
+ }
180
+ catch {
181
+ return { sessionId, itemCount: 0, openable: false };
182
+ }
183
+ return {
184
+ sessionId,
185
+ itemCount: parsed.entries.length,
186
+ openable: parsed.header?.id === sessionId &&
187
+ !parsed.truncated &&
188
+ resolveActiveEntries(parsed.entries).unreadable === null,
189
+ };
190
+ },
191
+ async switchTo(home, sessionId, runtime) {
192
+ const context = asSwitchContext(runtime);
193
+ const filePath = await findSessionFile(home, sessionId);
194
+ if (!filePath) {
195
+ throw new Error(`the Pi home ${home} holds no session ${sessionId} to switch to`);
196
+ }
197
+ // Called from a Pi command handler only. The deleted design documents claimed a call
198
+ // from an event handler deadlocks; the claim was never tested, so this module only
199
+ // uses the path C-10 proved. `src/host/pi-extension/` guarantees the call site.
200
+ const result = await context.switchSession(filePath, {
201
+ withSession: () => undefined,
202
+ });
203
+ const cancelled = result?.cancelled === true;
204
+ // A cancelled switch is not a failure: the session stays committed and the user opens
205
+ // it later with Pi's own command.
206
+ return { switched: !cancelled, cancelled };
207
+ },
208
+ };
209
+ }
@@ -0,0 +1,24 @@
1
+ import type { AdapterRole, AgentAdapter, AgentCapabilities, AgentRuntime, Bytes, LandingLevel, PendingFile, ProvenanceSupport, SelectionLevel, SerializedSession, StoredSessionFacts, SwitchOutcome, ValidationDefect } from "../contract.js";
2
+ export type { AdapterRole, AgentAdapter, AgentCapabilities, AgentRuntime, Bytes, LandingLevel, PendingFile, ProvenanceSupport, SelectionLevel, SerializedSession, StoredSessionFacts, SwitchOutcome, ValidationDefect, };
3
+ import type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole } from "../../session/contract.js";
4
+ export type { AgentId, CanonicalSession, CanonicalTurn, HomePath, ProvenanceMarker, RepoSnapshot, SessionDescriptor, SessionId, SessionRef, SourceProvenance, TargetProfile, ToolCallRecord, ToolEffect, TurnKind, TurnRole, };
5
+ /** The options Pi's switchSession takes (C-10). */
6
+ export interface PiSwitchOptions {
7
+ /** Pi calls this once the new session is active. */
8
+ withSession: () => void;
9
+ }
10
+ /** What Pi's switchSession returns (C-10). */
11
+ export interface PiSwitchResult {
12
+ cancelled: boolean;
13
+ }
14
+ /**
15
+ * The part of Pi's command context this module uses. `src/host/pi-extension/`
16
+ * supplies it as the AgentRuntime handle; nothing else may construct one (C-10).
17
+ */
18
+ export interface PiSwitchContext {
19
+ switchSession(path: string, options: PiSwitchOptions): Promise<PiSwitchResult>;
20
+ }
21
+ /** Builds the Pi adapter. The only export of this module (FR-57). */
22
+ export interface PiAdapterFactory {
23
+ create(): AgentAdapter;
24
+ }
@@ -0,0 +1,4 @@
1
+ // GENERATED from src/adapters/pi/module.md — the Public Contract section is the normative home.
2
+ // Declarations only: no behaviour, no defaults. If this file and module.md disagree,
3
+ // the document wins and this file is corrected.
4
+ export {};
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Everything this tool knows about Pi's session file layout.
3
+ *
4
+ * Verified against the installed Pi 0.83.0 (`dist/core/session-manager.js`,
5
+ * `dist/config.js`), not assumed:
6
+ *
7
+ * - the home is Pi's agent directory: `$PI_CODING_AGENT_DIR`, else `~/.pi/agent`;
8
+ * - sessions live in `<home>/sessions/<encoded cwd>/<timestamp>_<id>.jsonl`;
9
+ * - a file is JSONL: one `session` header, then entries with `id`/`parentId`/`timestamp`;
10
+ * - an assistant message without a numeric `usage` object crashes Pi (C-11).
11
+ *
12
+ * When Pi changes, this file changes. Nothing outside this folder holds these facts.
13
+ */
14
+ import type { ToolEffect } from "./contract.js";
15
+ /** Pi's current session file version (`CURRENT_SESSION_VERSION`). */
16
+ export declare const PI_SESSION_VERSION = 3;
17
+ export declare const SESSIONS_DIR_NAME = "sessions";
18
+ /**
19
+ * The customType of the provenance entry. It is written as a `custom` entry, which Pi's
20
+ * `buildSessionContext` drops on the floor: the marker is stored and shown, never sent to
21
+ * the model (FR-47, FR-48). `src/host/pi-extension/` registers the renderer that shows it.
22
+ */
23
+ export declare const MARKER_CUSTOM_TYPE = "resume-from-provenance";
24
+ /** Prefix of the title of a session file that cannot be read (T-PI-13). */
25
+ export declare const UNREADABLE_TITLE_PREFIX = "(unreadable) ";
26
+ /** What replaces a tool result body (FR-24, FR-25). */
27
+ export declare const DROPPED_BODY_NOTE = "(content dropped: imported session, may be stale)";
28
+ /** Context window assumed for Pi when configuration overrides none (FR-18). */
29
+ export declare const DEFAULT_WINDOW_TOKENS = 200000;
30
+ /** What an imported assistant turn declares about itself. It cost nothing to import. */
31
+ export declare const IMPORTED_API = "resume-from";
32
+ export declare const IMPORTED_PROVIDER = "resume-from";
33
+ export declare const IMPORTED_MODEL = "imported-session";
34
+ /** Pi's default agent directory. Mirrors Pi's own `getAgentDir()`. */
35
+ export declare function defaultPiHome(): string;
36
+ /** Pi's encoding of a working directory into one directory name. */
37
+ export declare function encodeCwd(cwd: string): string;
38
+ export declare function sessionsRoot(home: string): string;
39
+ export declare function sessionDirFor(home: string, cwd: string): string;
40
+ /** Pi's own file naming: the ISO timestamp with `:` and `.` replaced, then the id. */
41
+ export declare function sessionFileName(isoTimestamp: string, sessionId: string): string;
42
+ /** The session id Pi would read out of a session file name, or null. */
43
+ export declare function sessionIdFromFileName(fileName: string): string | null;
44
+ export interface PiSessionHeader {
45
+ type: "session";
46
+ version?: number;
47
+ id: string;
48
+ timestamp: string;
49
+ cwd: string;
50
+ }
51
+ export interface PiEntryBase {
52
+ type: string;
53
+ id: string;
54
+ parentId: string | null;
55
+ timestamp: string;
56
+ }
57
+ export type PiEntry = PiEntryBase & Record<string, unknown>;
58
+ export interface PiTextBlock {
59
+ type: "text";
60
+ text: string;
61
+ textSignature?: string;
62
+ }
63
+ export interface PiThinkingBlock {
64
+ type: "thinking";
65
+ thinking: string;
66
+ thinkingSignature?: string;
67
+ }
68
+ export interface PiImageBlock {
69
+ type: "image";
70
+ data: string;
71
+ mimeType: string;
72
+ }
73
+ export interface PiToolCallBlock {
74
+ type: "toolCall";
75
+ id: string;
76
+ name: string;
77
+ arguments: Record<string, unknown>;
78
+ }
79
+ export type PiContentBlock = PiTextBlock | PiThinkingBlock | PiToolCallBlock | PiImageBlock;
80
+ /** The six numbers Pi dereferences without a guard. Missing any of them is C-11. */
81
+ export interface PiUsage {
82
+ input: number;
83
+ output: number;
84
+ cacheRead: number;
85
+ cacheWrite: number;
86
+ reasoning?: number;
87
+ totalTokens: number;
88
+ cost: {
89
+ input: number;
90
+ output: number;
91
+ cacheRead: number;
92
+ cacheWrite: number;
93
+ total: number;
94
+ };
95
+ }
96
+ export interface PiUserMessage {
97
+ role: "user";
98
+ content: string | PiContentBlock[];
99
+ timestamp: number;
100
+ }
101
+ export interface PiAssistantMessage {
102
+ role: "assistant";
103
+ content: PiContentBlock[];
104
+ api: string;
105
+ provider: string;
106
+ model: string;
107
+ usage: PiUsage;
108
+ stopReason: string;
109
+ timestamp: number;
110
+ }
111
+ export interface PiToolResultMessage {
112
+ role: "toolResult";
113
+ toolCallId: string;
114
+ toolName: string;
115
+ content: PiContentBlock[];
116
+ isError: boolean;
117
+ timestamp: number;
118
+ }
119
+ export type PiMessage = PiUserMessage | PiAssistantMessage | PiToolResultMessage;
120
+ export interface PiMessageEntry extends PiEntryBase {
121
+ type: "message";
122
+ message: PiMessage;
123
+ }
124
+ export interface PiCompactionEntry extends PiEntryBase {
125
+ type: "compaction";
126
+ summary: string;
127
+ firstKeptEntryId?: string;
128
+ retainedTail?: PiMessage[];
129
+ tokensBefore: number;
130
+ }
131
+ export interface PiCustomEntry extends PiEntryBase {
132
+ type: "custom";
133
+ customType: string;
134
+ data?: unknown;
135
+ }
136
+ /**
137
+ * Entry types Pi writes that carry no turn: settings, bookmarks, and extension state.
138
+ * They are known, so they are not reported as skipped.
139
+ *
140
+ * `custom_message` is deliberately absent. Pi's `sessionEntryToContextMessages` turns it
141
+ * into a real context message, so an extension can put conversation content there. This
142
+ * adapter does not know that content's shape, so it is skipped — and counted, so the skip
143
+ * is visible rather than a silent shortening of the session.
144
+ */
145
+ export declare const NON_TURN_ENTRY_TYPES: ReadonlySet<string>;
146
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
147
+ export declare function asHeader(value: unknown): PiSessionHeader | null;
148
+ export declare function asEntry(value: unknown): PiEntry | null;
149
+ export declare function entryMessage(entry: PiEntry): Record<string, unknown> | null;
150
+ /** The visible text of a message content, hidden reasoning excluded (FR-28). */
151
+ export declare function visibleText(content: unknown): string;
152
+ export declare function toolCallBlocks(content: unknown): PiToolCallBlock[];
153
+ /** Did the call change the repository? (FR-26) A shell is never assumed either way. */
154
+ export declare function toolEffectFor(toolName: string): ToolEffect;
155
+ /** One line, whatever the input contained. */
156
+ export declare function oneLine(text: string): string;
157
+ export declare function truncate(text: string, limit: number): string;
158
+ export declare function shortArguments(argumentsText: string): string;
159
+ export declare function shortTitle(text: string): string;
160
+ /** ISO-8601 UTC, or null when the source recorded nothing usable. */
161
+ export declare function toIsoUtc(value: unknown): string | null;
162
+ export declare function emptyUsage(): PiUsage;