hyperbrane 0.1.2 → 0.2.0

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 (287) hide show
  1. package/README.md +67 -25
  2. package/dist/agent/agentClient.d.ts +5 -0
  3. package/dist/agent/agentClient.js +4 -0
  4. package/dist/agent/agentService.d.ts +9 -37
  5. package/dist/agent/agentService.js +62 -322
  6. package/dist/agent/agentStorage.d.ts +1 -20
  7. package/dist/agent/agentStorage.js +0 -30
  8. package/dist/agent/api.d.ts +80 -151
  9. package/dist/agent/api.js +1 -8
  10. package/dist/agent/client.d.ts +3 -1
  11. package/dist/agent/client.js +2 -3
  12. package/dist/agent/control.d.ts +23 -0
  13. package/dist/agent/control.js +479 -0
  14. package/dist/agent/hostTranscript.d.ts +0 -6
  15. package/dist/agent/hostTranscript.js +3 -14
  16. package/dist/agent/session/backend.d.ts +17 -32
  17. package/dist/agent/session/claude/home.d.ts +0 -1
  18. package/dist/agent/session/claude/home.js +0 -1
  19. package/dist/agent/session/claude/provision.d.ts +10 -0
  20. package/dist/agent/session/claude/provision.js +17 -0
  21. package/dist/agent/session/claude/transcript.d.ts +2 -7
  22. package/dist/agent/session/claude/transcript.js +68 -14
  23. package/dist/agent/session/client.d.ts +1 -10
  24. package/dist/agent/session/client.js +0 -3
  25. package/dist/agent/session/codex/provision.d.ts +14 -0
  26. package/dist/agent/session/codex/provision.js +25 -0
  27. package/dist/agent/session/fileTranscript.d.ts +6 -49
  28. package/dist/agent/session/fileTranscript.js +26 -82
  29. package/dist/agent/session/install.d.ts +5 -0
  30. package/dist/agent/session/install.js +35 -0
  31. package/dist/agent/session/sessionStorage.d.ts +4 -35
  32. package/dist/agent/session/sessionStorage.js +10 -16
  33. package/dist/agent/session/sharedConnection.d.ts +0 -8
  34. package/dist/agent/session/sharedConnection.js +0 -7
  35. package/dist/agent/session/storedTranscripts.d.ts +1 -6
  36. package/dist/agent/session/storedTranscripts.js +0 -3
  37. package/dist/agent/session/worker/jsonRpc.d.ts +2 -13
  38. package/dist/agent/session/worker/jsonRpc.js +2 -11
  39. package/dist/agent/session/worker/protocol.d.ts +26 -20
  40. package/dist/agent/session/worker/protocol.js +29 -18
  41. package/dist/agent/session/worker/workerParams.d.ts +0 -5
  42. package/dist/agent/session/worker/workerParams.js +14 -14
  43. package/dist/agent/session/workerConnection.d.ts +2 -13
  44. package/dist/agent/session/workerConnection.js +7 -12
  45. package/dist/agent/session/workerGatewayConnection.d.ts +18 -0
  46. package/dist/agent/session/workerGatewayConnection.js +86 -0
  47. package/dist/agent/vendor-types/manifest.json +87 -0
  48. package/dist/agent/workerBundle.d.ts +4 -19
  49. package/dist/agent/workerBundle.generated.js +1 -1
  50. package/dist/agent/workerBundle.js +3 -30
  51. package/dist/agent/workerReplacement.d.ts +3 -0
  52. package/dist/agent/workerReplacement.js +49 -0
  53. package/dist/cli/agent.js +31 -16
  54. package/dist/cli/command.d.ts +10 -11
  55. package/dist/cli/command.js +0 -4
  56. package/dist/cli/dimension.js +6 -6
  57. package/dist/cli/gateway.js +3 -6
  58. package/dist/cli/main.js +14 -31
  59. package/dist/cli/streamPrinter.d.ts +2 -10
  60. package/dist/cli/streamPrinter.js +16 -46
  61. package/dist/cli/table.d.ts +0 -3
  62. package/dist/cli/table.js +0 -4
  63. package/dist/dimension/api.d.ts +39 -27
  64. package/dist/dimension/api.js +28 -47
  65. package/dist/dimension/client.d.ts +5 -0
  66. package/dist/dimension/client.js +4 -0
  67. package/dist/dimension/control.d.ts +21 -0
  68. package/dist/dimension/control.js +328 -0
  69. package/dist/dimension/image.d.ts +0 -5
  70. package/dist/dimension/image.js +0 -8
  71. package/dist/dimension/imageFiles.generated.js +1 -1
  72. package/dist/dimension/paths.d.ts +5 -4
  73. package/dist/dimension/paths.js +15 -6
  74. package/dist/dimension/service.d.ts +8 -24
  75. package/dist/dimension/service.js +60 -228
  76. package/dist/dimension/transport-worker.d.ts +1 -0
  77. package/dist/dimension/transport-worker.js +151 -0
  78. package/dist/dimension/transportBundle.d.ts +2 -0
  79. package/dist/dimension/transportBundle.generated.d.ts +1 -0
  80. package/dist/dimension/transportBundle.generated.js +2 -0
  81. package/dist/dimension/transportBundle.js +4 -0
  82. package/dist/dimension/transportClient.d.ts +10 -0
  83. package/dist/dimension/transportClient.js +115 -0
  84. package/dist/dimension/transportProtocol.d.ts +53 -0
  85. package/dist/dimension/transportProtocol.js +8 -0
  86. package/dist/index.d.ts +5 -9
  87. package/dist/index.js +5 -19
  88. package/dist/lib/control.d.ts +8 -0
  89. package/dist/lib/control.js +62 -0
  90. package/dist/lib/docker.d.ts +0 -22
  91. package/dist/lib/docker.js +0 -10
  92. package/dist/lib/image.d.ts +0 -3
  93. package/dist/lib/image.js +0 -2
  94. package/dist/lib/jsonDirectory.d.ts +0 -13
  95. package/dist/lib/jsonDirectory.js +10 -15
  96. package/dist/lib/scriptBundle.d.ts +6 -0
  97. package/dist/lib/scriptBundle.js +28 -0
  98. package/dist/lib/sh.d.ts +2 -0
  99. package/dist/lib/sh.js +8 -0
  100. package/dist/lib/sleep.d.ts +0 -1
  101. package/dist/lib/sleep.js +0 -1
  102. package/dist/lib/streamFetch.d.ts +15 -0
  103. package/dist/lib/streamFetch.js +150 -0
  104. package/dist/lib/tar.d.ts +0 -1
  105. package/dist/lib/tar.js +5 -11
  106. package/dist/portal-gateway/api.d.ts +13 -44
  107. package/dist/portal-gateway/api.js +1 -3
  108. package/dist/portal-gateway/control.d.ts +42 -0
  109. package/dist/portal-gateway/control.js +657 -0
  110. package/dist/portal-gateway/gatewayClient.d.ts +5 -0
  111. package/dist/portal-gateway/gatewayClient.js +4 -0
  112. package/dist/portal-gateway/gatewayService.d.ts +8 -38
  113. package/dist/portal-gateway/gatewayService.js +68 -254
  114. package/dist/portal-gateway/image.d.ts +0 -1
  115. package/dist/portal-gateway/image.js +0 -2
  116. package/dist/portal-gateway/imageFiles.generated.js +2 -2
  117. package/dist/portal-gateway/portalClient.d.ts +5 -0
  118. package/dist/portal-gateway/portalClient.js +4 -0
  119. package/dist/portal-gateway/portalService.d.ts +5 -47
  120. package/dist/portal-gateway/portalService.js +19 -349
  121. package/dist/portal-gateway/transport-worker.d.ts +1 -0
  122. package/dist/portal-gateway/transport-worker.js +205 -0
  123. package/dist/portal-gateway/transportBundle.d.ts +2 -0
  124. package/dist/portal-gateway/transportBundle.generated.d.ts +1 -0
  125. package/dist/portal-gateway/transportBundle.generated.js +2 -0
  126. package/dist/portal-gateway/transportBundle.js +4 -0
  127. package/dist/portal-gateway/upstream.d.ts +0 -81
  128. package/dist/portal-gateway/upstream.js +0 -1
  129. package/dist/sandbox/api.d.ts +121 -187
  130. package/dist/sandbox/api.js +5 -7
  131. package/dist/sandbox/client.d.ts +2 -0
  132. package/dist/sandbox/client.js +4 -0
  133. package/dist/sandbox/data-endpoints/dataEndpoints.d.ts +13 -0
  134. package/dist/sandbox/data-endpoints/dataEndpoints.js +100 -0
  135. package/dist/sandbox/data-endpoints/firewalledPorts.d.ts +4 -0
  136. package/dist/sandbox/data-endpoints/firewalledPorts.js +28 -0
  137. package/dist/sandbox/lib/httpOverStream.d.ts +3 -0
  138. package/dist/sandbox/lib/httpOverStream.js +21 -0
  139. package/dist/sandbox/lib/remoteFileOperations.d.ts +10 -0
  140. package/dist/sandbox/lib/remoteFileOperations.js +70 -0
  141. package/dist/sandbox/lib/shellCommand.d.ts +3 -0
  142. package/dist/sandbox/lib/shellCommand.js +7 -0
  143. package/dist/sandbox/providers/boat/boatService.d.ts +2 -0
  144. package/dist/sandbox/providers/boat/boatService.js +321 -0
  145. package/dist/sandbox/providers/exedev/exeDevService.d.ts +2 -0
  146. package/dist/sandbox/providers/exedev/exeDevService.js +127 -0
  147. package/dist/sandbox/providers/exedev/exeDevToken.d.ts +1 -0
  148. package/dist/sandbox/providers/exedev/exeDevToken.js +54 -0
  149. package/dist/sandbox/providers/local/localService.d.ts +2 -0
  150. package/dist/sandbox/providers/local/localService.js +219 -0
  151. package/dist/sandbox/providers/ssh/sshService.d.ts +2 -0
  152. package/dist/sandbox/providers/ssh/sshService.js +82 -0
  153. package/dist/sandbox/providers/ssh/sshTransport.d.ts +12 -0
  154. package/dist/sandbox/providers/ssh/sshTransport.js +216 -0
  155. package/dist/sandbox/service.d.ts +2 -0
  156. package/dist/sandbox/service.js +18 -0
  157. package/dist/sandbox/storage/descriptors.d.ts +6 -0
  158. package/dist/sandbox/storage/descriptors.js +25 -0
  159. package/dist/sandbox/storage/sandboxStorage.d.ts +11 -0
  160. package/dist/sandbox/storage/sandboxStorage.js +42 -0
  161. package/dist/upstreams/anthropic.d.ts +0 -5
  162. package/dist/upstreams/anthropic.js +1 -8
  163. package/dist/upstreams/index.d.ts +2 -6
  164. package/dist/upstreams/index.js +0 -4
  165. package/dist/upstreams/openai.d.ts +0 -5
  166. package/dist/upstreams/openai.js +1 -13
  167. package/package.json +12 -4
  168. package/dist/agent/gatewaySignIns.d.ts +0 -5
  169. package/dist/agent/gatewaySignIns.js +0 -11
  170. package/dist/agent/session/codex/home.d.ts +0 -2
  171. package/dist/agent/session/codex/home.js +0 -4
  172. package/dist/agent/session/codex/messages.d.ts +0 -30
  173. package/dist/agent/session/codex/messages.js +0 -135
  174. package/dist/agent/session/codex/transcript.d.ts +0 -12
  175. package/dist/agent/session/codex/transcript.js +0 -22
  176. package/dist/agent/session/sdkMessages.d.ts +0 -15
  177. package/dist/agent/session/sdkMessages.js +0 -28
  178. package/dist/agent/session/workerSseConnection.d.ts +0 -31
  179. package/dist/agent/session/workerSseConnection.js +0 -76
  180. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.d.ts +0 -10
  181. package/dist/agent/vendor-types/codex/app-server/AbsolutePathBuf.js +0 -2
  182. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.d.ts +0 -2
  183. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputBody.js +0 -2
  184. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.d.ts +0 -22
  185. package/dist/agent/vendor-types/codex/app-server/FunctionCallOutputContentItem.js +0 -2
  186. package/dist/agent/vendor-types/codex/app-server/ImageDetail.d.ts +0 -1
  187. package/dist/agent/vendor-types/codex/app-server/ImageDetail.js +0 -2
  188. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.d.ts +0 -5
  189. package/dist/agent/vendor-types/codex/app-server/ImageGenerationFailure.js +0 -2
  190. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.d.ts +0 -11
  191. package/dist/agent/vendor-types/codex/app-server/ImageGenerationItem.js +0 -2
  192. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.d.ts +0 -23
  193. package/dist/agent/vendor-types/codex/app-server/LegacyAppPathString.js +0 -2
  194. package/dist/agent/vendor-types/codex/app-server/MessagePhase.d.ts +0 -7
  195. package/dist/agent/vendor-types/codex/app-server/MessagePhase.js +0 -2
  196. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.d.ts +0 -4
  197. package/dist/agent/vendor-types/codex/app-server/ReasoningEffort.js +0 -2
  198. package/dist/agent/vendor-types/codex/app-server/SleepItem.d.ts +0 -7
  199. package/dist/agent/vendor-types/codex/app-server/SleepItem.js +0 -2
  200. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.d.ts +0 -14
  201. package/dist/agent/vendor-types/codex/app-server/WebSearchItem.js +0 -2
  202. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.d.ts +0 -3
  203. package/dist/agent/vendor-types/codex/app-server/serde_json/JsonValue.js +0 -2
  204. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.d.ts +0 -1
  205. package/dist/agent/vendor-types/codex/app-server/v2/AgentMessageDelivery.js +0 -2
  206. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.d.ts +0 -4
  207. package/dist/agent/vendor-types/codex/app-server/v2/AsyncUserInputQuestion.js +0 -2
  208. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.d.ts +0 -4
  209. package/dist/agent/vendor-types/codex/app-server/v2/ByteRange.js +0 -2
  210. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.d.ts +0 -5
  211. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentState.js +0 -2
  212. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.d.ts +0 -1
  213. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentStatus.js +0 -2
  214. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.d.ts +0 -1
  215. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentTool.js +0 -2
  216. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.d.ts +0 -1
  217. package/dist/agent/vendor-types/codex/app-server/v2/CollabAgentToolCallStatus.js +0 -2
  218. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.d.ts +0 -19
  219. package/dist/agent/vendor-types/codex/app-server/v2/CommandAction.js +0 -2
  220. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.d.ts +0 -1
  221. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionSource.js +0 -2
  222. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.d.ts +0 -1
  223. package/dist/agent/vendor-types/codex/app-server/v2/CommandExecutionStatus.js +0 -2
  224. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.d.ts +0 -10
  225. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallOutputContentItem.js +0 -2
  226. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.d.ts +0 -1
  227. package/dist/agent/vendor-types/codex/app-server/v2/DynamicToolCallStatus.js +0 -2
  228. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.d.ts +0 -6
  229. package/dist/agent/vendor-types/codex/app-server/v2/FileUpdateChange.js +0 -2
  230. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.d.ts +0 -4
  231. package/dist/agent/vendor-types/codex/app-server/v2/HookPromptFragment.js +0 -2
  232. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.d.ts +0 -1
  233. package/dist/agent/vendor-types/codex/app-server/v2/McpAppDisplayMode.js +0 -2
  234. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.d.ts +0 -8
  235. package/dist/agent/vendor-types/codex/app-server/v2/McpAppUi.js +0 -2
  236. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.d.ts +0 -7
  237. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallAppContext.js +0 -2
  238. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.d.ts +0 -3
  239. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallError.js +0 -2
  240. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.d.ts +0 -6
  241. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallResult.js +0 -2
  242. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.d.ts +0 -1
  243. package/dist/agent/vendor-types/codex/app-server/v2/McpToolCallStatus.js +0 -2
  244. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.d.ts +0 -5
  245. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitation.js +0 -2
  246. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.d.ts +0 -6
  247. package/dist/agent/vendor-types/codex/app-server/v2/MemoryCitationEntry.js +0 -2
  248. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.d.ts +0 -1
  249. package/dist/agent/vendor-types/codex/app-server/v2/PatchApplyStatus.js +0 -2
  250. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.d.ts +0 -8
  251. package/dist/agent/vendor-types/codex/app-server/v2/PatchChangeKind.js +0 -2
  252. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.d.ts +0 -1
  253. package/dist/agent/vendor-types/codex/app-server/v2/SubAgentActivityKind.js +0 -2
  254. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.d.ts +0 -11
  255. package/dist/agent/vendor-types/codex/app-server/v2/TextElement.js +0 -2
  256. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.d.ts +0 -215
  257. package/dist/agent/vendor-types/codex/app-server/v2/ThreadItem.js +0 -2
  258. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.d.ts +0 -35
  259. package/dist/agent/vendor-types/codex/app-server/v2/UserInput.js +0 -2
  260. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.d.ts +0 -14
  261. package/dist/agent/vendor-types/codex/app-server/v2/WebSearchAction.js +0 -2
  262. package/dist/cli/services.d.ts +0 -19
  263. package/dist/cli/services.js +0 -34
  264. package/dist/dimension/http.d.ts +0 -7
  265. package/dist/dimension/http.js +0 -77
  266. package/dist/dimension/storage.d.ts +0 -39
  267. package/dist/dimension/storage.js +0 -79
  268. package/dist/portal-gateway/fakes.d.ts +0 -3
  269. package/dist/portal-gateway/fakes.js +0 -11
  270. package/dist/portal-gateway/gatewayStorage.d.ts +0 -66
  271. package/dist/portal-gateway/gatewayStorage.js +0 -125
  272. package/dist/portal-gateway/portalStorage.d.ts +0 -34
  273. package/dist/portal-gateway/portalStorage.js +0 -44
  274. package/dist/sandbox/boatService.d.ts +0 -52
  275. package/dist/sandbox/boatService.js +0 -343
  276. package/dist/sandbox/containerService.d.ts +0 -15
  277. package/dist/sandbox/containerService.js +0 -184
  278. package/dist/sandbox/endpointProxy.d.ts +0 -90
  279. package/dist/sandbox/endpointProxy.js +0 -716
  280. package/dist/sandbox/exeDevAuth.d.ts +0 -16
  281. package/dist/sandbox/exeDevAuth.js +0 -49
  282. package/dist/sandbox/localService.d.ts +0 -19
  283. package/dist/sandbox/localService.js +0 -200
  284. package/dist/sandbox/sseConnection.d.ts +0 -41
  285. package/dist/sandbox/sseConnection.js +0 -166
  286. package/dist/sandbox/sshService.d.ts +0 -30
  287. package/dist/sandbox/sshService.js +0 -680
@@ -0,0 +1,17 @@
1
+ import { existsSync } from "node:fs";
2
+ import { installPackage, installedPackage, pinnedVersion } from "../install.js";
3
+ export const CLAUDE_SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
4
+ export function claudePackage() {
5
+ return { pkg: CLAUDE_SDK_PACKAGE, version: pinnedVersion("claude", CLAUDE_SDK_PACKAGE), executables: `${CLAUDE_SDK_PACKAGE}-linux-*/claude` };
6
+ }
7
+ export function installClaude(installDir) {
8
+ return installPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion("claude", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));
9
+ }
10
+ export function installedClaude(installDir) {
11
+ return installedPackage(installDir, CLAUDE_SDK_PACKAGE, pinnedVersion("claude", CLAUDE_SDK_PACKAGE), () => claudeExecutable(installDir));
12
+ }
13
+ export function claudeExecutable(installDir) {
14
+ const base = `${installDir}/node_modules/${CLAUDE_SDK_PACKAGE}-linux-${process.arch}`;
15
+ const glibc = `${base}/claude`;
16
+ return existsSync(glibc) ? glibc : `${base}-musl/claude`;
17
+ }
@@ -1,13 +1,8 @@
1
1
  import type { AgentTranscriptMessage } from "../../api.ts";
2
2
  import type { TranscriptSource } from "../backend.ts";
3
3
  import { type FileTranscript, type TranscriptLayout } from "../fileTranscript.ts";
4
- /**
5
- * The conversation from claude's own session file, <config>/projects/<cwd>/<session>.jsonl. Its user
6
- * and assistant entries are the messages the stream carried, with their uuids (claude keeps the one
7
- * a prompt was sent with); the rest (attachments, queue bookkeeping, a subagent's sidechain, meta
8
- * notes) isn't the conversation.
9
- */
10
4
  export declare function claudeTranscript({ nativeId, env, files }: TranscriptSource): FileTranscript;
11
- /** projects/<cwd>/<session>.jsonl in claude's config directory. */
5
+ export declare function claudeSubagentTranscript(source: TranscriptSource, agentId: string, toolUseId: string): FileTranscript;
6
+ export declare function claudeFindSubagent(source: TranscriptSource, toolUseId: string): Promise<string | null>;
12
7
  export declare const CLAUDE_TRANSCRIPT_LAYOUT: TranscriptLayout;
13
8
  export declare function claudeLineMessages(line: unknown): AgentTranscriptMessage[];
@@ -1,29 +1,83 @@
1
- import { createFileTranscript, locateTranscript } from "../fileTranscript.js";
2
- /**
3
- * The conversation from claude's own session file, <config>/projects/<cwd>/<session>.jsonl. Its user
4
- * and assistant entries are the messages the stream carried, with their uuids (claude keeps the one
5
- * a prompt was sent with); the rest (attachments, queue bookkeeping, a subagent's sidechain, meta
6
- * notes) isn't the conversation.
7
- */
1
+ import { createFileTranscript, directoryNames, locateTranscript } from "../fileTranscript.js";
8
2
  export function claudeTranscript({ nativeId, env, files }) {
9
- const home = env.CLAUDE_CONFIG_DIR ?? `${env.HOME}/.claude`;
10
- return createFileTranscript(files, () => locateTranscript(files, home, CLAUDE_TRANSCRIPT_LAYOUT, nativeId), claudeLineMessages, CLAUDE_TRANSCRIPT_LAYOUT);
3
+ return createFileTranscript(files, () => locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId), claudeLineMessages, CLAUDE_TRANSCRIPT_LAYOUT);
11
4
  }
12
- /** projects/<cwd>/<session>.jsonl in claude's config directory. */
5
+ export function claudeSubagentTranscript(source, agentId, toolUseId) {
6
+ return subagentFile(source, `agent-${agentId}.jsonl`, toolUseId);
7
+ }
8
+ export async function claudeFindSubagent(source, toolUseId) {
9
+ const isResult = (message) => message.type === "user" && Array.isArray(message.message.content) && message.message.content.some((block) => block.type === "tool_result" && block.tool_use_id === toolUseId);
10
+ let found = await claudeTranscript(source).find(isResult);
11
+ const dir = found ? null : await subagentsDirectory(source);
12
+ const names = dir ? ((await directoryNames(source.files, dir)) ?? []) : [];
13
+ for (const name of names.filter((entry) => SUBAGENT_FILE.test(entry))) {
14
+ found = await subagentFile(source, name, toolUseId).find(isResult);
15
+ if (found)
16
+ break;
17
+ }
18
+ const agentId = found?.tool_use_result?.agentId;
19
+ return typeof agentId === "string" ? agentId : null;
20
+ }
21
+ function subagentFile(source, name, toolUseId) {
22
+ async function locate() {
23
+ const dir = await subagentsDirectory(source);
24
+ if (!dir)
25
+ return null;
26
+ return (await directoryNames(source.files, dir))?.includes(name) ? `${dir}/${name}` : null;
27
+ }
28
+ return createFileTranscript(source.files, locate, (line) => lineMessages(line, toolUseId), CLAUDE_TRANSCRIPT_LAYOUT);
29
+ }
30
+ async function subagentsDirectory({ nativeId, env, files }) {
31
+ const main = await locateTranscript(files, claudeHome(env), CLAUDE_TRANSCRIPT_LAYOUT, nativeId);
32
+ return main ? `${main.slice(0, -".jsonl".length)}/subagents` : null;
33
+ }
34
+ function claudeHome(env) {
35
+ return env.CLAUDE_CONFIG_DIR ?? `${env.HOME}/.claude`;
36
+ }
37
+ const SUBAGENT_FILE = /^agent-.+\.jsonl$/;
13
38
  export const CLAUDE_TRANSCRIPT_LAYOUT = {
14
39
  dir: "projects",
15
40
  depth: 2,
16
41
  name: "{id}.jsonl",
17
42
  time: "timestamp",
18
- // Resuming a session, claude writes what it said since its last compaction again, from that compaction's line.
19
43
  copies: '"subtype":"compact_boundary"',
20
44
  };
21
45
  export function claudeLineMessages(line) {
46
+ return lineMessages(line, null);
47
+ }
48
+ function lineMessages(line, parentToolUseId) {
22
49
  const entry = line;
50
+ if (entry.type === "system" && entry.subtype === "compact_boundary")
51
+ return compaction(entry);
23
52
  if (entry.type !== "user" && entry.type !== "assistant")
24
53
  return [];
25
- if (entry.isSidechain || entry.isMeta || !entry.uuid || !entry.message)
54
+ if ((entry.isSidechain && parentToolUseId === null) || entry.isMeta || !entry.uuid || !entry.message)
55
+ return [];
56
+ const common = { parent_tool_use_id: parentToolUseId, uuid: entry.uuid, session_id: entry.sessionId ?? "", ...(entry.timestamp ? { timestamp: entry.timestamp } : {}) };
57
+ if (entry.type === "assistant")
58
+ return [{ type: "assistant", message: entry.message, ...common }];
59
+ return [{ type: "user", message: entry.message, ...common, ...(entry.toolUseResult !== undefined ? { tool_use_result: entry.toolUseResult } : {}) }];
60
+ }
61
+ function compaction(entry) {
62
+ const meta = entry.compactMetadata;
63
+ if (!entry.uuid || !meta?.trigger)
26
64
  return [];
27
- // type-bypass: claude stores the message the stream carried; its type is the SDK's.
28
- return [{ type: entry.type, message: entry.message, parent_tool_use_id: null, uuid: entry.uuid, session_id: entry.sessionId ?? "" }];
65
+ const { preservedSegment: segment, preservedMessages: preserved } = meta;
66
+ return [
67
+ {
68
+ type: "system",
69
+ subtype: "compact_boundary",
70
+ compact_metadata: {
71
+ trigger: meta.trigger,
72
+ pre_tokens: meta.preTokens ?? 0,
73
+ ...(meta.postTokens !== undefined ? { post_tokens: meta.postTokens } : {}),
74
+ ...(meta.durationMs !== undefined ? { duration_ms: meta.durationMs } : {}),
75
+ ...(segment ? { preserved_segment: { head_uuid: segment.headUuid, anchor_uuid: segment.anchorUuid, tail_uuid: segment.tailUuid } } : {}),
76
+ ...(preserved ? { preserved_messages: { anchor_uuid: preserved.anchorUuid, uuids: preserved.uuids } } : {}),
77
+ },
78
+ uuid: entry.uuid,
79
+ session_id: entry.sessionId ?? "",
80
+ ...(entry.timestamp ? { timestamp: entry.timestamp } : {}),
81
+ },
82
+ ];
29
83
  }
@@ -1,18 +1,9 @@
1
1
  import type { AgentStreamingMessage } from "../api.ts";
2
2
  import type { WorkerMethod, WorkerRequests } from "./worker/protocol.ts";
3
3
  import type { ConnectWorker } from "./workerConnection.ts";
4
- /**
5
- * The worker's protocol (protocol.ts). Errors come back as the Agent*Error they were.
6
- *
7
- * @sealed
8
- */
4
+ /** @sealed */
9
5
  export type WorkerClient = {
10
6
  call<M extends WorkerMethod>(method: M, params: WorkerRequests[M][0]): Promise<WorkerRequests[M][1]>;
11
- /**
12
- * The session's messages after `afterId`, then each one as the worker produces it, over a
13
- * connection held open while the caller reads. Throws once the worker goes away.
14
- */
15
7
  stream(sessionName: string, afterId: string | undefined): AsyncGenerator<AgentStreamingMessage>;
16
8
  };
17
- /** Calls the worker over the connections `connect` opens (workerSocketConnector: its unix socket). */
18
9
  export declare function createWorkerClient(connect: ConnectWorker): WorkerClient;
@@ -1,6 +1,4 @@
1
- /** Calls the worker over the connections `connect` opens (workerSocketConnector: its unix socket). */
2
1
  export function createWorkerClient(connect) {
3
- /** One request on a connection of its own. */
4
2
  async function call(method, params) {
5
3
  const connection = await connect();
6
4
  try {
@@ -37,7 +35,6 @@ export function createWorkerClient(connect) {
37
35
  }
38
36
  }
39
37
  finally {
40
- // On a shared connection (sharedConnector) the worker would go on sending to it otherwise.
41
38
  if (!ended)
42
39
  connection.request("session/unsubscribe", { subscription }).catch(() => { });
43
40
  connection.close();
@@ -0,0 +1,14 @@
1
+ import type { AgentBackendInfo } from "../../api.ts";
2
+ export declare const CODEX_PACKAGE = "@openai/codex";
3
+ export declare function codexPackage(): {
4
+ pkg: string;
5
+ version: string;
6
+ executables: string;
7
+ };
8
+ export declare function installCodex(installDir: string): Promise<void>;
9
+ export declare function installedCodex(installDir: string): Promise<Omit<AgentBackendInfo, "backend">>;
10
+ export type CodexExecutable = {
11
+ path: string;
12
+ env: Record<string, string>;
13
+ };
14
+ export declare function codexExecutable(installDir: string): CodexExecutable;
@@ -0,0 +1,25 @@
1
+ import { installPackage, installedPackage, pinnedVersion } from "../install.js";
2
+ export const CODEX_PACKAGE = "@openai/codex";
3
+ const CODEX_VERSION = () => pinnedVersion("codex", CODEX_PACKAGE, "@openai/codex-sdk");
4
+ export function codexPackage() {
5
+ return { pkg: CODEX_PACKAGE, version: CODEX_VERSION(), executables: "@openai/codex-linux-*/vendor/*/bin/codex" };
6
+ }
7
+ export function installCodex(installDir) {
8
+ return installPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);
9
+ }
10
+ export function installedCodex(installDir) {
11
+ return installedPackage(installDir, CODEX_PACKAGE, CODEX_VERSION(), () => codexExecutable(installDir).path);
12
+ }
13
+ const LINUX_TARGETS = {
14
+ x64: { pkg: "@openai/codex-linux-x64", triple: "x86_64-unknown-linux-musl" },
15
+ arm64: { pkg: "@openai/codex-linux-arm64", triple: "aarch64-unknown-linux-musl" },
16
+ };
17
+ export function codexExecutable(installDir) {
18
+ const target = LINUX_TARGETS[process.arch];
19
+ if (process.platform !== "linux" || !target)
20
+ throw new Error(`codex: unsupported platform ${process.platform}/${process.arch}`);
21
+ return {
22
+ path: `${installDir}/node_modules/${target.pkg}/vendor/${target.triple}/bin/codex`,
23
+ env: { CODEX_MANAGED_PACKAGE_ROOT: `${installDir}/node_modules/${CODEX_PACKAGE}`, CODEX_MANAGED_BY_BUN: "1" },
24
+ };
25
+ }
@@ -1,44 +1,22 @@
1
1
  import { type SandboxFileOperations } from "../../sandbox/api.ts";
2
2
  import { type AgentTranscriptMessage as TranscriptMessage, type AgentTranscriptPage, type AgentTranscriptRequest } from "../api.ts";
3
- /** One page of a transcript: its messages oldest first, and the time the next (older) page is before. */
4
3
  export type TranscriptPage = {
5
4
  messages: TranscriptMessage[];
6
5
  nextBefore: string | null;
7
6
  };
8
- /**
9
- * A backend's transcript, read from its own JSONL file a chunk at a time, so a long conversation (a
10
- * file of hundreds of MB) never has to be in memory: a page holds its messages and a few chunks, and
11
- * a replay streams. Pages go by the time on each line: the file is in conversation order, its times
12
- * nearly so (a line is written when its message completes, so parallel branches land seconds out of
13
- * order), which lets a time be found by bisecting the file.
14
- */
15
- export interface FileTranscript {
16
- /**
17
- * The newest `limit` or so messages from before `before` (epoch ms; null for the newest), by their
18
- * times, with every other message of the oldest one's time, so the page before it is exactly those
19
- * before that time. `found`: what a search of the file found beforehand.
20
- */
7
+ export interface Transcript {
21
8
  page(before: number | null, limit: number, found?: TranscriptFound): Promise<TranscriptPage>;
22
- /**
23
- * Hands `emit` every message after the one whose uuid is `afterId` (all of them when undefined), in
24
- * order, as far as the file went when the replay began. False when `afterId` isn't in the file.
25
- */
26
9
  replay(afterId: string | undefined, emit: (message: TranscriptMessage) => void): Promise<boolean>;
27
10
  }
28
- /** What a transcript is read through: the worker's own files (sandboxFiles), or what the host fetched of them. */
11
+ export type FileTranscript = Transcript & {
12
+ find(match: (message: TranscriptMessage) => boolean): Promise<TranscriptMessage | null>;
13
+ };
29
14
  export type TranscriptFiles = {
30
15
  size(path: string): Promise<number>;
31
16
  readFileRange(path: string, start: number, end: number): Promise<Uint8Array>;
32
17
  listDirectory(path: string): Promise<string[]>;
33
18
  };
34
19
  export declare function sandboxFiles(sandbox: SandboxFileOperations): TranscriptFiles;
35
- /**
36
- * Where a backend keeps its conversations, under its home: in `dir`, `depth` levels down (1: in
37
- * `dir` itself), a file named `name`, where `*` is any characters and `{id}` the conversation's id.
38
- * Each line of it is a JSON object with its ISO 8601 time in the field `time`. `copies`: what's in
39
- * the line that starts a stretch the backend writes again (claude, resuming a session, writes what
40
- * it said since its last compaction again, under the same ids and times), as it's written.
41
- */
42
20
  export type TranscriptLayout = {
43
21
  dir: string;
44
22
  depth: number;
@@ -46,46 +24,25 @@ export type TranscriptLayout = {
46
24
  time: string;
47
25
  copies?: string;
48
26
  };
49
- /** A stretch of the file a reader passes over, from one line's start to another's. */
50
27
  export type Skip = {
51
28
  start: number;
52
29
  end: number;
53
30
  };
54
- /** What a search of the file found beforehand (the host's, in the same round trip as the record). */
55
31
  export type TranscriptFound = {
56
- /** Where the page starts: FileTranscript.page's `before`, found. */
57
32
  end: number;
58
- /** The time of the first timed line from `end` on, if any. */
59
33
  after: number | null;
60
- /** The stretches written again. */
61
34
  copies: Skip[];
62
35
  };
63
- /** The file of conversation `nativeId` laid out as `layout` under `home`; null when there's none yet. */
64
36
  export declare function locateTranscript(files: TranscriptFiles, home: string, layout: TranscriptLayout, nativeId: string): Promise<string | null>;
65
- /** The messages one line of the file stands for, oldest first (none for bookkeeping lines). */
66
37
  export type LineMessages = (entry: unknown) => TranscriptMessage[];
67
- /** How much of the file is read at a time. */
68
38
  export declare const CHUNK_BYTES: number;
69
- /**
70
- * How far out of time order lines get, nearly always (parallel branches completing; in long claude
71
- * sessions half are within 2s, 99% within a minute): searches allow for this much.
72
- */
73
39
  export declare const TIME_SLACK_MS = 60000;
74
- /** How many timed lines a probe of the bisection looks at: the earliest wins, so one odd line can't mislead it. */
75
40
  export declare const PROBE_LINES = 3;
76
- /** How much a probe reads, at most, before it gives up on finding a timed line (huge tool outputs). */
77
41
  export declare const PROBE_BYTES: number;
78
- /** `locate` finds the file; null when there's none yet (nothing stored). */
79
42
  export declare function createFileTranscript(files: TranscriptFiles, locate: () => Promise<string | null>, lineMessages: LineMessages, layout: Pick<TranscriptLayout, "time" | "copies">): FileTranscript;
80
- /** A page of the API's transcript (AgentTranscript.getTranscript); none stored for a session that never ran. */
81
- export declare function transcriptPage(transcript: FileTranscript | null, { limit, before, cursor }?: AgentTranscriptRequest, found?: TranscriptFound): Promise<AgentTranscriptPage>;
82
- /** AgentTranscriptRequest.before as epoch ms; null for none. */
43
+ export declare function transcriptPage(transcript: Transcript | null, { limit, before, cursor }?: AgentTranscriptRequest, found?: TranscriptFound): Promise<AgentTranscriptPage>;
83
44
  export declare function transcriptTime(before: string | undefined): number | null;
84
- /**
85
- * `files` with what was already fetched of the file at `path` in the same round trip: its size, and
86
- * pieces of it (what a page reads, mostly). Reading back beyond them (a page across huge tool
87
- * outputs) fetches twice as much as the time before, so it takes a few more reads, not one per chunk.
88
- */
45
+ export declare function directoryNames(files: TranscriptFiles, dir: string): Promise<string[] | null>;
89
46
  export declare function prefetchedFiles(files: TranscriptFiles, path: string, size: number, pieces: {
90
47
  start: number;
91
48
  bytes: Uint8Array;
@@ -7,40 +7,25 @@ export function sandboxFiles(sandbox) {
7
7
  listDirectory: (path) => sandbox.listDirectory(path),
8
8
  };
9
9
  }
10
- /** The file of conversation `nativeId` laid out as `layout` under `home`; null when there's none yet. */
11
10
  export function locateTranscript(files, home, layout, nativeId) {
12
11
  const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
13
12
  const [before = "", after = ""] = layout.name.split("{id}").map((part) => part.split("*").map(escape).join(".*"));
14
13
  const name = new RegExp(`^${before}${escape(nativeId)}${after}$`);
15
14
  return findFile(files, `${home}/${layout.dir}`, layout.depth, (entry) => name.test(entry));
16
15
  }
17
- /** How much of the file is read at a time. */
18
16
  export const CHUNK_BYTES = 64 * 1024;
19
- /**
20
- * How far out of time order lines get, nearly always (parallel branches completing; in long claude
21
- * sessions half are within 2s, 99% within a minute): searches allow for this much.
22
- */
23
17
  export const TIME_SLACK_MS = 60_000;
24
- /** How many timed lines a probe of the bisection looks at: the earliest wins, so one odd line can't mislead it. */
25
18
  export const PROBE_LINES = 3;
26
- /** How much a probe reads, at most, before it gives up on finding a timed line (huge tool outputs). */
27
19
  export const PROBE_BYTES = 4 * CHUNK_BYTES;
28
20
  const NEWLINE = 0x0a;
29
- /** A line's time (epoch ms) from its `field`, null without a valid one. */
30
21
  function lineTime(entry, field) {
31
22
  const value = entry?.[field];
32
23
  const time = typeof value === "string" ? Date.parse(value) : NaN;
33
24
  return Number.isNaN(time) ? null : time;
34
25
  }
35
- /** How much is read at a time when searching the whole file for where stretches were written again. */
36
26
  const SCAN_BYTES = 1024 * 1024;
37
- /**
38
- * The stretches written again in the files searched last, by path and size: a search reads the whole
39
- * file, and a file the backend only appends to that hasn't grown has the same ones.
40
- */
41
27
  const searchedCopies = new Map();
42
28
  const SEARCHED_FILES = 16;
43
- /** `locate` finds the file; null when there's none yet (nothing stored). */
44
29
  export function createFileTranscript(files, locate, lineMessages, layout) {
45
30
  function parse(bytes, start) {
46
31
  try {
@@ -48,11 +33,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
48
33
  return { start, time: lineTime(entry, layout.time), messages: lineMessages(entry) };
49
34
  }
50
35
  catch {
51
- // A line still being written (the last one), or one this reader doesn't know: nothing in it.
52
36
  return { start, time: null, messages: [] };
53
37
  }
54
38
  }
55
- /** The stretches between `from` and `to` that aren't skipped, in order. */
56
39
  function kept(from, to, skip) {
57
40
  const parts = [];
58
41
  let start = from;
@@ -67,14 +50,12 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
67
50
  parts.push({ start, end: to });
68
51
  return parts;
69
52
  }
70
- /** The file's lines before byte `end`, last first, read a chunk at a time, passing over `skip`. */
71
53
  async function* backward(path, end, skip = []) {
72
54
  for (const part of kept(0, end, skip).reverse())
73
55
  yield* backwardIn(path, part.start, part.end);
74
56
  }
75
57
  async function* backwardIn(path, floor, end) {
76
58
  let position = end;
77
- // Bytes of a line that runs on into what was read before (later in the file).
78
59
  let carry = new Uint8Array(0);
79
60
  while (position > floor) {
80
61
  const from = Math.max(floor, position - CHUNK_BYTES);
@@ -94,7 +75,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
94
75
  if (carry.length > 0)
95
76
  yield parse(carry, floor);
96
77
  }
97
- /** The file's lines from byte `start` to byte `end`, in order, read a chunk at a time, passing over `skip`. */
98
78
  async function* forward(path, start, end, skip = []) {
99
79
  for (const part of kept(start, end, skip))
100
80
  yield* forwardIn(path, part.start, part.end);
@@ -121,15 +101,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
121
101
  if (carry.length > 0)
122
102
  yield parse(carry, carryStart);
123
103
  }
124
- /**
125
- * Where the lines from time `target` on start, about, between `low` and `high`: the start of a line
126
- * (or `high`) with only lines from before it, allowing for the slack, ahead of it. Bisects, each
127
- * probe reading the times of the first few lines after its middle, passing over `skip`.
128
- */
129
104
  async function seek(path, low, high, target, skip) {
130
105
  return (await narrow(path, low, high, target, skip)).high;
131
106
  }
132
- /** seek's bisection: where it got, from both sides. */
133
107
  async function narrow(path, low, high, target, skip) {
134
108
  while (high - low > CHUNK_BYTES) {
135
109
  let middle = Math.floor((low + high) / 2);
@@ -143,7 +117,7 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
143
117
  }
144
118
  const probe = await probeTime(path, middle, high, skip);
145
119
  if (!probe)
146
- break; // one line spans it all: start from where the search got
120
+ break;
147
121
  if (probe.time >= target)
148
122
  high = probe.start;
149
123
  else
@@ -151,14 +125,13 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
151
125
  }
152
126
  return { low, high };
153
127
  }
154
- /** The first line starting after `from` (before `limit`), with the earliest time of the next few timed lines. */
155
128
  async function probeTime(path, from, limit, skip) {
156
129
  let start = -1;
157
130
  let time = Infinity;
158
131
  let timed = 0;
159
132
  for await (const line of forward(path, from, Math.min(limit, from + PROBE_BYTES), skip)) {
160
133
  if (line.start === from && from > 0)
161
- continue; // the middle of a line: only its end was read
134
+ continue;
162
135
  if (line.start >= limit)
163
136
  break;
164
137
  if (start < 0)
@@ -171,25 +144,17 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
171
144
  }
172
145
  return start < 0 || timed === 0 ? null : { start, time };
173
146
  }
174
- /** The time of the first timed line from `from` on (a line start), if there's one in reach. */
175
147
  async function timeAt(path, from, size, skip) {
176
148
  for await (const line of forward(path, from, Math.min(size, from + PROBE_BYTES), skip))
177
149
  if (line.time !== null)
178
150
  return line.time;
179
151
  return null;
180
152
  }
181
- /** Where the line holding byte `at` starts. */
182
153
  async function lineStart(path, at) {
183
154
  for await (const line of backward(path, at + 1))
184
155
  return line.start;
185
156
  return 0;
186
157
  }
187
- /**
188
- * The stretches the backend wrote again (TranscriptLayout.copies): each starts at a line with the
189
- * marker whose time goes back from the timed line before it, and runs until a line is newer than
190
- * that one. Found by searching the whole file for the marker; the rest only
191
- * reads around what's found.
192
- */
193
158
  function copiesOf(path, size) {
194
159
  if (!layout.copies)
195
160
  return Promise.resolve([]);
@@ -199,7 +164,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
199
164
  copies = findCopies(path, size, layout.copies);
200
165
  copies.catch(() => searchedCopies.delete(key));
201
166
  searchedCopies.set(key, copies);
202
- // A few files' worth: a worker serves an agent's sessions.
203
167
  if (searchedCopies.size > SEARCHED_FILES)
204
168
  searchedCopies.delete(searchedCopies.keys().next().value);
205
169
  }
@@ -209,7 +173,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
209
173
  const needle = Buffer.from(marker);
210
174
  const found = [];
211
175
  for (let position = 0; position < size; position += SCAN_BYTES) {
212
- // Overlapping by the marker's length, so one across two reads is found.
213
176
  const from = Math.max(0, position - needle.length + 1);
214
177
  const bytes = Buffer.from(await files.readFileRange(path, from, Math.min(size, position + SCAN_BYTES)));
215
178
  for (let index = bytes.indexOf(needle); index >= 0; index = bytes.indexOf(needle, index + 1)) {
@@ -219,7 +182,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
219
182
  }
220
183
  const copies = [];
221
184
  for (const [index, at] of found.entries()) {
222
- // The marker's line, and the timed line before it.
223
185
  const start = await lineStart(path, at);
224
186
  let earlier = null;
225
187
  for await (const before of backward(path, start)) {
@@ -235,13 +197,8 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
235
197
  time = line.time;
236
198
  break;
237
199
  }
238
- // A compaction as it happened comes after what's before it (compacting takes a while: 25s and
239
- // more); a copy starts back at its time.
240
200
  if (time === null || time >= earlier)
241
201
  continue;
242
- // It runs up to what was written before it, and ends with the first line newer than that. It
243
- // holds what was said since a compaction, so it ends by the next marker: the rest of the file
244
- // (other copies) can't mislead the search.
245
202
  const next = found[index + 1];
246
203
  const near = await narrow(path, start + 1, next === undefined ? size : await lineStart(path, next), earlier + 1, []);
247
204
  let end = near.high;
@@ -255,17 +212,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
255
212
  }
256
213
  return copies;
257
214
  }
258
- /**
259
- * The file's lines before `end`, last first, each with the time it's paged by: its own, unless it's
260
- * odd, more than the slack away from both the timed lines around it in the same direction (a
261
- * background task's result written minutes after it began, a note dated ahead), when it's the one
262
- * before it's; a line without a time goes with the timed line after it. These times follow the file
263
- * order to within the slack, so the late result is paged where it is in the conversation.
264
- * `after`: the time of the first timed line from `end` on.
265
- */
266
215
  async function* pagedBackward(path, end, after, skip) {
267
- let next = after; // the time of the timed line after the one waiting
268
- let waiting = []; // a timed line and the untimed ones before it, until the one before it is read
216
+ let next = after;
217
+ let waiting = [];
269
218
  const settle = function* (before) {
270
219
  const [timed, ...untimed] = waiting;
271
220
  if (!timed)
@@ -282,7 +231,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
282
231
  };
283
232
  for await (const line of backward(path, end, skip)) {
284
233
  if (line.time === null) {
285
- // Before any timed line: it goes with the line after the end, or is the newest.
286
234
  if (waiting.length === 0)
287
235
  yield { ...line, time: next ?? Infinity };
288
236
  else
@@ -302,11 +250,9 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
302
250
  const copies = found?.copies ?? (await copiesOf(path, size));
303
251
  const from = Math.min(found?.end ?? (before === null ? size : await seek(path, 0, size, before + TIME_SLACK_MS, copies)), size);
304
252
  const after = found ? found.after : from < size ? await timeAt(path, from, size, copies) : null;
305
- // Read back until the page is full and a line is older than its oldest by more than the slack:
306
- // nothing before it can belong on the page.
307
253
  const taken = [];
308
254
  let count = 0;
309
- let oldest = Infinity; // the time of the page's limit-th newest message, once it's full
255
+ let oldest = Infinity;
310
256
  let older = false;
311
257
  for await (const line of pagedBackward(path, from, after, copies)) {
312
258
  if (count >= limit && line.time < oldest - TIME_SLACK_MS) {
@@ -320,8 +266,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
320
266
  if (count >= limit)
321
267
  oldest = oldestOnPage(taken, limit);
322
268
  }
323
- // The page: its newest messages by time, ties with the oldest of them included. A line's messages
324
- // stay together, so a page can hold a message or so more than the limit.
325
269
  const kept = count >= limit ? taken.filter((line) => line.time >= oldest) : taken;
326
270
  if (kept.length < taken.length)
327
271
  older = true;
@@ -330,7 +274,6 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
330
274
  nextBefore: older && Number.isFinite(oldest) ? new Date(oldest).toISOString() : null,
331
275
  };
332
276
  }
333
- // Passing over the copies too: a resumed session's stretch written again isn't replayed twice.
334
277
  async function replay(afterId, emit) {
335
278
  const path = await locate();
336
279
  if (!path)
@@ -361,9 +304,20 @@ export function createFileTranscript(files, locate, lineMessages, layout) {
361
304
  }
362
305
  return true;
363
306
  }
364
- return { page, replay };
307
+ async function find(match) {
308
+ const path = await locate();
309
+ if (!path)
310
+ return null;
311
+ const end = await files.size(path);
312
+ for await (const line of backward(path, end, await copiesOf(path, end))) {
313
+ const found = line.messages.find(match);
314
+ if (found)
315
+ return found;
316
+ }
317
+ return null;
318
+ }
319
+ return { page, replay, find };
365
320
  }
366
- /** The time of the `limit`-th newest message of `lines`. */
367
321
  function oldestOnPage(lines, limit) {
368
322
  const newestFirst = [...lines].sort((a, b) => b.time - a.time);
369
323
  let count = 0;
@@ -386,7 +340,6 @@ function joined(first, second) {
386
340
  }
387
341
  const DEFAULT_PAGE_SIZE = 50;
388
342
  const MAX_PAGE_SIZE = 500;
389
- /** A page of the API's transcript (AgentTranscript.getTranscript); none stored for a session that never ran. */
390
343
  export async function transcriptPage(transcript, { limit, before, cursor } = {}, found) {
391
344
  const time = transcriptTime(before ?? cursor);
392
345
  if (!transcript)
@@ -395,7 +348,6 @@ export async function transcriptPage(transcript, { limit, before, cursor } = {},
395
348
  const page = await transcript.page(time, size, found);
396
349
  return { ...page, nextCursor: page.nextBefore };
397
350
  }
398
- /** AgentTranscriptRequest.before as epoch ms; null for none. */
399
351
  export function transcriptTime(before) {
400
352
  if (before === undefined)
401
353
  return null;
@@ -404,35 +356,27 @@ export function transcriptTime(before) {
404
356
  throw new AgentInvalidError(`invalid time ${before}: give an ISO 8601 time`);
405
357
  return time;
406
358
  }
407
- /**
408
- * The first file `depth` levels under `dir` whose name `matches` (the backends' own layouts, like
409
- * projects/<project>/<id>.jsonl), each level listed at once; null when there's none, or no `dir`.
410
- */
411
- async function findFile(files, dir, depth, matches) {
412
- let names;
359
+ export async function directoryNames(files, dir) {
413
360
  try {
414
- names = await files.listDirectory(dir);
361
+ return await files.listDirectory(dir);
415
362
  }
416
363
  catch (error) {
417
364
  if (error instanceof SandboxNotFoundError)
418
365
  return null;
419
366
  throw error;
420
367
  }
368
+ }
369
+ async function findFile(files, dir, depth, matches) {
370
+ const names = await directoryNames(files, dir);
371
+ if (!names)
372
+ return null;
421
373
  if (depth <= 1) {
422
374
  const name = names.find(matches);
423
375
  return name === undefined ? null : `${dir}/${name}`;
424
376
  }
425
- // Newest first, where names sort by date (codex's YYYY/MM/DD): a recent conversation is the likely one.
426
- const found = await Promise.all(names.sort().reverse().map((name) =>
427
- // One that isn't a directory has nothing below it.
428
- findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));
377
+ const found = await Promise.all(names.sort().reverse().map((name) => findFile(files, `${dir}/${name}`, depth - 1, matches).catch(() => null)));
429
378
  return found.find((path) => path !== null) ?? null;
430
379
  }
431
- /**
432
- * `files` with what was already fetched of the file at `path` in the same round trip: its size, and
433
- * pieces of it (what a page reads, mostly). Reading back beyond them (a page across huge tool
434
- * outputs) fetches twice as much as the time before, so it takes a few more reads, not one per chunk.
435
- */
436
380
  export function prefetchedFiles(files, path, size, pieces) {
437
381
  let more = Math.max(CHUNK_BYTES, ...pieces.map(({ bytes }) => bytes.length));
438
382
  return {
@@ -0,0 +1,5 @@
1
+ import manifest from "../vendor-types/manifest.json";
2
+ import type { AgentBackendInfo } from "../api.ts";
3
+ export declare function pinnedVersion(vendor: keyof typeof manifest, pkg: string, sourcePackage?: string): string;
4
+ export declare function installedPackage(installDir: string, pkg: string, version: string, executable: () => string): Promise<Omit<AgentBackendInfo, "backend">>;
5
+ export declare function installPackage(installDir: string, pkg: string, version: string, executable: () => string): Promise<void>;