@getpaseo/server 0.1.74 → 0.1.76

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 (233) hide show
  1. package/dist/scripts/supervisor-entrypoint.js +2 -21
  2. package/dist/scripts/supervisor-entrypoint.js.map +1 -1
  3. package/dist/scripts/supervisor-log-config.js +31 -0
  4. package/dist/scripts/supervisor-log-config.js.map +1 -0
  5. package/dist/server/client/daemon-client.d.ts +13 -2
  6. package/dist/server/client/daemon-client.d.ts.map +1 -1
  7. package/dist/server/client/daemon-client.js +36 -0
  8. package/dist/server/client/daemon-client.js.map +1 -1
  9. package/dist/server/server/agent/agent-manager.d.ts +5 -1
  10. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  11. package/dist/server/server/agent/agent-manager.js +152 -22
  12. package/dist/server/server/agent/agent-manager.js.map +1 -1
  13. package/dist/server/server/agent/agent-prompt.d.ts.map +1 -1
  14. package/dist/server/server/agent/agent-prompt.js +27 -0
  15. package/dist/server/server/agent/agent-prompt.js.map +1 -1
  16. package/dist/server/server/agent/agent-sdk-types.d.ts +5 -0
  17. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  18. package/dist/server/server/agent/agent-sdk-types.js +10 -0
  19. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  20. package/dist/server/server/agent/foreground-run-state.d.ts +1 -1
  21. package/dist/server/server/agent/foreground-run-state.d.ts.map +1 -1
  22. package/dist/server/server/agent/foreground-run-state.js +2 -4
  23. package/dist/server/server/agent/foreground-run-state.js.map +1 -1
  24. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  25. package/dist/server/server/agent/mcp-server.js +74 -3
  26. package/dist/server/server/agent/mcp-server.js.map +1 -1
  27. package/dist/server/server/agent/provider-history-timestamps.d.ts +2 -0
  28. package/dist/server/server/agent/provider-history-timestamps.d.ts.map +1 -0
  29. package/dist/server/server/agent/provider-history-timestamps.js +16 -0
  30. package/dist/server/server/agent/provider-history-timestamps.js.map +1 -0
  31. package/dist/server/server/agent/provider-manifest.d.ts +1 -1
  32. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  33. package/dist/server/server/agent/provider-manifest.js +17 -3
  34. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  35. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  36. package/dist/server/server/agent/provider-registry.js +66 -32
  37. package/dist/server/server/agent/provider-registry.js.map +1 -1
  38. package/dist/server/server/agent/provider-snapshot-manager.d.ts +2 -6
  39. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  40. package/dist/server/server/agent/provider-snapshot-manager.js +43 -32
  41. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  42. package/dist/server/server/agent/providers/acp-agent.d.ts +42 -2
  43. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  44. package/dist/server/server/agent/providers/acp-agent.js +182 -28
  45. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  46. package/dist/server/server/agent/providers/claude/agent.d.ts +1 -1
  47. package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -1
  48. package/dist/server/server/agent/providers/claude/agent.js +191 -62
  49. package/dist/server/server/agent/providers/claude/agent.js.map +1 -1
  50. package/dist/server/server/agent/providers/claude/models.d.ts +2 -0
  51. package/dist/server/server/agent/providers/claude/models.d.ts.map +1 -1
  52. package/dist/server/server/agent/providers/claude/models.js +78 -0
  53. package/dist/server/server/agent/providers/claude/models.js.map +1 -1
  54. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +8 -1
  55. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts.map +1 -1
  56. package/dist/server/server/agent/providers/codex/app-server-transport.js +32 -1
  57. package/dist/server/server/agent/providers/codex/app-server-transport.js.map +1 -1
  58. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  59. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +36 -7
  60. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  61. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +28 -4
  62. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  63. package/dist/server/server/agent/providers/codex-app-server-agent.js +487 -80
  64. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  65. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts +10 -1
  66. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
  67. package/dist/server/server/agent/providers/copilot-acp-agent.js +114 -7
  68. package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
  69. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts +21 -0
  70. package/dist/server/server/agent/providers/cursor-acp-agent.d.ts.map +1 -0
  71. package/dist/server/server/agent/providers/cursor-acp-agent.js +85 -0
  72. package/dist/server/server/agent/providers/cursor-acp-agent.js.map +1 -0
  73. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +13 -0
  74. package/dist/server/server/agent/providers/generic-acp-agent.d.ts.map +1 -1
  75. package/dist/server/server/agent/providers/generic-acp-agent.js +209 -2
  76. package/dist/server/server/agent/providers/generic-acp-agent.js.map +1 -1
  77. package/dist/server/server/agent/providers/opencode-agent.d.ts +7 -7
  78. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  79. package/dist/server/server/agent/providers/opencode-agent.js +121 -109
  80. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  81. package/dist/server/server/agent/providers/pi-direct-agent.d.ts +2 -2
  82. package/dist/server/server/agent/providers/pi-direct-agent.d.ts.map +1 -1
  83. package/dist/server/server/agent/providers/pi-direct-agent.js +16 -15
  84. package/dist/server/server/agent/providers/pi-direct-agent.js.map +1 -1
  85. package/dist/server/server/agent/providers/pi-session-recovery-policy.d.ts +22 -0
  86. package/dist/server/server/agent/providers/pi-session-recovery-policy.d.ts.map +1 -0
  87. package/dist/server/server/agent/providers/pi-session-recovery-policy.js +51 -0
  88. package/dist/server/server/agent/providers/pi-session-recovery-policy.js.map +1 -0
  89. package/dist/server/server/agent/providers/provider-runner.d.ts +1 -1
  90. package/dist/server/server/agent/providers/provider-runner.d.ts.map +1 -1
  91. package/dist/server/server/agent/providers/provider-runner.js +2 -1
  92. package/dist/server/server/agent/providers/provider-runner.js.map +1 -1
  93. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts +1 -1
  94. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.d.ts.map +1 -1
  95. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js +2 -1
  96. package/dist/server/server/agent/providers/test-utils/session-stream-adapter.js.map +1 -1
  97. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  98. package/dist/server/server/agent/stt-manager.d.ts +5 -1
  99. package/dist/server/server/agent/stt-manager.d.ts.map +1 -1
  100. package/dist/server/server/agent/stt-manager.js +3 -2
  101. package/dist/server/server/agent/stt-manager.js.map +1 -1
  102. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
  103. package/dist/server/server/agent/timeline-projection.js +9 -0
  104. package/dist/server/server/agent/timeline-projection.js.map +1 -1
  105. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts +40 -0
  106. package/dist/server/server/auto-archive-on-merge/archive-if-safe.d.ts.map +1 -0
  107. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js +80 -0
  108. package/dist/server/server/auto-archive-on-merge/archive-if-safe.js.map +1 -0
  109. package/dist/server/server/auto-archive-on-merge/index.d.ts +8 -0
  110. package/dist/server/server/auto-archive-on-merge/index.d.ts.map +1 -0
  111. package/dist/server/server/auto-archive-on-merge/index.js +15 -0
  112. package/dist/server/server/auto-archive-on-merge/index.js.map +1 -0
  113. package/dist/server/server/bootstrap.d.ts +6 -0
  114. package/dist/server/server/bootstrap.d.ts.map +1 -1
  115. package/dist/server/server/bootstrap.js +74 -38
  116. package/dist/server/server/bootstrap.js.map +1 -1
  117. package/dist/server/server/checkout/status-projection.d.ts.map +1 -1
  118. package/dist/server/server/checkout/status-projection.js +5 -1
  119. package/dist/server/server/checkout/status-projection.js.map +1 -1
  120. package/dist/server/server/config.d.ts.map +1 -1
  121. package/dist/server/server/config.js +3 -1
  122. package/dist/server/server/config.js.map +1 -1
  123. package/dist/server/server/daemon-config-store.js +1 -0
  124. package/dist/server/server/daemon-config-store.js.map +1 -1
  125. package/dist/server/server/dictation/dictation-stream-manager.d.ts +2 -0
  126. package/dist/server/server/dictation/dictation-stream-manager.d.ts.map +1 -1
  127. package/dist/server/server/dictation/dictation-stream-manager.js +2 -1
  128. package/dist/server/server/dictation/dictation-stream-manager.js.map +1 -1
  129. package/dist/server/server/logger.js +1 -1
  130. package/dist/server/server/logger.js.map +1 -1
  131. package/dist/server/server/loop/rpc-schemas.d.ts +96 -96
  132. package/dist/server/server/loop-service.d.ts +18 -18
  133. package/dist/server/server/paseo-worktree-service.d.ts +3 -1
  134. package/dist/server/server/paseo-worktree-service.d.ts.map +1 -1
  135. package/dist/server/server/paseo-worktree-service.js +55 -18
  136. package/dist/server/server/paseo-worktree-service.js.map +1 -1
  137. package/dist/server/server/persisted-config.d.ts +25 -0
  138. package/dist/server/server/persisted-config.d.ts.map +1 -1
  139. package/dist/server/server/persisted-config.js +3 -0
  140. package/dist/server/server/persisted-config.js.map +1 -1
  141. package/dist/server/server/relay-transport.d.ts +2 -1
  142. package/dist/server/server/relay-transport.d.ts.map +1 -1
  143. package/dist/server/server/relay-transport.js +26 -4
  144. package/dist/server/server/relay-transport.js.map +1 -1
  145. package/dist/server/server/schedule/service.d.ts.map +1 -1
  146. package/dist/server/server/schedule/service.js +14 -1
  147. package/dist/server/server/schedule/service.js.map +1 -1
  148. package/dist/server/server/session.d.ts +9 -0
  149. package/dist/server/server/session.d.ts.map +1 -1
  150. package/dist/server/server/session.js +281 -50
  151. package/dist/server/server/session.js.map +1 -1
  152. package/dist/server/server/speech/providers/local/config.d.ts +5 -0
  153. package/dist/server/server/speech/providers/local/config.d.ts.map +1 -1
  154. package/dist/server/server/speech/providers/local/config.js +35 -0
  155. package/dist/server/server/speech/providers/local/config.js.map +1 -1
  156. package/dist/server/server/speech/speech-config-resolver.d.ts.map +1 -1
  157. package/dist/server/server/speech/speech-config-resolver.js +1 -0
  158. package/dist/server/server/speech/speech-config-resolver.js.map +1 -1
  159. package/dist/server/server/speech/speech-runtime.d.ts +2 -0
  160. package/dist/server/server/speech/speech-runtime.d.ts.map +1 -1
  161. package/dist/server/server/speech/speech-runtime.js +2 -0
  162. package/dist/server/server/speech/speech-runtime.js.map +1 -1
  163. package/dist/server/server/voice/voice-turn-controller.d.ts +1 -0
  164. package/dist/server/server/voice/voice-turn-controller.d.ts.map +1 -1
  165. package/dist/server/server/voice/voice-turn-controller.js +1 -1
  166. package/dist/server/server/voice/voice-turn-controller.js.map +1 -1
  167. package/dist/server/server/websocket-server.d.ts.map +1 -1
  168. package/dist/server/server/websocket-server.js +7 -0
  169. package/dist/server/server/websocket-server.js.map +1 -1
  170. package/dist/server/server/workspace-git-service.d.ts +6 -1
  171. package/dist/server/server/workspace-git-service.d.ts.map +1 -1
  172. package/dist/server/server/workspace-git-service.js +27 -4
  173. package/dist/server/server/workspace-git-service.js.map +1 -1
  174. package/dist/server/server/workspace-reconciliation-service.d.ts +4 -2
  175. package/dist/server/server/workspace-reconciliation-service.d.ts.map +1 -1
  176. package/dist/server/server/workspace-reconciliation-service.js +112 -14
  177. package/dist/server/server/workspace-reconciliation-service.js.map +1 -1
  178. package/dist/server/server/workspace-registry.d.ts +6 -1
  179. package/dist/server/server/workspace-registry.d.ts.map +1 -1
  180. package/dist/server/server/workspace-registry.js +11 -0
  181. package/dist/server/server/workspace-registry.js.map +1 -1
  182. package/dist/server/server/worktree-session.d.ts.map +1 -1
  183. package/dist/server/server/worktree-session.js +1 -0
  184. package/dist/server/server/worktree-session.js.map +1 -1
  185. package/dist/server/services/github-service.d.ts +46 -0
  186. package/dist/server/services/github-service.d.ts.map +1 -1
  187. package/dist/server/services/github-service.js +274 -5
  188. package/dist/server/services/github-service.js.map +1 -1
  189. package/dist/server/shared/messages.d.ts +3427 -290
  190. package/dist/server/shared/messages.d.ts.map +1 -1
  191. package/dist/server/shared/messages.js +93 -3
  192. package/dist/server/shared/messages.js.map +1 -1
  193. package/dist/server/shared/terminal-input-mode.d.ts +26 -0
  194. package/dist/server/shared/terminal-input-mode.d.ts.map +1 -0
  195. package/dist/server/shared/terminal-input-mode.js +151 -0
  196. package/dist/server/shared/terminal-input-mode.js.map +1 -0
  197. package/dist/server/terminal/terminal-session-controller.d.ts.map +1 -1
  198. package/dist/server/terminal/terminal-session-controller.js +12 -2
  199. package/dist/server/terminal/terminal-session-controller.js.map +1 -1
  200. package/dist/server/terminal/terminal.d.ts +1 -0
  201. package/dist/server/terminal/terminal.d.ts.map +1 -1
  202. package/dist/server/terminal/terminal.js +16 -3
  203. package/dist/server/terminal/terminal.js.map +1 -1
  204. package/dist/server/terminal/worker-terminal-manager.d.ts.map +1 -1
  205. package/dist/server/terminal/worker-terminal-manager.js +8 -0
  206. package/dist/server/terminal/worker-terminal-manager.js.map +1 -1
  207. package/dist/server/utils/checkout-git.d.ts +4 -1
  208. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  209. package/dist/server/utils/checkout-git.js +85 -29
  210. package/dist/server/utils/checkout-git.js.map +1 -1
  211. package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
  212. package/dist/server/utils/directory-suggestions.js +51 -14
  213. package/dist/server/utils/directory-suggestions.js.map +1 -1
  214. package/dist/server/utils/executable.d.ts.map +1 -1
  215. package/dist/server/utils/executable.js +6 -3
  216. package/dist/server/utils/executable.js.map +1 -1
  217. package/dist/server/utils/run-git-command.d.ts +2 -0
  218. package/dist/server/utils/run-git-command.d.ts.map +1 -1
  219. package/dist/server/utils/run-git-command.js +41 -1
  220. package/dist/server/utils/run-git-command.js.map +1 -1
  221. package/dist/server/utils/worktree.js +1 -1
  222. package/dist/server/utils/worktree.js.map +1 -1
  223. package/dist/src/server/agent/agent-sdk-types.js +10 -0
  224. package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
  225. package/dist/src/server/agent/provider-manifest.js +17 -3
  226. package/dist/src/server/agent/provider-manifest.js.map +1 -1
  227. package/dist/src/server/persisted-config.js +3 -0
  228. package/dist/src/server/persisted-config.js.map +1 -1
  229. package/dist/src/shared/messages.js +93 -3
  230. package/dist/src/shared/messages.js.map +1 -1
  231. package/dist/src/utils/executable.js +6 -3
  232. package/dist/src/utils/executable.js.map +1 -1
  233. package/package.json +3 -3
@@ -1,3 +1,4 @@
1
+ import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
1
2
  import { homedir } from "node:os";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import * as fsSync from "node:fs";
@@ -13,8 +14,9 @@ import { findExecutable, isCommandAvailable } from "../../../utils/executable.js
13
14
  import { spawnProcess } from "../../../utils/spawn.js";
14
15
  import { extractCodexTerminalSessionId, nonEmptyString } from "./tool-call-mapper-utils.js";
15
16
  import { buildCodexFeatures, codexModelSupportsFastMode } from "./codex-feature-definitions.js";
16
- import { CodexAppServerClient } from "./codex/app-server-transport.js";
17
+ import { CodexAppServerClient, } from "./codex/app-server-transport.js";
17
18
  import { renderProviderImageOutputAsAssistantMarkdown, } from "./provider-image-output.js";
19
+ import { normalizeProviderReplayTimestamp } from "../provider-history-timestamps.js";
18
20
  import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
19
21
  import { runProviderTurn } from "./provider-runner.js";
20
22
  function assertChildWithPipes(child) {
@@ -37,11 +39,13 @@ const CODEX_TOOL_THREAD_ITEM_TYPES = new Set([
37
39
  "webSearch",
38
40
  "collabAgentToolCall",
39
41
  ]);
42
+ const CODEX_CONTEXT_COMPACTION_TYPE = "contextCompaction";
40
43
  const CODEX_PLAN_IMPLEMENTATION_PROMPT_PREFIX = "The user approved the plan. Implement it now. Do not restate or revise the plan unless blocked.";
41
44
  // Codex's experimental `goals` feature ships in 0.128.0+. Older binaries reject
42
45
  // `--enable goals` at launch, so we gate by version and silently skip the flag
43
46
  // (and the /goal slash command) when the binary is too old.
44
47
  const CODEX_GOALS_MIN_VERSION = [0, 128, 0];
48
+ const CODEX_AUTO_REVIEW_MIN_VERSION = [0, 115, 0];
45
49
  function parseCodexVersion(versionOutput) {
46
50
  const match = versionOutput.match(/(\d+)\.(\d+)\.(\d+)/);
47
51
  if (!match)
@@ -90,6 +94,11 @@ const CODEX_MODES = [
90
94
  label: "Default Permissions",
91
95
  description: "Edit files and run commands with Codex's default approval flow.",
92
96
  },
97
+ {
98
+ id: "auto-review",
99
+ label: "Auto-review",
100
+ description: "Same workspace-write permissions as Default, but eligible `on-request` approvals are routed through the auto-reviewer subagent.",
101
+ },
93
102
  {
94
103
  id: "full-access",
95
104
  label: "Full Access",
@@ -106,12 +115,30 @@ const MODE_PRESETS = {
106
115
  approvalPolicy: "on-request",
107
116
  sandbox: "workspace-write",
108
117
  },
118
+ "auto-review": {
119
+ approvalPolicy: "on-request",
120
+ sandbox: "workspace-write",
121
+ approvalsReviewer: "auto_review",
122
+ },
109
123
  "full-access": {
110
124
  approvalPolicy: "never",
111
125
  sandbox: "danger-full-access",
112
126
  networkAccess: true,
113
127
  },
114
128
  };
129
+ function isAutoReviewReviewer(value) {
130
+ return value === "auto_review" || value === "guardian_subagent";
131
+ }
132
+ function applyApprovalsReviewerParam(params, preset) {
133
+ if (preset.approvalsReviewer) {
134
+ params.approvalsReviewer = preset.approvalsReviewer;
135
+ }
136
+ }
137
+ function shouldPromoteThreadResponseToAutoReview(params) {
138
+ return (isAutoReviewReviewer(params.approvalsReviewer) &&
139
+ params.approvalPolicy === "on-request" &&
140
+ params.sandbox === "workspace-write");
141
+ }
115
142
  function validateCodexMode(modeId) {
116
143
  if (!(modeId in MODE_PRESETS)) {
117
144
  const validModes = Object.keys(MODE_PRESETS).join(", ");
@@ -1069,6 +1096,29 @@ function firstStringField(record, fields) {
1069
1096
  }
1070
1097
  return null;
1071
1098
  }
1099
+ function readCodexHistoryTimestamp(item) {
1100
+ const record = toObjectRecord(item);
1101
+ if (!record) {
1102
+ return null;
1103
+ }
1104
+ return (normalizeProviderReplayTimestamp(record.timestamp) ??
1105
+ normalizeProviderReplayTimestamp(record.createdAt) ??
1106
+ normalizeProviderReplayTimestamp(record.created_at));
1107
+ }
1108
+ function readCodexTurnHistoryTimestamp(turn, timelineItem) {
1109
+ const record = toObjectRecord(turn);
1110
+ if (!record) {
1111
+ return null;
1112
+ }
1113
+ const startedAt = normalizeProviderReplayTimestamp(record.startedAt) ??
1114
+ normalizeProviderReplayTimestamp(record.started_at);
1115
+ const completedAt = normalizeProviderReplayTimestamp(record.completedAt) ??
1116
+ normalizeProviderReplayTimestamp(record.completed_at);
1117
+ if (timelineItem.type === "user_message") {
1118
+ return startedAt ?? completedAt;
1119
+ }
1120
+ return completedAt ?? startedAt;
1121
+ }
1072
1122
  function codexImageOutputFromResult(result) {
1073
1123
  if (typeof result === "string") {
1074
1124
  const trimmed = result.trim();
@@ -1138,15 +1188,23 @@ function threadItemToTimeline(item, options) {
1138
1188
  switch (normalizedType) {
1139
1189
  case "userMessage":
1140
1190
  return mapCodexThreadUserMessageItem(normalizedItem, includeUserMessage);
1141
- case "agentMessage":
1191
+ case "agentMessage": {
1192
+ const messageId = nonEmptyString(normalizedItem.id);
1142
1193
  return {
1143
1194
  type: "assistant_message",
1144
1195
  text: typeof normalizedItem.text === "string" ? normalizedItem.text : "",
1196
+ ...(messageId ? { messageId } : {}),
1145
1197
  };
1198
+ }
1146
1199
  case "plan":
1147
1200
  return mapCodexThreadPlanItem(normalizedItem);
1148
1201
  case "reasoning":
1149
1202
  return mapCodexThreadReasoningItem(normalizedItem);
1203
+ case CODEX_CONTEXT_COMPACTION_TYPE:
1204
+ return {
1205
+ type: "compaction",
1206
+ status: "completed",
1207
+ };
1150
1208
  default:
1151
1209
  return null;
1152
1210
  }
@@ -1178,7 +1236,11 @@ async function loadCodexThreadHistoryTimeline(params) {
1178
1236
  for (const item of turn.items) {
1179
1237
  const timelineItem = threadItemToTimeline(item, { cwd: params.cwd });
1180
1238
  if (timelineItem) {
1181
- timeline.push(timelineItem);
1239
+ const timestamp = readCodexHistoryTimestamp(item) ?? readCodexTurnHistoryTimestamp(turn, timelineItem);
1240
+ timeline.push({
1241
+ item: timelineItem,
1242
+ timestamp: timestamp ?? undefined,
1243
+ });
1182
1244
  }
1183
1245
  }
1184
1246
  }
@@ -1295,6 +1357,12 @@ const ItemLifecycleNotificationSchema = z
1295
1357
  .passthrough(),
1296
1358
  })
1297
1359
  .passthrough();
1360
+ const ContextCompactedNotificationSchema = z
1361
+ .object({
1362
+ threadId: z.string(),
1363
+ turnId: z.string().optional(),
1364
+ })
1365
+ .passthrough();
1298
1366
  const CodexEventTurnAbortedNotificationSchema = z
1299
1367
  .object({
1300
1368
  msg: z
@@ -1510,6 +1578,18 @@ const CodexNotificationSchema = z.union([
1510
1578
  method,
1511
1579
  params,
1512
1580
  })),
1581
+ z
1582
+ .object({ method: z.literal("thread/compacted"), params: ContextCompactedNotificationSchema })
1583
+ .transform(({ params }) => ({
1584
+ kind: "context_compacted",
1585
+ threadId: params.threadId,
1586
+ turnId: params.turnId ?? null,
1587
+ })),
1588
+ z.object({ method: z.literal("thread/compacted"), params: z.unknown() }).transform(({ method, params }) => ({
1589
+ kind: "invalid_payload",
1590
+ method,
1591
+ params,
1592
+ })),
1513
1593
  z
1514
1594
  .object({
1515
1595
  method: z.literal("item/agentMessage/delta"),
@@ -1911,13 +1991,54 @@ function buildCodexAppServerInitializeParams() {
1911
1991
  },
1912
1992
  };
1913
1993
  }
1994
+ function normalizeOpenAICompatibleBaseUrl(value) {
1995
+ const trimmed = value.trim();
1996
+ if (!trimmed) {
1997
+ return null;
1998
+ }
1999
+ const withoutTrailingSlashes = trimmed.replace(/\/+$/u, "");
2000
+ if (withoutTrailingSlashes.endsWith("/v1")) {
2001
+ return withoutTrailingSlashes;
2002
+ }
2003
+ return `${withoutTrailingSlashes}/v1`;
2004
+ }
2005
+ function buildCodexCustomProviderConfig(runtimeSettings, customProvider) {
2006
+ if (customProvider?.extends !== CODEX_PROVIDER) {
2007
+ return null;
2008
+ }
2009
+ const baseUrl = runtimeSettings?.env?.OPENAI_BASE_URL;
2010
+ if (typeof baseUrl !== "string") {
2011
+ return null;
2012
+ }
2013
+ const normalizedBaseUrl = normalizeOpenAICompatibleBaseUrl(baseUrl);
2014
+ if (!normalizedBaseUrl) {
2015
+ return null;
2016
+ }
2017
+ const providerConfig = {
2018
+ name: customProvider.label,
2019
+ base_url: normalizedBaseUrl,
2020
+ wire_api: "responses",
2021
+ };
2022
+ if (runtimeSettings?.env?.OPENAI_API_KEY?.trim()) {
2023
+ providerConfig.env_key = "OPENAI_API_KEY";
2024
+ providerConfig.requires_openai_auth = false;
2025
+ }
2026
+ return {
2027
+ model_provider: customProvider.id,
2028
+ model_providers: {
2029
+ [customProvider.id]: providerConfig,
2030
+ },
2031
+ };
2032
+ }
1914
2033
  class CodexAppServerAgentSession {
1915
- constructor(config, resumeHandle, logger, spawnAppServer, deps = {}, ephemeral = false, goalsEnabled = false) {
2034
+ constructor(config, resumeHandle, logger, spawnAppServer, deps = {}, ephemeral = false, goalsEnabled = false, autoReviewEnabled = false, agentId) {
1916
2035
  this.resumeHandle = resumeHandle;
1917
2036
  this.spawnAppServer = spawnAppServer;
1918
2037
  this.deps = deps;
1919
2038
  this.ephemeral = ephemeral;
1920
2039
  this.goalsEnabled = goalsEnabled;
2040
+ this.autoReviewEnabled = autoReviewEnabled;
2041
+ this.agentId = agentId;
1921
2042
  this.provider = CODEX_PROVIDER;
1922
2043
  this.capabilities = CODEX_APP_SERVER_CAPABILITIES;
1923
2044
  this.currentThreadId = null;
@@ -1952,11 +2073,21 @@ class CodexAppServerAgentSession {
1952
2073
  this.warnedInvalidNotificationPayloads = new Set();
1953
2074
  this.warnedIncompleteEditToolCallIds = new Set();
1954
2075
  this.latestPlanResult = null;
2076
+ this.pendingManualCompactionStarts = 0;
2077
+ this.compactionTriggerByItemId = new Map();
2078
+ // Codex can report one completed compaction through both channels:
2079
+ // `thread/compacted` and a completed `contextCompaction` item.
2080
+ this.unpairedCompactionNotificationCompletions = 0;
2081
+ this.unpairedCompactionItemCompletions = 0;
1955
2082
  this.connected = false;
1956
2083
  this.collaborationModes = [];
1957
2084
  this.resolvedCollaborationMode = null;
1958
2085
  this.cachedSkills = [];
1959
- this.logger = logger.child({ module: "agent", provider: CODEX_PROVIDER });
2086
+ this.logger = logger.child({
2087
+ module: "agent",
2088
+ provider: CODEX_PROVIDER,
2089
+ agentId: this.agentId,
2090
+ });
1960
2091
  if (config.modeId === undefined) {
1961
2092
  throw new Error("Codex agent requires modeId to be specified");
1962
2093
  }
@@ -1964,7 +2095,7 @@ class CodexAppServerAgentSession {
1964
2095
  this.currentMode = config.modeId;
1965
2096
  this.config = config;
1966
2097
  this.config.thinkingOptionId = normalizeCodexThinkingOptionId(this.config.thinkingOptionId);
1967
- if (this.config.featureValues?.fast_mode) {
2098
+ if (this.config.featureValues?.fast_mode && codexModelSupportsFastMode(this.config.model)) {
1968
2099
  this.serviceTier = "fast";
1969
2100
  }
1970
2101
  if (this.config.featureValues?.plan_mode) {
@@ -1990,7 +2121,7 @@ class CodexAppServerAgentSession {
1990
2121
  if (this.connected)
1991
2122
  return;
1992
2123
  const child = await this.spawnAppServer();
1993
- this.client = new CodexAppServerClient(child, this.logger);
2124
+ this.client = new CodexAppServerClient(child, this.logger, () => this.traceContext());
1994
2125
  this.client.setNotificationHandler((method, params) => this.handleNotification(method, params));
1995
2126
  this.registerRequestHandlers();
1996
2127
  await this.client.request("initialize", buildCodexAppServerInitializeParams());
@@ -2003,6 +2134,13 @@ class CodexAppServerAgentSession {
2003
2134
  }
2004
2135
  this.connected = true;
2005
2136
  }
2137
+ traceContext() {
2138
+ return {
2139
+ agentId: this.agentId,
2140
+ sessionId: this.currentThreadId ?? undefined,
2141
+ turnId: this.activeForegroundTurnId ?? undefined,
2142
+ };
2143
+ }
2006
2144
  async loadCollaborationModes() {
2007
2145
  if (!this.client)
2008
2146
  return;
@@ -2023,7 +2161,13 @@ class CodexAppServerAgentSession {
2023
2161
  });
2024
2162
  }
2025
2163
  catch (error) {
2026
- this.logger.trace({ error }, "Failed to load collaboration modes");
2164
+ this.logger.trace({
2165
+ agentId: this.agentId,
2166
+ provider: CODEX_PROVIDER,
2167
+ sessionId: this.currentThreadId,
2168
+ turnId: this.activeForegroundTurnId ?? undefined,
2169
+ error,
2170
+ }, "provider.codex.metadata.collaboration_modes_failed");
2027
2171
  this.collaborationModes = [];
2028
2172
  }
2029
2173
  this.refreshResolvedCollaborationMode();
@@ -2036,7 +2180,7 @@ class CodexAppServerAgentSession {
2036
2180
  cwd: [this.config.cwd],
2037
2181
  }));
2038
2182
  const entries = Array.isArray(response?.data) ? response.data : [];
2039
- const skills = [];
2183
+ const skillsByName = new Map();
2040
2184
  for (const entry of entries) {
2041
2185
  const entryRecord = toObjectRecord(entry);
2042
2186
  const list = Array.isArray(entryRecord?.skills) ? entryRecord.skills : [];
@@ -2044,17 +2188,25 @@ class CodexAppServerAgentSession {
2044
2188
  const skillRecord = toObjectRecord(skill);
2045
2189
  if (typeof skillRecord?.name !== "string" || typeof skillRecord?.path !== "string")
2046
2190
  continue;
2047
- skills.push({
2048
- name: skillRecord.name,
2049
- description: resolveSkillDescription(skillRecord),
2050
- path: skillRecord.path,
2051
- });
2191
+ if (!skillsByName.has(skillRecord.name)) {
2192
+ skillsByName.set(skillRecord.name, {
2193
+ name: skillRecord.name,
2194
+ description: resolveSkillDescription(skillRecord),
2195
+ path: skillRecord.path,
2196
+ });
2197
+ }
2052
2198
  }
2053
2199
  }
2054
- this.cachedSkills = skills;
2200
+ this.cachedSkills = Array.from(skillsByName.values());
2055
2201
  }
2056
2202
  catch (error) {
2057
- this.logger.trace({ error }, "Failed to load skills list");
2203
+ this.logger.trace({
2204
+ agentId: this.agentId,
2205
+ provider: CODEX_PROVIDER,
2206
+ sessionId: this.currentThreadId,
2207
+ turnId: this.activeForegroundTurnId ?? undefined,
2208
+ error,
2209
+ }, "provider.codex.metadata.skills_failed");
2058
2210
  this.cachedSkills = [];
2059
2211
  }
2060
2212
  }
@@ -2212,9 +2364,10 @@ class CodexAppServerAgentSession {
2212
2364
  await this.client.request("thread/resume", params);
2213
2365
  }
2214
2366
  catch (error) {
2215
- this.logger.warn({ error }, "Failed to resume Codex thread, starting new thread");
2216
- this.currentThreadId = null;
2217
- await this.ensureThread();
2367
+ const threadId = this.currentThreadId;
2368
+ const message = error instanceof Error ? error.message : String(error);
2369
+ this.logger.warn({ error, threadId }, "Failed to resume persisted Codex thread");
2370
+ throw new Error(`Failed to resume Codex thread ${threadId}: ${message}`, { cause: error });
2218
2371
  }
2219
2372
  }
2220
2373
  parseSlashCommandInput(text) {
@@ -2265,56 +2418,18 @@ class CodexAppServerAgentSession {
2265
2418
  }
2266
2419
  const skill = this.cachedSkills.find((entry) => entry.name === commandName);
2267
2420
  if (skill) {
2421
+ const trimmedArgs = args?.trim() ?? "";
2422
+ const text = trimmedArgs ? `$${skill.name} ${trimmedArgs}` : `$${skill.name}`;
2268
2423
  const input = [
2269
2424
  { type: "skill", name: skill.name, path: skill.path },
2425
+ { type: "text", text },
2270
2426
  ];
2271
- if (args && args.trim().length > 0) {
2272
- input.push({ type: "text", text: args.trim() });
2273
- }
2274
- else {
2275
- input.push({ type: "text", text: `$${skill.name}` });
2276
- }
2277
2427
  return input;
2278
2428
  }
2279
2429
  return args ? `$${commandName} ${args}` : `$${commandName}`;
2280
2430
  }
2281
- async run(prompt, options) {
2282
- return runProviderTurn({
2283
- prompt,
2284
- runOptions: options,
2285
- startTurn: (p, o) => this.startTurn(p, o),
2286
- subscribe: (callback) => this.subscribe(callback),
2287
- getSessionId: async () => (await this.getRuntimeInfo()).sessionId ?? "",
2288
- reduceFinalText: ({ current, item }) => {
2289
- if (item.type === "assistant_message") {
2290
- return item.text;
2291
- }
2292
- if (item.type === "tool_call" && item.detail.type === "plan") {
2293
- return item.detail.text;
2294
- }
2295
- return current;
2296
- },
2297
- });
2298
- }
2299
- async startTurn(prompt, options) {
2300
- if (this.activeForegroundTurnId) {
2301
- throw new Error("A foreground turn is already active");
2302
- }
2303
- await this.connect();
2304
- if (!this.client) {
2305
- throw new Error("Codex client not initialized");
2306
- }
2307
- const slashCommand = await this.resolveSlashCommandInvocation(prompt);
2308
- const effectivePrompt = slashCommand
2309
- ? await this.buildCommandPromptInput(slashCommand.commandName, slashCommand.args)
2310
- : prompt;
2311
- if (this.currentThreadId) {
2312
- await this.ensureThreadLoaded();
2313
- }
2314
- else {
2315
- await this.ensureThread();
2316
- }
2317
- const input = await this.buildUserInput(effectivePrompt);
2431
+ async buildTurnStartParams(prompt, options) {
2432
+ const input = await this.buildUserInput(prompt);
2318
2433
  const preset = MODE_PRESETS[this.currentMode] ?? MODE_PRESETS[DEFAULT_CODEX_MODE_ID];
2319
2434
  const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
2320
2435
  const sandboxPolicyType = this.config.sandboxMode ?? preset.sandbox;
@@ -2326,6 +2441,7 @@ class CodexAppServerAgentSession {
2326
2441
  ? this.config.networkAccess
2327
2442
  : preset.networkAccess),
2328
2443
  };
2444
+ applyApprovalsReviewerParam(params, preset);
2329
2445
  if (this.config.model) {
2330
2446
  params.model = this.config.model;
2331
2447
  }
@@ -2355,10 +2471,81 @@ class CodexAppServerAgentSession {
2355
2471
  if (codexConfig) {
2356
2472
  params.config = codexConfig;
2357
2473
  }
2474
+ return {
2475
+ params,
2476
+ thinkingOptionId,
2477
+ approvalPolicy,
2478
+ sandboxPolicyType,
2479
+ hasOutputSchema: Boolean(options?.outputSchema),
2480
+ hasCodexConfig: Boolean(codexConfig),
2481
+ };
2482
+ }
2483
+ logTurnStartSummary({ turnId, thinkingOptionId, approvalPolicy, sandboxPolicyType, hasOutputSchema, hasCodexConfig, }) {
2484
+ this.logger.info({
2485
+ turnId,
2486
+ threadId: this.currentThreadId,
2487
+ model: this.config.model ?? null,
2488
+ modeId: this.currentMode ?? null,
2489
+ effort: thinkingOptionId ?? null,
2490
+ serviceTier: this.serviceTier,
2491
+ cwd: this.config.cwd ?? null,
2492
+ approvalPolicy,
2493
+ sandboxPolicyType,
2494
+ hasCollaborationMode: Boolean(this.resolvedCollaborationMode),
2495
+ hasOutputSchema,
2496
+ hasDeveloperInstructions: Boolean(this.config.systemPrompt?.trim()),
2497
+ hasCodexConfig,
2498
+ }, "Starting Codex app-server turn");
2499
+ }
2500
+ async run(prompt, options) {
2501
+ return runProviderTurn({
2502
+ prompt,
2503
+ runOptions: options,
2504
+ startTurn: (p, o) => this.startTurn(p, o),
2505
+ subscribe: (callback) => this.subscribe(callback),
2506
+ getSessionId: async () => (await this.getRuntimeInfo()).sessionId ?? "",
2507
+ reduceFinalText: ({ current, item }) => {
2508
+ if (item.type === "assistant_message") {
2509
+ return item.text;
2510
+ }
2511
+ if (item.type === "tool_call" && item.detail.type === "plan") {
2512
+ return item.detail.text;
2513
+ }
2514
+ return current;
2515
+ },
2516
+ });
2517
+ }
2518
+ async startTurn(prompt, options) {
2519
+ if (this.activeForegroundTurnId) {
2520
+ throw new Error("A foreground turn is already active");
2521
+ }
2522
+ await this.connect();
2523
+ if (!this.client) {
2524
+ throw new Error("Codex client not initialized");
2525
+ }
2526
+ const slashCommand = await this.resolveSlashCommandInvocation(prompt);
2527
+ const effectivePrompt = slashCommand
2528
+ ? await this.buildCommandPromptInput(slashCommand.commandName, slashCommand.args)
2529
+ : prompt;
2530
+ if (this.currentThreadId) {
2531
+ await this.ensureThreadLoaded();
2532
+ }
2533
+ else {
2534
+ await this.ensureThread();
2535
+ }
2536
+ const turnStart = await this.buildTurnStartParams(effectivePrompt, options);
2358
2537
  const turnId = this.createTurnId();
2359
2538
  this.activeForegroundTurnId = turnId;
2360
2539
  try {
2361
- await this.client.request("turn/start", params, TURN_START_TIMEOUT_MS);
2540
+ this.logTurnStartSummary({
2541
+ turnId,
2542
+ thinkingOptionId: turnStart.thinkingOptionId,
2543
+ approvalPolicy: turnStart.approvalPolicy,
2544
+ sandboxPolicyType: turnStart.sandboxPolicyType,
2545
+ hasOutputSchema: turnStart.hasOutputSchema,
2546
+ hasCodexConfig: turnStart.hasCodexConfig,
2547
+ });
2548
+ await this.client.request("turn/start", turnStart.params, TURN_START_TIMEOUT_MS);
2362
2549
  }
2363
2550
  catch (error) {
2364
2551
  this.activeForegroundTurnId = null;
@@ -2379,8 +2566,13 @@ class CodexAppServerAgentSession {
2379
2566
  const history = this.persistedHistory;
2380
2567
  this.persistedHistory = [];
2381
2568
  this.historyPending = false;
2382
- for (const item of history) {
2383
- yield { type: "timeline", provider: CODEX_PROVIDER, item };
2569
+ for (const entry of history) {
2570
+ yield {
2571
+ type: "timeline",
2572
+ provider: CODEX_PROVIDER,
2573
+ item: entry.item,
2574
+ timestamp: entry.timestamp,
2575
+ };
2384
2576
  }
2385
2577
  }
2386
2578
  async getRuntimeInfo() {
@@ -2406,7 +2598,10 @@ class CodexAppServerAgentSession {
2406
2598
  return { ...info };
2407
2599
  }
2408
2600
  async getAvailableModes() {
2409
- return CODEX_MODES;
2601
+ if (this.autoReviewEnabled) {
2602
+ return CODEX_MODES;
2603
+ }
2604
+ return CODEX_MODES.filter((mode) => mode.id !== "auto-review");
2410
2605
  }
2411
2606
  async getCurrentMode() {
2412
2607
  return this.currentMode ?? null;
@@ -2431,6 +2626,9 @@ class CodexAppServerAgentSession {
2431
2626
  }
2432
2627
  async setFeature(featureId, value) {
2433
2628
  if (featureId === "fast_mode") {
2629
+ if (Boolean(value) && !codexModelSupportsFastMode(this.config.model)) {
2630
+ throw new Error(`Codex fast mode is not available for model '${this.config.model ?? "default"}'`);
2631
+ }
2434
2632
  this.applyFeatureValue("fast_mode", Boolean(value));
2435
2633
  return;
2436
2634
  }
@@ -2635,7 +2833,13 @@ class CodexAppServerAgentSession {
2635
2833
  const fallbackSkills = appServerSkills.length === 0
2636
2834
  ? await listCodexSkills(this.config.cwd, this.deps.workspaceGitService)
2637
2835
  : [];
2638
- const builtin = [];
2836
+ const builtin = [
2837
+ {
2838
+ name: "compact",
2839
+ description: "Summarize conversation to prevent hitting the context limit",
2840
+ argumentHint: "",
2841
+ },
2842
+ ];
2639
2843
  if (this.goalsEnabled) {
2640
2844
  builtin.push({
2641
2845
  name: "goal",
@@ -2646,12 +2850,26 @@ class CodexAppServerAgentSession {
2646
2850
  return [...builtin, ...appServerSkills, ...fallbackSkills, ...prompts].sort((a, b) => a.name.localeCompare(b.name));
2647
2851
  }
2648
2852
  tryHandleOutOfBand(prompt) {
2649
- if (!this.goalsEnabled)
2650
- return null;
2651
2853
  if (typeof prompt !== "string")
2652
2854
  return null;
2653
2855
  const parsed = this.parseSlashCommandInput(prompt);
2654
- if (!parsed || parsed.commandName !== "goal")
2856
+ if (!parsed)
2857
+ return null;
2858
+ if (parsed.commandName === "compact") {
2859
+ return {
2860
+ run: async ({ emit }) => {
2861
+ const error = await this.executeCompactCommand();
2862
+ if (error) {
2863
+ emit({
2864
+ type: "timeline",
2865
+ provider: CODEX_PROVIDER,
2866
+ item: { type: "assistant_message", text: formatOutOfBandStatusMessage(error) },
2867
+ });
2868
+ }
2869
+ },
2870
+ };
2871
+ }
2872
+ if (!this.goalsEnabled || parsed.commandName !== "goal")
2655
2873
  return null;
2656
2874
  const subcommand = parseGoalSubcommand(parsed.args);
2657
2875
  return {
@@ -2665,6 +2883,35 @@ class CodexAppServerAgentSession {
2665
2883
  },
2666
2884
  };
2667
2885
  }
2886
+ async executeCompactCommand() {
2887
+ try {
2888
+ await this.connect();
2889
+ if (this.currentThreadId) {
2890
+ await this.ensureThreadLoaded();
2891
+ }
2892
+ else {
2893
+ await this.ensureThread();
2894
+ }
2895
+ if (!this.client || !this.currentThreadId) {
2896
+ throw new Error("Codex thread is not available");
2897
+ }
2898
+ this.pendingManualCompactionStarts += 1;
2899
+ try {
2900
+ await this.client.request("thread/compact/start", {
2901
+ threadId: this.currentThreadId,
2902
+ });
2903
+ }
2904
+ catch (error) {
2905
+ this.pendingManualCompactionStarts = Math.max(0, this.pendingManualCompactionStarts - 1);
2906
+ throw error;
2907
+ }
2908
+ return null;
2909
+ }
2910
+ catch (error) {
2911
+ const message = error instanceof Error ? error.message : "unknown error";
2912
+ return `Failed to compact context: ${message}`;
2913
+ }
2914
+ }
2668
2915
  async executeGoalSubcommand(subcommand) {
2669
2916
  if (subcommand.kind === "usage") {
2670
2917
  return "Usage: /goal <objective>|pause|resume|clear";
@@ -2776,7 +3023,7 @@ class CodexAppServerAgentSession {
2776
3023
  const approvalPolicy = this.config.approvalPolicy ?? preset.approvalPolicy;
2777
3024
  const sandbox = this.config.sandboxMode ?? preset.sandbox;
2778
3025
  const innerConfig = this.buildCodexInnerConfig();
2779
- const rawResponse = await this.client.request("thread/start", {
3026
+ const params = {
2780
3027
  model,
2781
3028
  cwd: this.config.cwd ?? null,
2782
3029
  approvalPolicy,
@@ -2786,13 +3033,24 @@ class CodexAppServerAgentSession {
2786
3033
  : {}),
2787
3034
  ...(innerConfig ? { config: innerConfig } : {}),
2788
3035
  ...(this.ephemeral ? { ephemeral: true } : {}),
2789
- });
3036
+ };
3037
+ applyApprovalsReviewerParam(params, preset);
3038
+ const rawResponse = await this.client.request("thread/start", params);
2790
3039
  const response = toObjectRecord(rawResponse);
2791
3040
  const threadRecord = toObjectRecord(response?.thread);
2792
3041
  const threadId = typeof threadRecord?.id === "string" ? threadRecord.id : undefined;
2793
3042
  if (!threadId) {
2794
3043
  throw new Error("Codex app-server did not return thread id");
2795
3044
  }
3045
+ const responseApprovalsReviewer = typeof response?.approvalsReviewer === "string" ? response.approvalsReviewer : undefined;
3046
+ if (shouldPromoteThreadResponseToAutoReview({
3047
+ approvalsReviewer: responseApprovalsReviewer,
3048
+ approvalPolicy,
3049
+ sandbox,
3050
+ })) {
3051
+ this.currentMode = "auto-review";
3052
+ this.cachedRuntimeInfo = null;
3053
+ }
2796
3054
  this.currentThreadId = threadId;
2797
3055
  }
2798
3056
  buildCodexInnerConfig() {
@@ -2807,6 +3065,9 @@ class CodexAppServerAgentSession {
2807
3065
  if (this.config.extra?.codex) {
2808
3066
  Object.assign(innerConfig, this.config.extra.codex);
2809
3067
  }
3068
+ if (this.deps.customCodexConfig) {
3069
+ Object.assign(innerConfig, this.deps.customCodexConfig);
3070
+ }
2810
3071
  return Object.keys(innerConfig).length > 0 ? innerConfig : null;
2811
3072
  }
2812
3073
  async buildUserInput(prompt) {
@@ -2821,6 +3082,13 @@ class CodexAppServerAgentSession {
2821
3082
  notifySubscribers(event) {
2822
3083
  const turnId = this.activeForegroundTurnId;
2823
3084
  const tagged = turnId ? { ...event, turnId } : event;
3085
+ this.logger.trace({
3086
+ agentId: this.agentId,
3087
+ provider: CODEX_PROVIDER,
3088
+ sessionId: this.currentThreadId,
3089
+ turnId: getAgentStreamEventTurnId(tagged),
3090
+ event: tagged,
3091
+ }, "provider.codex.event_emit");
2824
3092
  for (const callback of this.subscribers) {
2825
3093
  try {
2826
3094
  callback(tagged);
@@ -2835,6 +3103,7 @@ class CodexAppServerAgentSession {
2835
3103
  }
2836
3104
  handleNotification(method, params) {
2837
3105
  const parsed = CodexNotificationSchema.parse({ method, params });
3106
+ this.traceParsedNotification(method, params, parsed);
2838
3107
  switch (parsed.kind) {
2839
3108
  case "thread_started":
2840
3109
  this.handleThreadStartedNotification(parsed);
@@ -2856,6 +3125,9 @@ class CodexAppServerAgentSession {
2856
3125
  case "token_usage_updated":
2857
3126
  this.handleTokenUsageUpdatedNotification(parsed);
2858
3127
  return;
3128
+ case "context_compacted":
3129
+ this.handleContextCompactedNotification(parsed);
3130
+ return;
2859
3131
  case "agent_message_delta":
2860
3132
  case "reasoning_delta":
2861
3133
  case "exec_command_output_delta":
@@ -2890,6 +3162,17 @@ class CodexAppServerAgentSession {
2890
3162
  this.warnUnknownNotificationMethod(parsed.method, parsed.params);
2891
3163
  }
2892
3164
  }
3165
+ traceParsedNotification(method, params, parsed) {
3166
+ this.logger.trace({
3167
+ agentId: this.agentId,
3168
+ provider: CODEX_PROVIDER,
3169
+ sessionId: this.currentThreadId,
3170
+ turnId: this.activeForegroundTurnId ?? undefined,
3171
+ method,
3172
+ params,
3173
+ parsed,
3174
+ }, "provider.codex.parsed_event");
3175
+ }
2893
3176
  getSubAgentCallIdForThread(threadId) {
2894
3177
  if (!threadId || threadId === this.currentThreadId) {
2895
3178
  return null;
@@ -2998,6 +3281,7 @@ class CodexAppServerAgentSession {
2998
3281
  if (subAgentCallId) {
2999
3282
  this.upsertSubAgentChildItem(subAgentCallId, parsed.itemId, {
3000
3283
  type: "assistant_message",
3284
+ messageId: parsed.itemId,
3001
3285
  text,
3002
3286
  });
3003
3287
  this.emitSubAgentActivityUpdate(subAgentCallId, "running");
@@ -3009,6 +3293,7 @@ class CodexAppServerAgentSession {
3009
3293
  provider: CODEX_PROVIDER,
3010
3294
  item: {
3011
3295
  type: "assistant_message",
3296
+ messageId: parsed.itemId,
3012
3297
  text: isFirstDeltaForItem && this.pendingAssistantMessageBoundary
3013
3298
  ? `${ASSISTANT_MESSAGE_BOUNDARY_MARKDOWN}${parsed.delta}`
3014
3299
  : parsed.delta,
@@ -3113,6 +3398,8 @@ class CodexAppServerAgentSession {
3113
3398
  this.pendingFileChangeOutputDeltas.clear();
3114
3399
  this.pendingAssistantMessageBoundary = false;
3115
3400
  this.warnedIncompleteEditToolCallIds.clear();
3401
+ this.unpairedCompactionNotificationCompletions = 0;
3402
+ this.unpairedCompactionItemCompletions = 0;
3116
3403
  }
3117
3404
  handlePlanUpdatedNotification(parsed) {
3118
3405
  const timelineItem = mapCodexPlanToToolCall({
@@ -3143,6 +3430,55 @@ class CodexAppServerAgentSession {
3143
3430
  });
3144
3431
  }
3145
3432
  }
3433
+ resolveContextCompactionTrigger(itemId) {
3434
+ if (itemId) {
3435
+ const known = this.compactionTriggerByItemId.get(itemId);
3436
+ if (known) {
3437
+ return known;
3438
+ }
3439
+ }
3440
+ if (this.pendingManualCompactionStarts > 0) {
3441
+ this.pendingManualCompactionStarts -= 1;
3442
+ return "manual";
3443
+ }
3444
+ return undefined;
3445
+ }
3446
+ createContextCompactionTimelineItem(status, itemId) {
3447
+ const trigger = this.resolveContextCompactionTrigger(itemId);
3448
+ if (itemId && trigger) {
3449
+ if (status === "loading") {
3450
+ this.compactionTriggerByItemId.set(itemId, trigger);
3451
+ }
3452
+ else {
3453
+ this.compactionTriggerByItemId.delete(itemId);
3454
+ }
3455
+ }
3456
+ return {
3457
+ type: "compaction",
3458
+ status,
3459
+ ...(trigger ? { trigger } : {}),
3460
+ };
3461
+ }
3462
+ isContextCompactionItem(item) {
3463
+ return (normalizeCodexThreadItemType(typeof item.type === "string" ? item.type : undefined) ===
3464
+ CODEX_CONTEXT_COMPACTION_TYPE);
3465
+ }
3466
+ handleContextCompactedNotification(parsed) {
3467
+ if (parsed.threadId !== this.currentThreadId) {
3468
+ return;
3469
+ }
3470
+ if (this.unpairedCompactionItemCompletions > 0) {
3471
+ this.unpairedCompactionItemCompletions -= 1;
3472
+ return;
3473
+ }
3474
+ this.unpairedCompactionNotificationCompletions += 1;
3475
+ this.emitEvent({
3476
+ type: "timeline",
3477
+ provider: CODEX_PROVIDER,
3478
+ item: this.createContextCompactionTimelineItem("completed"),
3479
+ ...(parsed.turnId ? { turnId: parsed.turnId } : {}),
3480
+ });
3481
+ }
3146
3482
  handleExecCommandStartedNotification(parsed) {
3147
3483
  if (parsed.callId) {
3148
3484
  this.emittedExecCommandStartedCallIds.add(parsed.callId);
@@ -3239,6 +3575,19 @@ class CodexAppServerAgentSession {
3239
3575
  if (parsed.source === "codex_event") {
3240
3576
  return;
3241
3577
  }
3578
+ if (this.isContextCompactionItem(parsed.item)) {
3579
+ if (this.unpairedCompactionNotificationCompletions > 0) {
3580
+ this.unpairedCompactionNotificationCompletions -= 1;
3581
+ return;
3582
+ }
3583
+ this.emitEvent({
3584
+ type: "timeline",
3585
+ provider: CODEX_PROVIDER,
3586
+ item: this.createContextCompactionTimelineItem("completed", parsed.item.id),
3587
+ });
3588
+ this.unpairedCompactionItemCompletions += 1;
3589
+ return;
3590
+ }
3242
3591
  const timelineItem = threadItemToTimeline(parsed.item, {
3243
3592
  includeUserMessage: false,
3244
3593
  cwd: this.config.cwd ?? null,
@@ -3320,7 +3669,13 @@ class CodexAppServerAgentSession {
3320
3669
  this.emitEvent({
3321
3670
  type: "timeline",
3322
3671
  provider: CODEX_PROVIDER,
3323
- item: { type: timelineItem.type, text: suffix },
3672
+ item: timelineItem.type === "assistant_message"
3673
+ ? {
3674
+ type: timelineItem.type,
3675
+ text: suffix,
3676
+ ...(timelineItem.messageId ? { messageId: timelineItem.messageId } : {}),
3677
+ }
3678
+ : { type: timelineItem.type, text: suffix },
3324
3679
  });
3325
3680
  }
3326
3681
  applyBufferedDeltaTextToTimelineItem(timelineItem, itemId) {
@@ -3345,6 +3700,14 @@ class CodexAppServerAgentSession {
3345
3700
  if (parsed.source === "codex_event") {
3346
3701
  return;
3347
3702
  }
3703
+ if (this.isContextCompactionItem(parsed.item)) {
3704
+ this.emitEvent({
3705
+ type: "timeline",
3706
+ provider: CODEX_PROVIDER,
3707
+ item: this.createContextCompactionTimelineItem("loading", parsed.item.id),
3708
+ });
3709
+ return;
3710
+ }
3348
3711
  const timelineItem = threadItemToTimeline(parsed.item, {
3349
3712
  includeUserMessage: false,
3350
3713
  cwd: this.config.cwd ?? null,
@@ -3385,7 +3748,14 @@ class CodexAppServerAgentSession {
3385
3748
  return;
3386
3749
  }
3387
3750
  this.warnedUnknownNotificationMethods.add(method);
3388
- this.logger.trace({ method, params }, "Unhandled Codex app-server notification method");
3751
+ this.logger.trace({
3752
+ agentId: this.agentId,
3753
+ provider: CODEX_PROVIDER,
3754
+ sessionId: this.currentThreadId,
3755
+ turnId: this.activeForegroundTurnId ?? undefined,
3756
+ method,
3757
+ params,
3758
+ }, "provider.codex.event_unhandled");
3389
3759
  }
3390
3760
  warnInvalidNotificationPayload(method, params) {
3391
3761
  const key = method;
@@ -3617,6 +3987,13 @@ export class CodexAppServerAgentClient {
3617
3987
  this.provider = CODEX_PROVIDER;
3618
3988
  this.capabilities = CODEX_APP_SERVER_CAPABILITIES;
3619
3989
  this.goalsEnabledPromise = null;
3990
+ this.autoReviewEnabledPromise = null;
3991
+ }
3992
+ sessionDeps() {
3993
+ return {
3994
+ ...this.deps,
3995
+ customCodexConfig: buildCodexCustomProviderConfig(this.runtimeSettings, this.deps.customProvider),
3996
+ };
3620
3997
  }
3621
3998
  resolveGoalsEnabled() {
3622
3999
  if (!this.goalsEnabledPromise) {
@@ -3625,7 +4002,11 @@ export class CodexAppServerAgentClient {
3625
4002
  const launchPrefix = await resolveCodexLaunchPrefix(this.runtimeSettings);
3626
4003
  const versionOutput = await resolveBinaryVersion(launchPrefix.command);
3627
4004
  const enabled = codexVersionAtLeast(versionOutput, CODEX_GOALS_MIN_VERSION);
3628
- this.logger.trace({ versionOutput, enabled }, "Resolved codex goals feature gate");
4005
+ this.logger.trace({
4006
+ provider: CODEX_PROVIDER,
4007
+ versionOutput,
4008
+ enabled,
4009
+ }, "provider.codex.config.goals_resolved");
3629
4010
  return enabled;
3630
4011
  }
3631
4012
  catch (error) {
@@ -3636,6 +4017,28 @@ export class CodexAppServerAgentClient {
3636
4017
  }
3637
4018
  return this.goalsEnabledPromise;
3638
4019
  }
4020
+ resolveAutoReviewEnabled() {
4021
+ if (!this.autoReviewEnabledPromise) {
4022
+ this.autoReviewEnabledPromise = (async () => {
4023
+ try {
4024
+ const launchPrefix = await resolveCodexLaunchPrefix(this.runtimeSettings);
4025
+ const versionOutput = await resolveBinaryVersion(launchPrefix.command);
4026
+ const enabled = codexVersionAtLeast(versionOutput, CODEX_AUTO_REVIEW_MIN_VERSION);
4027
+ this.logger.trace({
4028
+ provider: CODEX_PROVIDER,
4029
+ versionOutput,
4030
+ enabled,
4031
+ }, "provider.codex.config.auto_review_resolved");
4032
+ return enabled;
4033
+ }
4034
+ catch (error) {
4035
+ this.logger.warn({ err: error }, "Failed to probe codex version for auto-review gate");
4036
+ return false;
4037
+ }
4038
+ })();
4039
+ }
4040
+ return this.autoReviewEnabledPromise;
4041
+ }
3639
4042
  async spawnAppServer(launchEnv, options) {
3640
4043
  const launchPrefix = await resolveCodexLaunchPrefix(this.runtimeSettings);
3641
4044
  const args = [...launchPrefix.args, "app-server"];
@@ -3643,9 +4046,11 @@ export class CodexAppServerAgentClient {
3643
4046
  args.push("--enable", "goals");
3644
4047
  }
3645
4048
  this.logger.trace({
4049
+ agentId: options?.agentId,
4050
+ provider: CODEX_PROVIDER,
3646
4051
  launchPrefix,
3647
4052
  goalsEnabled: options?.goalsEnabled === true,
3648
- }, "Spawning Codex app server");
4053
+ }, "provider.codex.spawn");
3649
4054
  const child = spawnProcess(launchPrefix.command, args, {
3650
4055
  detached: process.platform !== "win32",
3651
4056
  stdio: ["pipe", "pipe", "pipe"],
@@ -3665,7 +4070,8 @@ export class CodexAppServerAgentClient {
3665
4070
  }
3666
4071
  const sessionConfig = { ...config, provider: CODEX_PROVIDER };
3667
4072
  const goalsEnabled = await this.resolveGoalsEnabled();
3668
- const session = new CodexAppServerAgentSession(sessionConfig, null, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled }), this.deps, options?.persistSession === false, goalsEnabled);
4073
+ const autoReviewEnabled = await this.resolveAutoReviewEnabled();
4074
+ const session = new CodexAppServerAgentSession(sessionConfig, null, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled, agentId: launchContext?.agentId }), this.sessionDeps(), options?.persistSession === false, goalsEnabled, autoReviewEnabled, launchContext?.agentId);
3669
4075
  await session.connect();
3670
4076
  return session;
3671
4077
  }
@@ -3678,13 +4084,14 @@ export class CodexAppServerAgentClient {
3678
4084
  cwd: overrides?.cwd ?? storedConfig.cwd ?? process.cwd(),
3679
4085
  };
3680
4086
  const goalsEnabled = await this.resolveGoalsEnabled();
3681
- const session = new CodexAppServerAgentSession(merged, handle, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled }), this.deps, false, goalsEnabled);
4087
+ const autoReviewEnabled = await this.resolveAutoReviewEnabled();
4088
+ const session = new CodexAppServerAgentSession(merged, handle, this.logger, () => this.spawnAppServer(launchContext?.env, { goalsEnabled, agentId: launchContext?.agentId }), this.sessionDeps(), false, goalsEnabled, autoReviewEnabled, launchContext?.agentId);
3682
4089
  await session.connect();
3683
4090
  return session;
3684
4091
  }
3685
4092
  async listPersistedAgents(options) {
3686
4093
  const child = await this.spawnAppServer();
3687
- const client = this.deps._createCodexClient?.(child, this.logger) ??
4094
+ const client = this.deps._createCodexClient?.(child, this.logger, () => ({})) ??
3688
4095
  new CodexAppServerClient(child, this.logger);
3689
4096
  try {
3690
4097
  await client.request("initialize", buildCodexAppServerInitializeParams());
@@ -3734,7 +4141,7 @@ export class CodexAppServerAgentClient {
3734
4141
  threadId,
3735
4142
  },
3736
4143
  },
3737
- timeline,
4144
+ timeline: timeline.map((entry) => entry.item),
3738
4145
  };
3739
4146
  }));
3740
4147
  return descriptors;