@getpaseo/server 0.1.69 → 0.1.70

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 (283) hide show
  1. package/dist/server/client/daemon-client-runtime-metrics.js.map +1 -1
  2. package/dist/server/client/daemon-client-websocket-transport.js.map +1 -1
  3. package/dist/server/client/daemon-client.d.ts +33 -0
  4. package/dist/server/client/daemon-client.d.ts.map +1 -1
  5. package/dist/server/client/daemon-client.js +37 -1
  6. package/dist/server/client/daemon-client.js.map +1 -1
  7. package/dist/server/server/agent/agent-manager.d.ts +8 -0
  8. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  9. package/dist/server/server/agent/agent-manager.js +54 -5
  10. package/dist/server/server/agent/agent-manager.js.map +1 -1
  11. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  12. package/dist/server/server/agent/agent-sdk-types.d.ts +15 -2
  13. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  14. package/dist/server/server/agent/agent-stream-coalescer.d.ts +1 -1
  15. package/dist/server/server/agent/agent-stream-coalescer.d.ts.map +1 -1
  16. package/dist/server/server/agent/agent-timeline-store.js +1 -1
  17. package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
  18. package/dist/server/server/agent/create-agent-mode.d.ts +14 -0
  19. package/dist/server/server/agent/create-agent-mode.d.ts.map +1 -0
  20. package/dist/server/server/agent/create-agent-mode.js +23 -0
  21. package/dist/server/server/agent/create-agent-mode.js.map +1 -0
  22. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  23. package/dist/server/server/agent/mcp-server.js +36 -12
  24. package/dist/server/server/agent/mcp-server.js.map +1 -1
  25. package/dist/server/server/agent/mcp-shared.d.ts +6 -2
  26. package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
  27. package/dist/server/server/agent/mcp-shared.js +12 -5
  28. package/dist/server/server/agent/mcp-shared.js.map +1 -1
  29. package/dist/server/server/agent/pcm16-resampler.js.map +1 -1
  30. package/dist/server/server/agent/prompt-attachments.d.ts.map +1 -1
  31. package/dist/server/server/agent/prompt-attachments.js +2 -0
  32. package/dist/server/server/agent/prompt-attachments.js.map +1 -1
  33. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  34. package/dist/server/server/agent/provider-manifest.d.ts +4 -1
  35. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  36. package/dist/server/server/agent/provider-manifest.js +11 -0
  37. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  38. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  39. package/dist/server/server/agent/provider-registry.js +8 -3
  40. package/dist/server/server/agent/provider-registry.js.map +1 -1
  41. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  42. package/dist/server/server/agent/providers/acp-agent.d.ts +2 -1
  43. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  44. package/dist/server/server/agent/providers/acp-agent.js +35 -17
  45. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  46. package/dist/server/server/agent/providers/{claude-agent.d.ts → claude/agent.d.ts} +9 -6
  47. package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -0
  48. package/dist/server/server/agent/providers/{claude-agent.js → claude/agent.js} +170 -152
  49. package/dist/server/server/agent/providers/claude/agent.js.map +1 -0
  50. package/dist/server/server/agent/providers/claude/{claude-models.d.ts → models.d.ts} +1 -1
  51. package/dist/server/server/agent/providers/claude/models.d.ts.map +1 -0
  52. package/dist/server/server/agent/providers/claude/{claude-models.js → models.js} +8 -1
  53. package/dist/server/server/agent/providers/claude/models.js.map +1 -0
  54. package/dist/server/server/agent/providers/claude/query.d.ts +14 -0
  55. package/dist/server/server/agent/providers/claude/query.d.ts.map +1 -0
  56. package/dist/server/server/agent/providers/claude/query.js +84 -0
  57. package/dist/server/server/agent/providers/claude/query.js.map +1 -0
  58. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  59. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +11 -2
  60. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  61. package/dist/server/server/agent/providers/codex-app-server-agent.js +408 -91
  62. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  63. package/dist/server/server/agent/providers/diagnostic-utils.d.ts.map +1 -1
  64. package/dist/server/server/agent/providers/diagnostic-utils.js +4 -0
  65. package/dist/server/server/agent/providers/diagnostic-utils.js.map +1 -1
  66. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +1 -1
  67. package/dist/server/server/agent/providers/generic-acp-agent.d.ts.map +1 -1
  68. package/dist/server/server/agent/providers/generic-acp-agent.js +0 -3
  69. package/dist/server/server/agent/providers/generic-acp-agent.js.map +1 -1
  70. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
  71. package/dist/server/server/agent/providers/mock-load-test-agent.js +6 -2
  72. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
  73. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  74. package/dist/server/server/agent/providers/opencode-agent.js +32 -27
  75. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  76. package/dist/server/server/agent/providers/provider-image-output.d.ts +20 -0
  77. package/dist/server/server/agent/providers/provider-image-output.d.ts.map +1 -0
  78. package/dist/server/server/agent/providers/provider-image-output.js +51 -0
  79. package/dist/server/server/agent/providers/provider-image-output.js.map +1 -0
  80. package/dist/server/server/agent/providers/provider-runner.d.ts +3 -3
  81. package/dist/server/server/agent/providers/provider-runner.d.ts.map +1 -1
  82. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -1
  83. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +7 -7
  84. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  85. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +12 -18
  86. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  87. package/dist/server/server/agent/stt-manager.d.ts +1 -0
  88. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  89. package/dist/server/server/agent/stt-manager.js +3 -0
  90. package/dist/server/server/agent/stt-manager.js.map +1 -1
  91. package/dist/server/server/agent/tool-name-normalization.js.map +1 -1
  92. package/dist/server/server/agent/tts-manager.js.map +1 -1
  93. package/dist/server/server/bootstrap.d.ts +1 -0
  94. package/dist/server/server/bootstrap.d.ts.map +1 -1
  95. package/dist/server/server/bootstrap.js +3 -1
  96. package/dist/server/server/bootstrap.js.map +1 -1
  97. package/dist/server/server/config.d.ts +1 -0
  98. package/dist/server/server/config.d.ts.map +1 -1
  99. package/dist/server/server/config.js +7 -1
  100. package/dist/server/server/config.js.map +1 -1
  101. package/dist/server/server/connection-offer.d.ts +1 -0
  102. package/dist/server/server/connection-offer.d.ts.map +1 -1
  103. package/dist/server/server/daemon-config-store.d.ts.map +1 -1
  104. package/dist/server/server/daemon-config-store.js +2 -6
  105. package/dist/server/server/daemon-config-store.js.map +1 -1
  106. package/dist/server/server/daemon-keypair.js.map +1 -1
  107. package/dist/server/server/daemon-worker.js +3 -0
  108. package/dist/server/server/daemon-worker.js.map +1 -1
  109. package/dist/server/server/editor-targets.js.map +1 -1
  110. package/dist/server/server/json-utils.js.map +1 -1
  111. package/dist/server/server/logger.js.map +1 -1
  112. package/dist/server/server/loop/rpc-schemas.d.ts +68 -0
  113. package/dist/server/server/loop/rpc-schemas.d.ts.map +1 -1
  114. package/dist/server/server/loop/rpc-schemas.js +4 -0
  115. package/dist/server/server/loop/rpc-schemas.js.map +1 -1
  116. package/dist/server/server/loop-service.d.ts +8 -0
  117. package/dist/server/server/loop-service.d.ts.map +1 -1
  118. package/dist/server/server/loop-service.js +11 -2
  119. package/dist/server/server/loop-service.js.map +1 -1
  120. package/dist/server/server/package-version.d.ts +12 -0
  121. package/dist/server/server/package-version.d.ts.map +1 -1
  122. package/dist/server/server/package-version.js +13 -1
  123. package/dist/server/server/package-version.js.map +1 -1
  124. package/dist/server/server/pairing-offer.d.ts +1 -0
  125. package/dist/server/server/pairing-offer.d.ts.map +1 -1
  126. package/dist/server/server/pairing-offer.js +2 -1
  127. package/dist/server/server/pairing-offer.js.map +1 -1
  128. package/dist/server/server/pairing-qr.js +1 -1
  129. package/dist/server/server/pairing-qr.js.map +1 -1
  130. package/dist/server/server/paseo-env.d.ts +7 -3
  131. package/dist/server/server/paseo-env.d.ts.map +1 -1
  132. package/dist/server/server/paseo-env.js +16 -33
  133. package/dist/server/server/paseo-env.js.map +1 -1
  134. package/dist/server/server/persisted-config.d.ts +9 -0
  135. package/dist/server/server/persisted-config.d.ts.map +1 -1
  136. package/dist/server/server/persisted-config.js +1 -0
  137. package/dist/server/server/persisted-config.js.map +1 -1
  138. package/dist/server/server/persistence-hooks.js.map +1 -1
  139. package/dist/server/server/pid-lock.d.ts +21 -4
  140. package/dist/server/server/pid-lock.d.ts.map +1 -1
  141. package/dist/server/server/pid-lock.js +30 -8
  142. package/dist/server/server/pid-lock.js.map +1 -1
  143. package/dist/server/server/relay-transport.d.ts +2 -1
  144. package/dist/server/server/relay-transport.d.ts.map +1 -1
  145. package/dist/server/server/relay-transport.js +8 -5
  146. package/dist/server/server/relay-transport.js.map +1 -1
  147. package/dist/server/server/schedule/rpc-schemas.d.ts +1136 -0
  148. package/dist/server/server/schedule/rpc-schemas.d.ts.map +1 -1
  149. package/dist/server/server/schedule/rpc-schemas.js +39 -0
  150. package/dist/server/server/schedule/rpc-schemas.js.map +1 -1
  151. package/dist/server/server/schedule/service.d.ts +3 -1
  152. package/dist/server/server/schedule/service.d.ts.map +1 -1
  153. package/dist/server/server/schedule/service.js +92 -5
  154. package/dist/server/server/schedule/service.js.map +1 -1
  155. package/dist/server/server/schedule/types.d.ts +16 -0
  156. package/dist/server/server/schedule/types.d.ts.map +1 -1
  157. package/dist/server/server/script-health-monitor.js.map +1 -1
  158. package/dist/server/server/session.d.ts +3 -4
  159. package/dist/server/server/session.d.ts.map +1 -1
  160. package/dist/server/server/session.js +160 -120
  161. package/dist/server/server/session.js.map +1 -1
  162. package/dist/server/server/speech/audio.js.map +1 -1
  163. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.d.ts.map +1 -1
  164. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js +52 -52
  165. package/dist/server/server/speech/providers/local/pocket/pocket-tts-onnx.js.map +1 -1
  166. package/dist/server/server/speech/providers/local/sherpa/model-catalog.d.ts.map +1 -1
  167. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js +9 -3
  168. package/dist/server/server/speech/providers/local/sherpa/model-catalog.js.map +1 -1
  169. package/dist/server/server/speech/providers/local/sherpa/model-downloader.d.ts.map +1 -1
  170. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js +12 -10
  171. package/dist/server/server/speech/providers/local/sherpa/model-downloader.js.map +1 -1
  172. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js +2 -2
  173. package/dist/server/server/speech/providers/local/sherpa/sherpa-realtime-session.js.map +1 -1
  174. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js +1 -1
  175. package/dist/server/server/speech/providers/local/sherpa/sherpa-stt.js.map +1 -1
  176. package/dist/server/server/speech/providers/local/sherpa/sherpa-tts.js.map +1 -1
  177. package/dist/server/server/voice/voice-turn-controller.d.ts +16 -13
  178. package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
  179. package/dist/server/server/voice/voice-turn-controller.js +303 -71
  180. package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
  181. package/dist/server/server/voice-config.js +1 -1
  182. package/dist/server/server/voice-config.js.map +1 -1
  183. package/dist/server/server/websocket-server.js.map +1 -1
  184. package/dist/server/server/workspace-directory.d.ts.map +1 -1
  185. package/dist/server/server/workspace-directory.js +4 -2
  186. package/dist/server/server/workspace-directory.js.map +1 -1
  187. package/dist/server/server/workspace-git-metadata.d.ts.map +1 -1
  188. package/dist/server/server/workspace-git-metadata.js +12 -5
  189. package/dist/server/server/workspace-git-metadata.js.map +1 -1
  190. package/dist/server/server/workspace-git-service.d.ts +2 -0
  191. package/dist/server/server/workspace-git-service.d.ts.map +1 -1
  192. package/dist/server/server/workspace-git-service.js +49 -1
  193. package/dist/server/server/workspace-git-service.js.map +1 -1
  194. package/dist/server/server/workspace-registry-model.d.ts.map +1 -1
  195. package/dist/server/server/workspace-registry-model.js +10 -3
  196. package/dist/server/server/workspace-registry-model.js.map +1 -1
  197. package/dist/server/server/worktree-session.js +1 -1
  198. package/dist/server/server/worktree-session.js.map +1 -1
  199. package/dist/server/services/github-service.d.ts.map +1 -1
  200. package/dist/server/services/github-service.js +9 -2
  201. package/dist/server/services/github-service.js.map +1 -1
  202. package/dist/server/shared/connection-offer.d.ts +10 -0
  203. package/dist/server/shared/connection-offer.d.ts.map +1 -1
  204. package/dist/server/shared/connection-offer.js +1 -0
  205. package/dist/server/shared/connection-offer.js.map +1 -1
  206. package/dist/server/shared/daemon-endpoints.d.ts +4 -0
  207. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  208. package/dist/server/shared/daemon-endpoints.js +6 -1
  209. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  210. package/dist/server/shared/error-utils.d.ts +11 -0
  211. package/dist/server/shared/error-utils.d.ts.map +1 -0
  212. package/dist/server/shared/error-utils.js +27 -0
  213. package/dist/server/shared/error-utils.js.map +1 -0
  214. package/dist/server/shared/messages.d.ts +6475 -2265
  215. package/dist/server/shared/messages.d.ts.map +1 -1
  216. package/dist/server/shared/messages.js +5 -1
  217. package/dist/server/shared/messages.js.map +1 -1
  218. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  219. package/dist/server/shared/tool-call-display.js +2 -0
  220. package/dist/server/shared/tool-call-display.js.map +1 -1
  221. package/dist/server/terminal/terminal-manager.d.ts +2 -1
  222. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  223. package/dist/server/terminal/terminal-manager.js +2 -1
  224. package/dist/server/terminal/terminal-manager.js.map +1 -1
  225. package/dist/server/terminal/terminal-output-coalescer.js.map +1 -1
  226. package/dist/server/terminal/terminal-session-controller.d.ts.map +1 -1
  227. package/dist/server/terminal/terminal-session-controller.js +2 -0
  228. package/dist/server/terminal/terminal-session-controller.js.map +1 -1
  229. package/dist/server/terminal/terminal.d.ts +1 -1
  230. package/dist/server/terminal/terminal.d.ts.map +1 -1
  231. package/dist/server/terminal/terminal.js +53 -8
  232. package/dist/server/terminal/terminal.js.map +1 -1
  233. package/dist/server/terminal/worker-terminal-manager.js.map +1 -1
  234. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  235. package/dist/server/utils/checkout-git.js +67 -11
  236. package/dist/server/utils/checkout-git.js.map +1 -1
  237. package/dist/server/utils/directory-suggestions.js.map +1 -1
  238. package/dist/server/utils/executable.d.ts +2 -1
  239. package/dist/server/utils/executable.d.ts.map +1 -1
  240. package/dist/server/utils/executable.js +50 -51
  241. package/dist/server/utils/executable.js.map +1 -1
  242. package/dist/server/utils/paseo-config-file.d.ts +1 -1
  243. package/dist/server/utils/paseo-config-file.d.ts.map +1 -1
  244. package/dist/server/utils/spawn.d.ts +2 -0
  245. package/dist/server/utils/spawn.d.ts.map +1 -1
  246. package/dist/server/utils/spawn.js +2 -1
  247. package/dist/server/utils/spawn.js.map +1 -1
  248. package/dist/server/utils/tree-kill.d.ts +18 -0
  249. package/dist/server/utils/tree-kill.d.ts.map +1 -0
  250. package/dist/server/utils/{process-tree.js → tree-kill.js} +14 -33
  251. package/dist/server/utils/tree-kill.js.map +1 -0
  252. package/dist/server/utils/worktree.js.map +1 -1
  253. package/dist/src/server/agent/provider-launch-config.js.map +1 -1
  254. package/dist/src/server/agent/provider-manifest.js +11 -0
  255. package/dist/src/server/agent/provider-manifest.js.map +1 -1
  256. package/dist/src/server/loop/rpc-schemas.js +4 -0
  257. package/dist/src/server/loop/rpc-schemas.js.map +1 -1
  258. package/dist/src/server/paseo-env.js +16 -33
  259. package/dist/src/server/paseo-env.js.map +1 -1
  260. package/dist/src/server/persisted-config.js +1 -0
  261. package/dist/src/server/persisted-config.js.map +1 -1
  262. package/dist/src/server/pid-lock.js +30 -8
  263. package/dist/src/server/pid-lock.js.map +1 -1
  264. package/dist/src/server/schedule/rpc-schemas.js +39 -0
  265. package/dist/src/server/schedule/rpc-schemas.js.map +1 -1
  266. package/dist/src/shared/messages.js +5 -1
  267. package/dist/src/shared/messages.js.map +1 -1
  268. package/dist/src/utils/executable.js +50 -51
  269. package/dist/src/utils/executable.js.map +1 -1
  270. package/dist/src/utils/spawn.js +2 -1
  271. package/dist/src/utils/spawn.js.map +1 -1
  272. package/package.json +5 -4
  273. package/dist/server/server/agent/providers/claude/claude-models.d.ts.map +0 -1
  274. package/dist/server/server/agent/providers/claude/claude-models.js.map +0 -1
  275. package/dist/server/server/agent/providers/claude-agent.d.ts.map +0 -1
  276. package/dist/server/server/agent/providers/claude-agent.js.map +0 -1
  277. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts +0 -16
  278. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.d.ts.map +0 -1
  279. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js +0 -35
  280. package/dist/server/server/voice/fixed-duration-pcm-ring-buffer.js.map +0 -1
  281. package/dist/server/utils/process-tree.d.ts +0 -25
  282. package/dist/server/utils/process-tree.d.ts.map +0 -1
  283. package/dist/server/utils/process-tree.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { homedir } from "node:os";
2
2
  import { randomUUID } from "node:crypto";
3
+ import * as fsSync from "node:fs";
3
4
  import fs from "node:fs/promises";
4
5
  import os from "node:os";
5
6
  import path from "node:path";
@@ -10,12 +11,21 @@ import { curateAgentActivity } from "../activity-curator.js";
10
11
  import { mapCodexRolloutToolCall, mapCodexToolCallFromThreadItem, } from "./codex/tool-call-mapper.js";
11
12
  import { createProviderEnv, createProviderEnvSpec, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
12
13
  import { findExecutable, isCommandAvailable } from "../../../utils/executable.js";
13
- import { terminateProcessTree } from "../../../utils/process-tree.js";
14
+ import { terminateWithTreeKill } from "../../../utils/tree-kill.js";
14
15
  import { spawnProcess } from "../../../utils/spawn.js";
15
16
  import { extractCodexTerminalSessionId, nonEmptyString } from "./tool-call-mapper-utils.js";
16
17
  import { buildCodexFeatures, codexModelSupportsFastMode } from "./codex-feature-definitions.js";
18
+ import { renderProviderImageOutputAsAssistantMarkdown, } from "./provider-image-output.js";
17
19
  import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
18
20
  import { runProviderTurn } from "./provider-runner.js";
21
+ function assertChildWithPipes(child) {
22
+ if (!child.stdin || !child.stdout || !child.stderr) {
23
+ throw new Error("Child process did not expose stdio pipes");
24
+ }
25
+ }
26
+ function isRecord(value) {
27
+ return value != null && typeof value === "object" && !Array.isArray(value);
28
+ }
19
29
  const DEFAULT_TIMEOUT_MS = 14 * 24 * 60 * 60 * 1000;
20
30
  const TURN_START_TIMEOUT_MS = 90 * 1000;
21
31
  const INTERRUPT_TIMEOUT_MS = 2000;
@@ -24,7 +34,52 @@ const APP_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS = 1000;
24
34
  const CODEX_PROVIDER = "codex";
25
35
  const CODEX_IMAGE_ATTACHMENT_DIR = "paseo-attachments";
26
36
  const ASSISTANT_MESSAGE_BOUNDARY_MARKDOWN = "\n\n---\n\n";
37
+ const CODEX_TOOL_THREAD_ITEM_TYPES = new Set([
38
+ "commandExecution",
39
+ "fileChange",
40
+ "mcpToolCall",
41
+ "webSearch",
42
+ "collabAgentToolCall",
43
+ ]);
27
44
  const CODEX_PLAN_IMPLEMENTATION_PROMPT_PREFIX = "The user approved the plan. Implement it now. Do not restate or revise the plan unless blocked.";
45
+ // Codex's experimental `goals` feature ships in 0.128.0+. Older binaries reject
46
+ // `--enable goals` at launch, so we gate by version and silently skip the flag
47
+ // (and the /goal slash command) when the binary is too old.
48
+ const CODEX_GOALS_MIN_VERSION = [0, 128, 0];
49
+ function parseCodexVersion(versionOutput) {
50
+ const match = versionOutput.match(/(\d+)\.(\d+)\.(\d+)/);
51
+ if (!match)
52
+ return null;
53
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
54
+ }
55
+ function codexVersionAtLeast(versionOutput, min) {
56
+ const parsed = parseCodexVersion(versionOutput);
57
+ if (!parsed)
58
+ return false;
59
+ for (let i = 0; i < 3; i += 1) {
60
+ if (parsed[i] > min[i])
61
+ return true;
62
+ if (parsed[i] < min[i])
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+ function parseGoalSubcommand(args) {
68
+ const trimmed = (args ?? "").trim();
69
+ if (!trimmed)
70
+ return { kind: "usage" };
71
+ const lower = trimmed.toLowerCase();
72
+ if (lower === "pause")
73
+ return { kind: "pause" };
74
+ if (lower === "resume")
75
+ return { kind: "resume" };
76
+ if (lower === "clear")
77
+ return { kind: "clear" };
78
+ return { kind: "set", objective: trimmed };
79
+ }
80
+ function formatOutOfBandStatusMessage(text) {
81
+ return `${text.replace(/\n+$/u, "")}\n\n`;
82
+ }
28
83
  const CODEX_APP_SERVER_CAPABILITIES = {
29
84
  supportsStreaming: true,
30
85
  supportsSessionPersistence: true,
@@ -397,8 +452,50 @@ function toCodexMcpConfig(config) {
397
452
  url: config.url,
398
453
  http_headers: config.headers,
399
454
  };
455
+ default: {
456
+ const _exhaustive = config;
457
+ throw new Error(`Unsupported MCP config type: ${String(_exhaustive.type)}`);
458
+ }
400
459
  }
401
460
  }
461
+ function isJsonRpcResponse(msg) {
462
+ if (!isRecord(msg))
463
+ return false;
464
+ if (typeof msg.id !== "number")
465
+ return false;
466
+ return msg.result !== undefined || !!msg.error;
467
+ }
468
+ function isJsonRpcRequest(msg) {
469
+ if (!isRecord(msg))
470
+ return false;
471
+ return typeof msg.id === "number" && typeof msg.method === "string";
472
+ }
473
+ function isJsonRpcNotification(msg) {
474
+ if (!isRecord(msg))
475
+ return false;
476
+ return typeof msg.method === "string" && typeof msg.id !== "number";
477
+ }
478
+ function toObjectRecord(value) {
479
+ return isRecord(value) ? value : undefined;
480
+ }
481
+ const CodexModelListResponseSchema = z.object({
482
+ data: z
483
+ .array(z.object({
484
+ id: z.string(),
485
+ displayName: z.string().optional(),
486
+ description: z.string().optional(),
487
+ isDefault: z.boolean().optional(),
488
+ model: z.string().optional(),
489
+ defaultReasoningEffort: z.string().optional(),
490
+ supportedReasoningEfforts: z
491
+ .array(z.object({
492
+ reasoningEffort: z.string().optional(),
493
+ description: z.string().optional(),
494
+ }))
495
+ .optional(),
496
+ }))
497
+ .optional(),
498
+ });
402
499
  class CodexAppServerClient {
403
500
  constructor(child, logger) {
404
501
  this.child = child;
@@ -490,7 +587,7 @@ class CodexAppServerClient {
490
587
  catch {
491
588
  // ignore
492
589
  }
493
- const result = await terminateProcessTree(this.child, {
590
+ const result = await terminateWithTreeKill(this.child, {
494
591
  gracefulTimeoutMs: APP_SERVER_GRACEFUL_SHUTDOWN_TIMEOUT_MS,
495
592
  forceTimeoutMs: APP_SERVER_FORCE_SHUTDOWN_TIMEOUT_MS,
496
593
  onForceSignal: () => {
@@ -504,33 +601,30 @@ class CodexAppServerClient {
504
601
  async handleLine(line) {
505
602
  if (!line.trim())
506
603
  return;
507
- let msg;
508
- try {
509
- msg = JSON.parse(line);
510
- }
511
- catch (error) {
512
- this.logger.warn({ error, line }, "Failed to parse Codex app-server JSON");
604
+ const raw = JSON.parse(line);
605
+ if (!isRecord(raw)) {
606
+ this.logger.warn({ line }, "Parsed JSON is not an object");
513
607
  return;
514
608
  }
515
- if (typeof msg.id === "number") {
516
- const id = msg.id;
517
- if (msg.result !== undefined || msg.error) {
609
+ if (isJsonRpcResponse(raw)) {
610
+ const id = raw.id;
611
+ if (raw.result !== undefined || raw.error) {
518
612
  const pending = this.pending.get(id);
519
613
  if (!pending)
520
614
  return;
521
615
  clearTimeout(pending.timer);
522
616
  this.pending.delete(id);
523
- if (msg.error) {
524
- pending.reject(new Error(msg.error?.message ?? "Unknown error"));
617
+ if (raw.error) {
618
+ pending.reject(new Error(raw.error.message ?? "Unknown error"));
525
619
  }
526
620
  else {
527
- pending.resolve(msg.result);
621
+ pending.resolve(raw.result);
528
622
  }
529
623
  return;
530
624
  }
531
625
  // Server-initiated request
532
- if (typeof msg.method === "string") {
533
- const request = msg;
626
+ if (isJsonRpcRequest(raw)) {
627
+ const request = raw;
534
628
  const handler = this.requestHandlers.get(request.method);
535
629
  try {
536
630
  const result = handler ? await handler(request.params) : {};
@@ -545,22 +639,22 @@ class CodexAppServerClient {
545
639
  return;
546
640
  }
547
641
  }
548
- if (typeof msg.method === "string") {
549
- const notification = msg;
550
- this.notificationHandler?.(notification.method, notification.params);
642
+ if (isJsonRpcNotification(raw)) {
643
+ this.notificationHandler?.(raw.method, raw.params);
551
644
  }
552
645
  }
553
646
  }
554
647
  function toAgentUsage(tokenUsage) {
555
- if (!tokenUsage || typeof tokenUsage !== "object")
648
+ const usage = toObjectRecord(tokenUsage);
649
+ if (!usage)
556
650
  return undefined;
557
- const usage = tokenUsage;
651
+ const last = toObjectRecord(usage.last);
558
652
  const contextWindowMaxTokens = firstPositiveFiniteNumber(usage.model_context_window, usage.modelContextWindow);
559
- const contextWindowUsedTokens = firstPositiveFiniteNumber(usage.last?.total_tokens, usage.last?.totalTokens);
653
+ const contextWindowUsedTokens = firstPositiveFiniteNumber(last?.total_tokens, last?.totalTokens);
560
654
  return {
561
- inputTokens: usage.last?.inputTokens,
562
- cachedInputTokens: usage.last?.cachedInputTokens,
563
- outputTokens: usage.last?.outputTokens,
655
+ inputTokens: typeof last?.inputTokens === "number" ? last.inputTokens : undefined,
656
+ cachedInputTokens: typeof last?.cachedInputTokens === "number" ? last.cachedInputTokens : undefined,
657
+ outputTokens: typeof last?.outputTokens === "number" ? last.outputTokens : undefined,
564
658
  ...(contextWindowMaxTokens !== undefined ? { contextWindowMaxTokens } : {}),
565
659
  ...(contextWindowUsedTokens !== undefined ? { contextWindowUsedTokens } : {}),
566
660
  };
@@ -570,11 +664,12 @@ function extractUserText(content) {
570
664
  return null;
571
665
  const parts = [];
572
666
  for (const item of content) {
573
- if (item && typeof item === "object") {
574
- const obj = item;
575
- if (obj.type === "text" && typeof obj.text === "string") {
576
- parts.push(obj.text);
577
- }
667
+ const record = toObjectRecord(item);
668
+ if (!record) {
669
+ continue;
670
+ }
671
+ if (record.type === "text" && typeof record.text === "string") {
672
+ parts.push(record.text);
578
673
  }
579
674
  }
580
675
  return parts.length > 0 ? parts.join("\n") : null;
@@ -661,10 +756,10 @@ function normalizeCodexQuestionPrompts(raw) {
661
756
  }
662
757
  const questions = [];
663
758
  for (const item of raw) {
664
- if (!item || typeof item !== "object") {
759
+ const record = toObjectRecord(item);
760
+ if (!record) {
665
761
  continue;
666
762
  }
667
- const record = item;
668
763
  const id = nonEmptyString(record.id);
669
764
  const header = nonEmptyString(record.header);
670
765
  const question = nonEmptyString(record.question);
@@ -673,10 +768,10 @@ function normalizeCodexQuestionPrompts(raw) {
673
768
  }
674
769
  const options = Array.isArray(record.options)
675
770
  ? record.options.flatMap((option) => {
676
- if (!option || typeof option !== "object") {
771
+ const optionRecord = toObjectRecord(option);
772
+ if (!optionRecord) {
677
773
  return [];
678
774
  }
679
- const optionRecord = option;
680
775
  const label = nonEmptyString(optionRecord.label);
681
776
  if (!label) {
682
777
  return [];
@@ -771,10 +866,9 @@ function mapCodexQuestionResponseByHeader(params) {
771
866
  if (params.response.behavior !== "allow") {
772
867
  return null;
773
868
  }
774
- const answersRecord = params.response.updatedInput && typeof params.response.updatedInput === "object"
775
- ? params.response.updatedInput.answers
776
- : undefined;
777
- if (!answersRecord || typeof answersRecord !== "object") {
869
+ const updatedInputRecord = toObjectRecord(params.response.updatedInput);
870
+ const answersRecord = toObjectRecord(updatedInputRecord?.answers);
871
+ if (!answersRecord) {
778
872
  return null;
779
873
  }
780
874
  const answers = {};
@@ -800,10 +894,10 @@ function mapCodexQuestionResponseByHeader(params) {
800
894
  return Object.keys(answers).length > 0 ? answers : null;
801
895
  }
802
896
  function extractPatchLikeText(value) {
803
- if (!value || typeof value !== "object") {
897
+ const record = toObjectRecord(value);
898
+ if (!record) {
804
899
  return undefined;
805
900
  }
806
- const record = value;
807
901
  const candidates = [
808
902
  record.diff,
809
903
  record.patch,
@@ -842,6 +936,10 @@ function normalizeCodexThreadItemType(rawType) {
842
936
  return "webSearch";
843
937
  case "CollabAgentToolCall":
844
938
  return "collabAgentToolCall";
939
+ case "ImageView":
940
+ return "imageView";
941
+ case "ImageGeneration":
942
+ return "imageGeneration";
845
943
  default:
846
944
  return rawType;
847
945
  }
@@ -900,10 +998,10 @@ function parseCodexPatchChanges(changes) {
900
998
  if (Array.isArray(changes)) {
901
999
  return changes
902
1000
  .map((entry) => {
903
- if (!entry || typeof entry !== "object") {
1001
+ const record = toObjectRecord(entry);
1002
+ if (!record) {
904
1003
  return null;
905
1004
  }
906
- const record = entry;
907
1005
  const pathValue = resolvePathFromRecord(record);
908
1006
  if (!pathValue) {
909
1007
  return null;
@@ -918,7 +1016,10 @@ function parseCodexPatchChanges(changes) {
918
1016
  })
919
1017
  .filter((entry) => entry !== null);
920
1018
  }
921
- const recordChanges = changes;
1019
+ const recordChanges = toObjectRecord(changes);
1020
+ if (!recordChanges) {
1021
+ return [];
1022
+ }
922
1023
  const directPathValue = resolvePathFromRecord(recordChanges);
923
1024
  if (directPathValue) {
924
1025
  return [
@@ -1118,16 +1219,81 @@ function mapCodexThreadUserMessageItem(normalizedItem, includeUserMessage) {
1118
1219
  const text = extractUserText(normalizedItem.content) ?? "";
1119
1220
  return { type: "user_message", text };
1120
1221
  }
1222
+ function firstStringField(record, fields) {
1223
+ for (const field of fields) {
1224
+ const value = record[field];
1225
+ if (typeof value === "string") {
1226
+ return value;
1227
+ }
1228
+ }
1229
+ return null;
1230
+ }
1231
+ function codexImageOutputFromResult(result) {
1232
+ if (typeof result === "string") {
1233
+ const trimmed = result.trim();
1234
+ if (trimmed.toLowerCase().startsWith("data:image/") ||
1235
+ (/^[A-Za-z0-9+/]+={0,2}$/.test(trimmed) && trimmed.length > 64)) {
1236
+ return { data: trimmed };
1237
+ }
1238
+ return { url: trimmed };
1239
+ }
1240
+ const resultRecord = toObjectRecord(result);
1241
+ if (!resultRecord) {
1242
+ return null;
1243
+ }
1244
+ return {
1245
+ path: firstStringField(resultRecord, ["path", "savedPath", "saved_path"]),
1246
+ url: firstStringField(resultRecord, ["url"]),
1247
+ data: firstStringField(resultRecord, ["data"]),
1248
+ mimeType: firstStringField(resultRecord, ["mimeType", "mime_type"]),
1249
+ };
1250
+ }
1251
+ function writeImageAttachmentSync(mimeType, data) {
1252
+ const attachmentsDir = path.join(os.tmpdir(), CODEX_IMAGE_ATTACHMENT_DIR);
1253
+ fsSync.mkdirSync(attachmentsDir, { recursive: true });
1254
+ const normalized = normalizeImageData(mimeType, data);
1255
+ const extension = getImageExtension(normalized.mimeType);
1256
+ const filename = `${randomUUID()}.${extension}`;
1257
+ const filePath = path.join(attachmentsDir, filename);
1258
+ fsSync.writeFileSync(filePath, Buffer.from(normalized.data, "base64"));
1259
+ return filePath;
1260
+ }
1261
+ function materializeCodexImageOutput(image) {
1262
+ return {
1263
+ path: writeImageAttachmentSync(image.mimeType ?? "image/png", image.data),
1264
+ };
1265
+ }
1266
+ function mapCodexThreadImageItem(normalizedType, normalizedItem) {
1267
+ if (normalizedType === "imageView") {
1268
+ return renderProviderImageOutputAsAssistantMarkdown({
1269
+ path: firstStringField(normalizedItem, ["path"]),
1270
+ });
1271
+ }
1272
+ const savedPath = firstStringField(normalizedItem, ["savedPath", "saved_path"]);
1273
+ const result = codexImageOutputFromResult(normalizedItem.result);
1274
+ return renderProviderImageOutputAsAssistantMarkdown({
1275
+ path: savedPath ?? result?.path ?? null,
1276
+ url: result?.url ?? null,
1277
+ data: result?.data ?? null,
1278
+ mimeType: result?.mimeType ?? null,
1279
+ }, { materialize: materializeCodexImageOutput });
1280
+ }
1121
1281
  function threadItemToTimeline(item, options) {
1122
- if (!item || typeof item !== "object")
1282
+ const itemRecord = toObjectRecord(item);
1283
+ if (!itemRecord)
1123
1284
  return null;
1124
- const itemRecord = item;
1125
1285
  const includeUserMessage = options?.includeUserMessage ?? true;
1126
1286
  const cwd = options?.cwd ?? null;
1127
1287
  const normalizedType = normalizeCodexThreadItemType(typeof itemRecord.type === "string" ? itemRecord.type : undefined);
1128
1288
  const normalizedItem = normalizedType && normalizedType !== itemRecord.type
1129
1289
  ? { ...itemRecord, type: normalizedType }
1130
1290
  : itemRecord;
1291
+ if (normalizedType === "imageView" || normalizedType === "imageGeneration") {
1292
+ return mapCodexThreadImageItem(normalizedType, normalizedItem);
1293
+ }
1294
+ if (normalizedType && CODEX_TOOL_THREAD_ITEM_TYPES.has(normalizedType)) {
1295
+ return mapCodexToolCallFromThreadItem(normalizedItem, { cwd });
1296
+ }
1131
1297
  switch (normalizedType) {
1132
1298
  case "userMessage":
1133
1299
  return mapCodexThreadUserMessageItem(normalizedItem, includeUserMessage);
@@ -1140,12 +1306,6 @@ function threadItemToTimeline(item, options) {
1140
1306
  return mapCodexThreadPlanItem(normalizedItem);
1141
1307
  case "reasoning":
1142
1308
  return mapCodexThreadReasoningItem(normalizedItem);
1143
- case "commandExecution":
1144
- case "fileChange":
1145
- case "mcpToolCall":
1146
- case "webSearch":
1147
- case "collabAgentToolCall":
1148
- return mapCodexToolCallFromThreadItem(normalizedItem, { cwd });
1149
1309
  default:
1150
1310
  return null;
1151
1311
  }
@@ -1816,10 +1976,13 @@ async function writeImageAttachment(mimeType, data) {
1816
1976
  async function readCodexConfiguredDefaults(client, logger) {
1817
1977
  let savedConfigDefaults = {};
1818
1978
  try {
1819
- const response = (await client.request("getUserSavedConfig", {}));
1979
+ const response = toObjectRecord(await client.request("getUserSavedConfig", {}));
1980
+ const config = toObjectRecord(response?.config);
1981
+ const modelValue = typeof config?.model === "string" ? config.model : undefined;
1982
+ const thinkingOptionValue = typeof config?.modelReasoningEffort === "string" ? config.modelReasoningEffort : null;
1820
1983
  savedConfigDefaults = {
1821
- model: normalizeCodexModelId(response?.config?.model),
1822
- thinkingOptionId: normalizeCodexThinkingOptionId(response?.config?.modelReasoningEffort ?? null),
1984
+ model: normalizeCodexModelId(modelValue),
1985
+ thinkingOptionId: normalizeCodexThinkingOptionId(thinkingOptionValue),
1823
1986
  };
1824
1987
  }
1825
1988
  catch (error) {
@@ -1830,10 +1993,13 @@ async function readCodexConfiguredDefaults(client, logger) {
1830
1993
  }
1831
1994
  let configReadDefaults = {};
1832
1995
  try {
1833
- const response = (await client.request("config/read", {}));
1996
+ const response = toObjectRecord(await client.request("config/read", {}));
1997
+ const config = toObjectRecord(response?.config);
1998
+ const modelValue = typeof config?.model === "string" ? config.model : undefined;
1999
+ const thinkingOptionValue = typeof config?.model_reasoning_effort === "string" ? config.model_reasoning_effort : null;
1834
2000
  configReadDefaults = {
1835
- model: normalizeCodexModelId(response?.config?.model),
1836
- thinkingOptionId: normalizeCodexThinkingOptionId(response?.config?.model_reasoning_effort ?? null),
2001
+ model: normalizeCodexModelId(modelValue),
2002
+ thinkingOptionId: normalizeCodexThinkingOptionId(thinkingOptionValue),
1837
2003
  };
1838
2004
  }
1839
2005
  catch (error) {
@@ -1905,11 +2071,12 @@ function buildCodexAppServerInitializeParams() {
1905
2071
  };
1906
2072
  }
1907
2073
  class CodexAppServerAgentSession {
1908
- constructor(config, resumeHandle, logger, spawnAppServer, deps = {}, ephemeral = false) {
2074
+ constructor(config, resumeHandle, logger, spawnAppServer, deps = {}, ephemeral = false, goalsEnabled = false) {
1909
2075
  this.resumeHandle = resumeHandle;
1910
2076
  this.spawnAppServer = spawnAppServer;
1911
2077
  this.deps = deps;
1912
2078
  this.ephemeral = ephemeral;
2079
+ this.goalsEnabled = goalsEnabled;
1913
2080
  this.provider = CODEX_PROVIDER;
1914
2081
  this.capabilities = CODEX_APP_SERVER_CAPABILITIES;
1915
2082
  this.currentThreadId = null;
@@ -1999,15 +2166,20 @@ class CodexAppServerAgentSession {
1999
2166
  if (!this.client)
2000
2167
  return;
2001
2168
  try {
2002
- const response = (await this.client.request("collaborationMode/list", {}));
2169
+ const response = toObjectRecord(await this.client.request("collaborationMode/list", {}));
2003
2170
  const data = Array.isArray(response?.data) ? response.data : [];
2004
- this.collaborationModes = data.map((entry) => ({
2005
- name: typeof entry.name === "string" ? entry.name : "",
2006
- mode: typeof entry.mode === "string" ? entry.mode : null,
2007
- model: typeof entry.model === "string" ? entry.model : null,
2008
- reasoning_effort: typeof entry.reasoning_effort === "string" ? entry.reasoning_effort : null,
2009
- developer_instructions: typeof entry.developer_instructions === "string" ? entry.developer_instructions : null,
2010
- }));
2171
+ this.collaborationModes = data.map((entry) => {
2172
+ const record = toObjectRecord(entry);
2173
+ return {
2174
+ name: typeof record?.name === "string" ? record.name : "",
2175
+ mode: typeof record?.mode === "string" ? record.mode : null,
2176
+ model: typeof record?.model === "string" ? record.model : null,
2177
+ reasoning_effort: typeof record?.reasoning_effort === "string" ? record.reasoning_effort : null,
2178
+ developer_instructions: typeof record?.developer_instructions === "string"
2179
+ ? record.developer_instructions
2180
+ : null,
2181
+ };
2182
+ });
2011
2183
  }
2012
2184
  catch (error) {
2013
2185
  this.logger.trace({ error }, "Failed to load collaboration modes");
@@ -2019,22 +2191,22 @@ class CodexAppServerAgentSession {
2019
2191
  if (!this.client)
2020
2192
  return;
2021
2193
  try {
2022
- const response = (await this.client.request("skills/list", {
2194
+ const response = toObjectRecord(await this.client.request("skills/list", {
2023
2195
  cwd: [this.config.cwd],
2024
2196
  }));
2025
2197
  const entries = Array.isArray(response?.data) ? response.data : [];
2026
2198
  const skills = [];
2027
2199
  for (const entry of entries) {
2028
- const list = Array.isArray(entry.skills)
2029
- ? entry.skills
2030
- : [];
2200
+ const entryRecord = toObjectRecord(entry);
2201
+ const list = Array.isArray(entryRecord?.skills) ? entryRecord.skills : [];
2031
2202
  for (const skill of list) {
2032
- if (typeof skill?.name !== "string" || typeof skill?.path !== "string")
2203
+ const skillRecord = toObjectRecord(skill);
2204
+ if (typeof skillRecord?.name !== "string" || typeof skillRecord?.path !== "string")
2033
2205
  continue;
2034
2206
  skills.push({
2035
- name: skill.name,
2036
- description: resolveSkillDescription(skill),
2037
- path: skill.path,
2207
+ name: skillRecord.name,
2208
+ description: resolveSkillDescription(skillRecord),
2209
+ path: skillRecord.path,
2038
2210
  });
2039
2211
  }
2040
2212
  }
@@ -2183,7 +2355,7 @@ class CodexAppServerAgentSession {
2183
2355
  if (!this.client || !this.currentThreadId)
2184
2356
  return;
2185
2357
  try {
2186
- const loaded = (await this.client.request("thread/loaded/list", {}));
2358
+ const loaded = toObjectRecord(await this.client.request("thread/loaded/list", {}));
2187
2359
  const ids = Array.isArray(loaded?.data) ? loaded.data : [];
2188
2360
  if (ids.includes(this.currentThreadId)) {
2189
2361
  return;
@@ -2622,7 +2794,86 @@ class CodexAppServerAgentSession {
2622
2794
  const fallbackSkills = appServerSkills.length === 0
2623
2795
  ? await listCodexSkills(this.config.cwd, this.deps.workspaceGitService)
2624
2796
  : [];
2625
- return [...appServerSkills, ...fallbackSkills, ...prompts].sort((a, b) => a.name.localeCompare(b.name));
2797
+ const builtin = [];
2798
+ if (this.goalsEnabled) {
2799
+ builtin.push({
2800
+ name: "goal",
2801
+ description: "Set, pause, resume, or clear the agent's goal",
2802
+ argumentHint: "[<objective>|pause|resume|clear]",
2803
+ });
2804
+ }
2805
+ return [...builtin, ...appServerSkills, ...fallbackSkills, ...prompts].sort((a, b) => a.name.localeCompare(b.name));
2806
+ }
2807
+ tryHandleOutOfBand(prompt) {
2808
+ if (!this.goalsEnabled)
2809
+ return null;
2810
+ if (typeof prompt !== "string")
2811
+ return null;
2812
+ const parsed = this.parseSlashCommandInput(prompt);
2813
+ if (!parsed || parsed.commandName !== "goal")
2814
+ return null;
2815
+ const subcommand = parseGoalSubcommand(parsed.args);
2816
+ return {
2817
+ run: async ({ emit }) => {
2818
+ const text = formatOutOfBandStatusMessage(await this.executeGoalSubcommand(subcommand));
2819
+ emit({
2820
+ type: "timeline",
2821
+ provider: CODEX_PROVIDER,
2822
+ item: { type: "assistant_message", text },
2823
+ });
2824
+ },
2825
+ };
2826
+ }
2827
+ async executeGoalSubcommand(subcommand) {
2828
+ if (subcommand.kind === "usage") {
2829
+ return "Usage: /goal <objective>|pause|resume|clear";
2830
+ }
2831
+ try {
2832
+ await this.connect();
2833
+ if (this.currentThreadId) {
2834
+ await this.ensureThreadLoaded();
2835
+ }
2836
+ else {
2837
+ await this.ensureThread();
2838
+ }
2839
+ if (!this.client || !this.currentThreadId) {
2840
+ throw new Error("Codex thread is not available");
2841
+ }
2842
+ switch (subcommand.kind) {
2843
+ case "set": {
2844
+ await this.client.request("thread/goal/set", {
2845
+ threadId: this.currentThreadId,
2846
+ objective: subcommand.objective,
2847
+ status: "active",
2848
+ });
2849
+ return `Goal set: ${subcommand.objective}`;
2850
+ }
2851
+ case "pause": {
2852
+ await this.client.request("thread/goal/set", {
2853
+ threadId: this.currentThreadId,
2854
+ status: "paused",
2855
+ });
2856
+ return "Goal paused.";
2857
+ }
2858
+ case "resume": {
2859
+ await this.client.request("thread/goal/set", {
2860
+ threadId: this.currentThreadId,
2861
+ status: "active",
2862
+ });
2863
+ return "Goal resumed.";
2864
+ }
2865
+ case "clear": {
2866
+ await this.client.request("thread/goal/clear", {
2867
+ threadId: this.currentThreadId,
2868
+ });
2869
+ return "Goal cleared.";
2870
+ }
2871
+ }
2872
+ }
2873
+ catch (error) {
2874
+ const message = error instanceof Error ? error.message : "unknown error";
2875
+ return `Failed to update goal: ${message}`;
2876
+ }
2626
2877
  }
2627
2878
  async resolveModelAndThinking() {
2628
2879
  if (!this.client) {
@@ -2641,8 +2892,20 @@ class CodexAppServerAgentSession {
2641
2892
  thinkingOptionId = configuredDefaults.thinkingOptionId;
2642
2893
  }
2643
2894
  if (!model || !thinkingOptionId) {
2644
- const modelResponse = (await this.client.request("model/list", {}));
2645
- const models = modelResponse?.data ?? [];
2895
+ const modelResponse = toObjectRecord(await this.client.request("model/list", {}));
2896
+ const modelData = Array.isArray(modelResponse?.data) ? modelResponse.data : [];
2897
+ const models = modelData
2898
+ .map((m) => {
2899
+ const record = toObjectRecord(m);
2900
+ return {
2901
+ id: typeof record?.id === "string" ? record.id : "",
2902
+ isDefault: !!record?.isDefault,
2903
+ defaultReasoningEffort: typeof record?.defaultReasoningEffort === "string"
2904
+ ? record.defaultReasoningEffort
2905
+ : undefined,
2906
+ };
2907
+ })
2908
+ .filter((m) => m.id);
2646
2909
  const defaultModel = models.find((m) => m.isDefault) ?? models[0];
2647
2910
  if (!defaultModel) {
2648
2911
  throw new Error("No models available from Codex app-server");
@@ -2672,7 +2935,7 @@ class CodexAppServerAgentSession {
2672
2935
  const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
2673
2936
  const sandbox = this.config.sandboxMode ?? preset.sandbox;
2674
2937
  const innerConfig = this.buildCodexInnerConfig();
2675
- const response = (await this.client.request("thread/start", {
2938
+ const rawResponse = await this.client.request("thread/start", {
2676
2939
  model,
2677
2940
  cwd: this.config.cwd ?? null,
2678
2941
  approvalPolicy,
@@ -2682,8 +2945,10 @@ class CodexAppServerAgentSession {
2682
2945
  : {}),
2683
2946
  ...(innerConfig ? { config: innerConfig } : {}),
2684
2947
  ...(this.ephemeral ? { ephemeral: true } : {}),
2685
- }));
2686
- const threadId = response?.thread?.id;
2948
+ });
2949
+ const response = toObjectRecord(rawResponse);
2950
+ const threadRecord = toObjectRecord(response?.thread);
2951
+ const threadId = typeof threadRecord?.id === "string" ? threadRecord.id : undefined;
2687
2952
  if (!threadId) {
2688
2953
  throw new Error("Codex app-server did not return thread id");
2689
2954
  }
@@ -3367,7 +3632,16 @@ class CodexAppServerAgentSession {
3367
3632
  }, "Codex edit tool call is missing diff/content fields");
3368
3633
  }
3369
3634
  handleCommandApprovalRequest(params) {
3370
- const parsed = params;
3635
+ const parsed = z
3636
+ .object({
3637
+ itemId: z.string(),
3638
+ threadId: z.string(),
3639
+ turnId: z.string(),
3640
+ command: z.string().nullable().optional(),
3641
+ cwd: z.string().nullable().optional(),
3642
+ reason: z.string().nullable().optional(),
3643
+ })
3644
+ .parse(params);
3371
3645
  const commandPreview = mapCodexExecNotificationToToolCall({
3372
3646
  callId: parsed.itemId,
3373
3647
  command: parsed.command,
@@ -3408,7 +3682,14 @@ class CodexAppServerAgentSession {
3408
3682
  });
3409
3683
  }
3410
3684
  handleFileChangeApprovalRequest(params) {
3411
- const parsed = params;
3685
+ const parsed = z
3686
+ .object({
3687
+ itemId: z.string(),
3688
+ threadId: z.string(),
3689
+ turnId: z.string(),
3690
+ reason: z.string().nullable().optional(),
3691
+ })
3692
+ .parse(params);
3412
3693
  const requestId = `permission-${parsed.itemId}`;
3413
3694
  const request = {
3414
3695
  id: requestId,
@@ -3437,7 +3718,14 @@ class CodexAppServerAgentSession {
3437
3718
  });
3438
3719
  }
3439
3720
  handleToolApprovalRequest(params) {
3440
- const parsed = params;
3721
+ const parsed = z
3722
+ .object({
3723
+ itemId: z.string(),
3724
+ threadId: z.string(),
3725
+ turnId: z.string(),
3726
+ questions: z.array(z.unknown()),
3727
+ })
3728
+ .parse(params);
3441
3729
  const requestId = `permission-${parsed.itemId}`;
3442
3730
  const questions = normalizeCodexQuestionPrompts(parsed.questions);
3443
3731
  const request = {
@@ -3487,13 +3775,37 @@ export class CodexAppServerAgentClient {
3487
3775
  this.deps = deps;
3488
3776
  this.provider = CODEX_PROVIDER;
3489
3777
  this.capabilities = CODEX_APP_SERVER_CAPABILITIES;
3778
+ this.goalsEnabledPromise = null;
3490
3779
  }
3491
- async spawnAppServer(launchEnv) {
3780
+ resolveGoalsEnabled() {
3781
+ if (!this.goalsEnabledPromise) {
3782
+ this.goalsEnabledPromise = (async () => {
3783
+ try {
3784
+ const launchPrefix = await resolveCodexLaunchPrefix(this.runtimeSettings);
3785
+ const versionOutput = await resolveBinaryVersion(launchPrefix.command);
3786
+ const enabled = codexVersionAtLeast(versionOutput, CODEX_GOALS_MIN_VERSION);
3787
+ this.logger.trace({ versionOutput, enabled }, "Resolved codex goals feature gate");
3788
+ return enabled;
3789
+ }
3790
+ catch (error) {
3791
+ this.logger.warn({ err: error }, "Failed to probe codex version for goals gate");
3792
+ return false;
3793
+ }
3794
+ })();
3795
+ }
3796
+ return this.goalsEnabledPromise;
3797
+ }
3798
+ async spawnAppServer(launchEnv, options) {
3492
3799
  const launchPrefix = await resolveCodexLaunchPrefix(this.runtimeSettings);
3800
+ const args = [...launchPrefix.args, "app-server"];
3801
+ if (options?.goalsEnabled) {
3802
+ args.push("--enable", "goals");
3803
+ }
3493
3804
  this.logger.trace({
3494
3805
  launchPrefix,
3806
+ goalsEnabled: options?.goalsEnabled === true,
3495
3807
  }, "Spawning Codex app server");
3496
- return spawnProcess(launchPrefix.command, [...launchPrefix.args, "app-server"], {
3808
+ const child = spawnProcess(launchPrefix.command, args, {
3497
3809
  detached: process.platform !== "win32",
3498
3810
  stdio: ["pipe", "pipe", "pipe"],
3499
3811
  ...createProviderEnvSpec({
@@ -3501,10 +3813,13 @@ export class CodexAppServerAgentClient {
3501
3813
  overlays: [launchEnv],
3502
3814
  }),
3503
3815
  });
3816
+ assertChildWithPipes(child);
3817
+ return child;
3504
3818
  }
3505
3819
  async createSession(config, launchContext, options) {
3506
3820
  const sessionConfig = { ...config, provider: CODEX_PROVIDER };
3507
- const session = new CodexAppServerAgentSession(sessionConfig, null, this.logger, () => this.spawnAppServer(launchContext?.env), this.deps, options?.persistSession === false);
3821
+ const goalsEnabled = await this.resolveGoalsEnabled();
3822
+ const session = new CodexAppServerAgentSession(sessionConfig, null, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled }), this.deps, options?.persistSession === false, goalsEnabled);
3508
3823
  await session.connect();
3509
3824
  return session;
3510
3825
  }
@@ -3516,7 +3831,8 @@ export class CodexAppServerAgentClient {
3516
3831
  provider: CODEX_PROVIDER,
3517
3832
  cwd: overrides?.cwd ?? storedConfig.cwd ?? process.cwd(),
3518
3833
  };
3519
- const session = new CodexAppServerAgentSession(merged, handle, this.logger, () => this.spawnAppServer(launchContext?.env), this.deps);
3834
+ const goalsEnabled = await this.resolveGoalsEnabled();
3835
+ const session = new CodexAppServerAgentSession(merged, handle, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled }), this.deps, false, goalsEnabled);
3520
3836
  await session.connect();
3521
3837
  return session;
3522
3838
  }
@@ -3527,7 +3843,7 @@ export class CodexAppServerAgentClient {
3527
3843
  await client.request("initialize", buildCodexAppServerInitializeParams());
3528
3844
  client.notify("initialized", {});
3529
3845
  const limit = options?.limit ?? 20;
3530
- const response = (await client.request("thread/list", { limit }));
3846
+ const response = toObjectRecord(await client.request("thread/list", { limit }));
3531
3847
  const threads = Array.isArray(response?.data) ? response.data : [];
3532
3848
  const descriptors = await Promise.all(threads.slice(0, limit).map(async (thread) => {
3533
3849
  const threadId = typeof thread.id === "string" ? thread.id : "";
@@ -3581,8 +3897,9 @@ export class CodexAppServerAgentClient {
3581
3897
  try {
3582
3898
  await client.request("initialize", buildCodexAppServerInitializeParams());
3583
3899
  client.notify("initialized", {});
3584
- const response = (await client.request("model/list", {}));
3585
- const models = Array.isArray(response?.data) ? response.data : [];
3900
+ const rawResponse = await client.request("model/list", {});
3901
+ const parsedResponse = CodexModelListResponseSchema.safeParse(rawResponse);
3902
+ const models = parsedResponse.success ? (parsedResponse.data.data ?? []) : [];
3586
3903
  const configuredDefaults = await readCodexConfiguredDefaults(client, this.logger);
3587
3904
  const configuredDefaultModelId = configuredDefaults.model;
3588
3905
  const configuredDefaultThinkingOptionId = configuredDefaults.thinkingOptionId;