@gajae-code/coding-agent 0.16.7 → 0.17.1

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 (321) hide show
  1. package/CHANGELOG.md +180 -1
  2. package/dist/types/capability/fs.d.ts +37 -4
  3. package/dist/types/capability/index.d.ts +19 -0
  4. package/dist/types/capability/types.d.ts +9 -0
  5. package/dist/types/cli/doctor/args.d.ts +36 -0
  6. package/dist/types/cli/doctor/checks.d.ts +18 -0
  7. package/dist/types/cli/doctor/config-repairs.d.ts +38 -0
  8. package/dist/types/cli/doctor/context.d.ts +89 -0
  9. package/dist/types/cli/doctor/files.d.ts +27 -0
  10. package/dist/types/cli/doctor/ids.d.ts +20 -0
  11. package/dist/types/cli/doctor/install-repairs.d.ts +94 -0
  12. package/dist/types/cli/doctor/journal.d.ts +43 -0
  13. package/dist/types/cli/doctor/managed-link.d.ts +39 -0
  14. package/dist/types/cli/doctor/native-probe.d.ts +2 -0
  15. package/dist/types/cli/doctor/permission-repairs.d.ts +25 -0
  16. package/dist/types/cli/doctor/plugin-quarantine.d.ts +77 -0
  17. package/dist/types/cli/doctor/plugin-restore.d.ts +40 -0
  18. package/dist/types/cli/doctor/plugin-targets.d.ts +11 -0
  19. package/dist/types/cli/doctor/probe-client.d.ts +9 -0
  20. package/dist/types/cli/doctor/probe-types.d.ts +18 -0
  21. package/dist/types/cli/doctor/probe.d.ts +3 -0
  22. package/dist/types/cli/doctor/projection-probe.d.ts +2 -0
  23. package/dist/types/cli/doctor/repairs.d.ts +28 -0
  24. package/dist/types/cli/doctor/report.d.ts +27 -0
  25. package/dist/types/cli/doctor/runner.d.ts +4 -0
  26. package/dist/types/cli/doctor/safety.d.ts +10 -0
  27. package/dist/types/cli/doctor/service-repairs.d.ts +8 -0
  28. package/dist/types/cli/doctor/service-targets.d.ts +41 -0
  29. package/dist/types/cli/doctor/types.d.ts +145 -0
  30. package/dist/types/cli/doctor-cli.d.ts +7 -0
  31. package/dist/types/cli/doctor-supervisor.d.ts +121 -0
  32. package/dist/types/cli/doctor-worker.d.ts +29 -0
  33. package/dist/types/cli/install-activation.d.ts +158 -0
  34. package/dist/types/cli/macos-community-app.d.ts +41 -0
  35. package/dist/types/cli/mcp-cli.d.ts +0 -11
  36. package/dist/types/cli/update-cli.d.ts +41 -4
  37. package/dist/types/cli-main.d.ts +174 -0
  38. package/dist/types/cli.d.ts +1 -171
  39. package/dist/types/commands/doctor.d.ts +7 -0
  40. package/dist/types/commands/sdk.d.ts +17 -8
  41. package/dist/types/config/autorouting-tier-map.d.ts +19 -0
  42. package/dist/types/config/file-lock.d.ts +27 -3
  43. package/dist/types/config/model-preset-registry.d.ts +1 -0
  44. package/dist/types/config/model-registry.d.ts +12 -5
  45. package/dist/types/config/model-resolver.d.ts +16 -3
  46. package/dist/types/config/models-config-schema.d.ts +2 -0
  47. package/dist/types/config/settings-schema.d.ts +1 -1
  48. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +1 -1
  49. package/dist/types/coordinator-mcp/session-reaper.d.ts +15 -5
  50. package/dist/types/cursor.d.ts +9 -9
  51. package/dist/types/discovery/agents-md.d.ts +6 -1
  52. package/dist/types/discovery/helpers.d.ts +36 -7
  53. package/dist/types/discovery/index.d.ts +1 -1
  54. package/dist/types/discovery/mcp-json.d.ts +2 -0
  55. package/dist/types/extensibility/extensions/loader.d.ts +22 -1
  56. package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +14 -1
  57. package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -1
  58. package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
  59. package/dist/types/extensibility/plugins/manager.d.ts +33 -4
  60. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +6 -0
  61. package/dist/types/extensibility/plugins/marketplace/manager.d.ts +186 -0
  62. package/dist/types/extensibility/plugins/marketplace/registry.d.ts +27 -0
  63. package/dist/types/extensibility/plugins/marketplace/source-resolver.d.ts +16 -1
  64. package/dist/types/extensibility/runtime-skill-discovery.d.ts +16 -3
  65. package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +6 -0
  66. package/dist/types/gjc-runtime/launch-worktree.d.ts +53 -0
  67. package/dist/types/gjc-runtime/session-layout.d.ts +8 -0
  68. package/dist/types/gjc-runtime/session-state-lock.d.ts +15 -0
  69. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
  70. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +6 -0
  71. package/dist/types/main.d.ts +19 -1
  72. package/dist/types/modes/acp/acp-agent.d.ts +40 -0
  73. package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
  74. package/dist/types/modes/components/model-selector.d.ts +2 -0
  75. package/dist/types/modes/interactive-mode.d.ts +1 -1
  76. package/dist/types/runtime-mcp/config-writer.d.ts +47 -0
  77. package/dist/types/runtime-mcp/policy-json-edit.d.ts +60 -0
  78. package/dist/types/runtime-mcp/startup-policy.d.ts +4 -0
  79. package/dist/types/runtime-mcp/types.d.ts +2 -2
  80. package/dist/types/sdk/acp/adapter.d.ts +5 -1
  81. package/dist/types/sdk/broker/broker.d.ts +51 -0
  82. package/dist/types/sdk/broker/daemon-entry.d.ts +39 -0
  83. package/dist/types/sdk/broker/discovery.d.ts +38 -0
  84. package/dist/types/sdk/broker/doctor-restart.d.ts +79 -0
  85. package/dist/types/sdk/broker/endpoint-authority.d.ts +7 -0
  86. package/dist/types/sdk/broker/ensure.d.ts +14 -0
  87. package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
  88. package/dist/types/sdk/broker/process-incarnation.d.ts +50 -0
  89. package/dist/types/sdk/broker/runtime.d.ts +26 -0
  90. package/dist/types/sdk/broker/session-index.d.ts +13 -4
  91. package/dist/types/sdk/broker/worktree-occupancy.d.ts +14 -0
  92. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +16 -0
  93. package/dist/types/sdk/bus/chat-daemon-control.d.ts +101 -6
  94. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +16 -0
  95. package/dist/types/sdk/bus/daemon-startup-exclusion.d.ts +43 -0
  96. package/dist/types/sdk/bus/doctor-daemon-restart-client.d.ts +51 -0
  97. package/dist/types/sdk/bus/doctor-daemon-restart.d.ts +39 -0
  98. package/dist/types/sdk/bus/index.d.ts +2 -0
  99. package/dist/types/sdk/bus/notification-service.d.ts +3 -0
  100. package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +11 -0
  101. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  102. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -0
  103. package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +1 -0
  104. package/dist/types/sdk/bus/telegram-daemon.d.ts +36 -5
  105. package/dist/types/sdk/cli/session-cli.d.ts +1 -0
  106. package/dist/types/sdk/host/session-runtime.d.ts +4 -0
  107. package/dist/types/sdk/host/surface-policy.d.ts +3 -1
  108. package/dist/types/sdk/lifecycle/service.d.ts +44 -10
  109. package/dist/types/sdk/prompt-deadline-lease.d.ts +6 -3
  110. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -2
  111. package/dist/types/sdk/prompt-failure.d.ts +47 -0
  112. package/dist/types/sdk/prompt-status.d.ts +19 -0
  113. package/dist/types/sdk/router/session-router.d.ts +3 -4
  114. package/dist/types/sdk/service-artifact-paths.d.ts +16 -0
  115. package/dist/types/sdk/session-authority.d.ts +9 -0
  116. package/dist/types/sdk/session.d.ts +3 -1
  117. package/dist/types/session/agent-session.d.ts +1 -1
  118. package/dist/types/session/blob-store.d.ts +1 -1
  119. package/dist/types/session/fallback-chain-controller.d.ts +3 -6
  120. package/dist/types/session/internal/managed-session-scope.d.ts +44 -0
  121. package/dist/types/session/primary-control-surface.d.ts +4 -0
  122. package/dist/types/session/session-manager.d.ts +6 -0
  123. package/dist/types/system-prompt.d.ts +6 -0
  124. package/dist/types/tools/ask.d.ts +25 -0
  125. package/dist/types/tools/image-gen.d.ts +0 -4
  126. package/dist/types/tools/skill-discovery.d.ts +7 -5
  127. package/dist/types/tools/ultragoal-ask-guard.d.ts +7 -4
  128. package/dist/types/utils/changelog.d.ts +0 -9
  129. package/dist/types/utils/mupdf-wasm.d.ts +1 -0
  130. package/package.json +9 -8
  131. package/scripts/compile-args.ts +8 -4
  132. package/src/capability/fs.ts +395 -17
  133. package/src/capability/index.ts +273 -10
  134. package/src/capability/types.ts +9 -0
  135. package/src/cli/customize-doctor.ts +18 -12
  136. package/src/cli/doctor/args.ts +236 -0
  137. package/src/cli/doctor/checks.ts +431 -0
  138. package/src/cli/doctor/config-repairs.ts +517 -0
  139. package/src/cli/doctor/context.ts +149 -0
  140. package/src/cli/doctor/files.ts +149 -0
  141. package/src/cli/doctor/ids.ts +115 -0
  142. package/src/cli/doctor/install-repairs.ts +335 -0
  143. package/src/cli/doctor/journal.ts +423 -0
  144. package/src/cli/doctor/managed-link.ts +514 -0
  145. package/src/cli/doctor/native-probe.ts +13 -0
  146. package/src/cli/doctor/permission-repairs.ts +242 -0
  147. package/src/cli/doctor/plugin-quarantine.ts +377 -0
  148. package/src/cli/doctor/plugin-restore.ts +59 -0
  149. package/src/cli/doctor/plugin-targets.ts +373 -0
  150. package/src/cli/doctor/probe-client.ts +195 -0
  151. package/src/cli/doctor/probe-types.ts +21 -0
  152. package/src/cli/doctor/probe.ts +42 -0
  153. package/src/cli/doctor/projection-probe.ts +249 -0
  154. package/src/cli/doctor/repairs.ts +1157 -0
  155. package/src/cli/doctor/report.ts +176 -0
  156. package/src/cli/doctor/runner.ts +342 -0
  157. package/src/cli/doctor/safety.ts +40 -0
  158. package/src/cli/doctor/service-repairs.ts +452 -0
  159. package/src/cli/doctor/service-targets.ts +274 -0
  160. package/src/cli/doctor/types.ts +164 -0
  161. package/src/cli/doctor-cli.ts +62 -0
  162. package/src/cli/doctor-supervisor.ts +571 -0
  163. package/src/cli/doctor-worker.ts +181 -0
  164. package/src/cli/install-activation.ts +666 -0
  165. package/src/cli/macos-community-app.ts +1449 -0
  166. package/src/cli/mcp-cli.ts +11 -21
  167. package/src/cli/nofile-limit.ts +5 -1
  168. package/src/cli/plugin-cli.ts +6 -1
  169. package/src/cli/update-cli.ts +362 -264
  170. package/src/cli-main.ts +517 -0
  171. package/src/cli.ts +64 -433
  172. package/src/commands/autoresearch.ts +11 -5
  173. package/src/commands/completion.ts +1 -1
  174. package/src/commands/doctor.ts +13 -0
  175. package/src/commands/sdk.ts +58 -10
  176. package/src/commands/ultragoal.ts +11 -5
  177. package/src/config/autorouting-tier-map.ts +7 -0
  178. package/src/config/file-lock.ts +736 -71
  179. package/src/config/mcp-schema.json +3 -3
  180. package/src/config/model-preset-registry.ts +1 -0
  181. package/src/config/model-registry.ts +80 -27
  182. package/src/config/model-resolver.ts +33 -4
  183. package/src/config/models-config-schema.ts +2 -0
  184. package/src/config/settings-schema.ts +6 -2
  185. package/src/coordinator-mcp/question-gate-codec.ts +1 -0
  186. package/src/coordinator-mcp/server.ts +534 -51
  187. package/src/coordinator-mcp/session-reaper.ts +64 -8
  188. package/src/cursor.ts +223 -53
  189. package/src/discovery/agents-md.ts +74 -8
  190. package/src/discovery/agents.ts +32 -7
  191. package/src/discovery/builtin.ts +184 -75
  192. package/src/discovery/claude-plugins.ts +85 -17
  193. package/src/discovery/cline.ts +38 -10
  194. package/src/discovery/cursor.ts +19 -6
  195. package/src/discovery/gemini.ts +62 -22
  196. package/src/discovery/github.ts +13 -3
  197. package/src/discovery/helpers.ts +483 -89
  198. package/src/discovery/index.ts +1 -0
  199. package/src/discovery/mcp-json.ts +36 -10
  200. package/src/discovery/opencode.ts +36 -19
  201. package/src/discovery/ssh.ts +11 -8
  202. package/src/discovery/windsurf.ts +12 -3
  203. package/src/exec/bash-executor.ts +10 -3
  204. package/src/exec/isolated-shell.ts +15 -2
  205. package/src/extensibility/extensions/loader.ts +88 -26
  206. package/src/extensibility/gjc-plugins/lifecycle.ts +189 -0
  207. package/src/extensibility/gjc-plugins/paths.ts +4 -1
  208. package/src/extensibility/gjc-plugins/types.ts +27 -0
  209. package/src/extensibility/plugins/manager.ts +200 -22
  210. package/src/extensibility/plugins/marketplace/cache.ts +55 -2
  211. package/src/extensibility/plugins/marketplace/manager.ts +1262 -162
  212. package/src/extensibility/plugins/marketplace/registry.ts +153 -0
  213. package/src/extensibility/plugins/marketplace/source-resolver.ts +116 -10
  214. package/src/extensibility/runtime-skill-discovery.ts +61 -9
  215. package/src/extensibility/skill-management.ts +7 -1
  216. package/src/gjc-runtime/autoresearch-runtime.ts +10 -1
  217. package/src/gjc-runtime/launch-worktree.test.ts +248 -0
  218. package/src/gjc-runtime/launch-worktree.ts +275 -38
  219. package/src/gjc-runtime/repository-binding.ts +26 -1
  220. package/src/gjc-runtime/session-layout.ts +10 -0
  221. package/src/gjc-runtime/session-state-lock.ts +330 -23
  222. package/src/gjc-runtime/session-state-sidecar.ts +328 -80
  223. package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
  224. package/src/harness-control-plane/session-lease.ts +2 -2
  225. package/src/internal-urls/docs-index.generated.ts +28 -26
  226. package/src/main.ts +150 -29
  227. package/src/memories/index.ts +24 -2
  228. package/src/modes/acp/acp-agent.ts +522 -68
  229. package/src/modes/components/agent-dashboard.ts +3 -1
  230. package/src/modes/components/model-selector.ts +28 -19
  231. package/src/modes/components/plugin-settings.ts +2 -1
  232. package/src/modes/controllers/selector-controller.ts +15 -9
  233. package/src/modes/interactive-mode.ts +21 -3
  234. package/src/prompts/system/system-prompt.md +5 -1
  235. package/src/prompts/tools/browser.md +1 -1
  236. package/src/prompts/tools/move-session.md +4 -0
  237. package/src/prompts/tools/read.md +2 -1
  238. package/src/prompts/tools/skill-discovery.md +6 -5
  239. package/src/prompts/tools/task.md +1 -0
  240. package/src/runtime-mcp/config-writer.ts +228 -0
  241. package/src/runtime-mcp/oauth-discovery.ts +114 -14
  242. package/src/runtime-mcp/policy-json-edit.ts +129 -0
  243. package/src/runtime-mcp/startup-policy.ts +14 -0
  244. package/src/runtime-mcp/types.ts +2 -2
  245. package/src/sdk/acp/adapter.ts +27 -12
  246. package/src/sdk/broker/broker.ts +273 -44
  247. package/src/sdk/broker/daemon-entry.ts +119 -0
  248. package/src/sdk/broker/discovery.ts +173 -20
  249. package/src/sdk/broker/doctor-restart.ts +206 -0
  250. package/src/sdk/broker/endpoint-authority.ts +36 -0
  251. package/src/sdk/broker/ensure.ts +49 -14
  252. package/src/sdk/broker/lifecycle.ts +174 -42
  253. package/src/sdk/broker/process-incarnation.ts +86 -2
  254. package/src/sdk/broker/runtime.ts +145 -21
  255. package/src/sdk/broker/session-index.ts +136 -22
  256. package/src/sdk/broker/spawn-substrate.ts +4 -1
  257. package/src/sdk/broker/transport.ts +5 -0
  258. package/src/sdk/broker/worktree-occupancy.ts +21 -1
  259. package/src/sdk/bus/chat-daemon-cli.ts +63 -0
  260. package/src/sdk/bus/chat-daemon-control.ts +260 -64
  261. package/src/sdk/bus/chat-daemon-runtime.ts +141 -25
  262. package/src/sdk/bus/daemon-startup-exclusion.ts +119 -0
  263. package/src/sdk/bus/doctor-daemon-restart-client.ts +291 -0
  264. package/src/sdk/bus/doctor-daemon-restart.ts +92 -0
  265. package/src/sdk/bus/index.ts +252 -51
  266. package/src/sdk/bus/kind-aware-reconciliation.ts +76 -41
  267. package/src/sdk/bus/notification-service.ts +21 -3
  268. package/src/sdk/bus/reconciliation-store.ts +17 -11
  269. package/src/sdk/bus/telegram-daemon-cli.ts +91 -5
  270. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  271. package/src/sdk/bus/telegram-daemon-control.ts +47 -0
  272. package/src/sdk/bus/telegram-daemon-owner-registry.ts +1 -1
  273. package/src/sdk/bus/telegram-daemon.ts +178 -13
  274. package/src/sdk/cli/session-cli.ts +100 -12
  275. package/src/sdk/host/host.ts +2 -7
  276. package/src/sdk/host/session-runtime.test.ts +329 -8
  277. package/src/sdk/host/session-runtime.ts +202 -50
  278. package/src/sdk/host/surface-policy.ts +8 -1
  279. package/src/sdk/lifecycle/service.ts +199 -22
  280. package/src/sdk/mcp/server.ts +1 -1
  281. package/src/sdk/prompt-deadline-lease.ts +12 -4
  282. package/src/sdk/prompt-deadline-manager.ts +5 -7
  283. package/src/sdk/prompt-failure.ts +148 -0
  284. package/src/sdk/prompt-status.ts +30 -1
  285. package/src/sdk/prompt-watchdog.ts +11 -3
  286. package/src/sdk/protocol/operation-inventory.generated.json +18 -0
  287. package/src/sdk/protocol/operation-registry.ts +20 -8
  288. package/src/sdk/router/session-router.ts +8 -3
  289. package/src/sdk/service-artifact-paths.ts +26 -0
  290. package/src/sdk/session-authority.ts +10 -0
  291. package/src/sdk/session.ts +361 -194
  292. package/src/session/agent-session.ts +398 -93
  293. package/src/session/blob-store.ts +9 -9
  294. package/src/session/fallback-chain-controller.ts +3 -7
  295. package/src/session/internal/managed-session-scope.ts +445 -29
  296. package/src/session/internal/managed-session-storage.ts +38 -6
  297. package/src/session/primary-control-surface.ts +12 -0
  298. package/src/session/session-manager.ts +11 -3
  299. package/src/session/session-storage.ts +1 -1
  300. package/src/session-import/codex.ts +6 -1
  301. package/src/session-import/redact.ts +7 -0
  302. package/src/skill-state/workflow-mutation-guard.ts +645 -39
  303. package/src/slash-commands/builtin-registry.ts +7 -3
  304. package/src/system-prompt.ts +8 -0
  305. package/src/task/executor.ts +36 -3
  306. package/src/task/types.ts +27 -5
  307. package/src/tools/ask.ts +114 -34
  308. package/src/tools/browser/actions.ts +2 -2
  309. package/src/tools/browser/tab-worker.ts +11 -8
  310. package/src/tools/descriptors.ts +6 -1
  311. package/src/tools/fetch.ts +7 -2
  312. package/src/tools/image-gen.ts +33 -34
  313. package/src/tools/read.ts +78 -32
  314. package/src/tools/skill-discovery.ts +17 -7
  315. package/src/tools/tool-catalog.generated.ts +8 -8
  316. package/src/tools/ultragoal-ask-guard.ts +16 -26
  317. package/src/utils/changelog.ts +7 -1
  318. package/src/utils/git.ts +3 -1
  319. package/src/utils/markit.ts +24 -2
  320. package/src/utils/mupdf-wasm.ts +26 -0
  321. package/src/web/search/providers/openai-compatible.ts +191 -14
@@ -1,3 +1,4 @@
1
+ import { readSseEvents } from "@gajae-code/utils";
1
2
  import type { ActiveSearchModelCredentials, SearchCitation, SearchResponse, SearchSource } from "../types";
2
3
  import { SearchProviderError } from "../types";
3
4
  import type { SearchParams } from "./base";
@@ -50,14 +51,173 @@ function normalizeResponseBody(value: unknown): JsonObject {
50
51
  return { ...value, output, choices };
51
52
  }
52
53
 
53
- function parseResponseBody(text: string): JsonObject {
54
+ function parseJsonObject(text: string): JsonObject {
54
55
  let value: unknown;
55
56
  try {
56
57
  value = text ? JSON.parse(text) : {};
57
58
  } catch {
58
59
  throw new SearchProviderError("openai-compatible", "OpenAI-compatible web search returned invalid JSON", 502);
59
60
  }
60
- return normalizeResponseBody(value);
61
+ if (!isJsonObject(value)) malformedResponse("expected a JSON object");
62
+ return value;
63
+ }
64
+
65
+ function validateJsonSuccess(value: JsonObject, chat: boolean): JsonObject {
66
+ const json = normalizeResponseBody(value);
67
+ if (json.error != null) malformedResponse("unsuccessful response");
68
+ if (chat) {
69
+ const firstChoice = optionalArray(json.choices, "choices must be an array").find(
70
+ (choice, index) => isJsonObject(choice) && (choice.index === 0 || (choice.index === undefined && index === 0)),
71
+ );
72
+ if (
73
+ isJsonObject(firstChoice) &&
74
+ firstChoice.finish_reason !== undefined &&
75
+ firstChoice.finish_reason !== "stop"
76
+ ) {
77
+ malformedResponse("unsuccessful choice completion");
78
+ }
79
+ return json;
80
+ }
81
+ if ((json.status !== undefined && json.status !== "completed") || json.incomplete_details != null) {
82
+ malformedResponse("unsuccessful response");
83
+ }
84
+ for (const item of optionalArray(json.output, "output must be an array")) {
85
+ if (isJsonObject(item) && item.status !== undefined && item.status !== "completed") {
86
+ malformedResponse("unsuccessful output item");
87
+ }
88
+ }
89
+ return json;
90
+ }
91
+
92
+ function failedStreamEvent(type: unknown): boolean {
93
+ return type === "error" || type === "response.failed" || type === "response.incomplete";
94
+ }
95
+
96
+ async function readStreamBody(response: Response, chat: boolean, signal: AbortSignal): Promise<JsonObject> {
97
+ if (!response.body) malformedResponse("missing event stream");
98
+ const outputItems = new Map<number, JsonObject>();
99
+ const chunks: string[] = [];
100
+ const annotations: unknown[] = [];
101
+ let id: string | undefined;
102
+ let toolUsage: unknown;
103
+ let completed = false;
104
+ try {
105
+ for await (const event of readSseEvents(response.body, signal)) {
106
+ signal.throwIfAborted();
107
+ if (failedStreamEvent(event.event)) malformedResponse("unsuccessful stream event");
108
+ if (event.data === "[DONE]") break;
109
+ if (!event.data) continue;
110
+ const json = parseJsonObject(event.data);
111
+ if (failedStreamEvent(json.type) || json.error != null) malformedResponse("unsuccessful stream event");
112
+ if (!chat) {
113
+ const type = json.type ?? event.event;
114
+ if (type === "response.output_item.done") {
115
+ const index = json.output_index;
116
+ const item = json.item;
117
+ if (
118
+ typeof index !== "number" ||
119
+ !Number.isInteger(index) ||
120
+ index < 0 ||
121
+ !isJsonObject(item) ||
122
+ typeof item.type !== "string"
123
+ ) {
124
+ malformedResponse("invalid completed output item");
125
+ }
126
+ if (
127
+ (item.status !== undefined && item.status !== "completed") ||
128
+ ((item.type === "message" || item.type === "web_search_call") && item.status !== "completed")
129
+ ) {
130
+ malformedResponse("unsuccessful output item");
131
+ }
132
+ normalizeResponseBody({ output: [item] });
133
+ outputItems.set(index, item);
134
+ continue;
135
+ }
136
+ if (type !== "response.completed" && type !== "response.done") continue;
137
+ const snapshot = json.response;
138
+ if (
139
+ !isJsonObject(snapshot) ||
140
+ snapshot.status !== "completed" ||
141
+ snapshot.error != null ||
142
+ snapshot.incomplete_details != null
143
+ ) {
144
+ malformedResponse("missing successful response snapshot");
145
+ }
146
+ // Nonempty terminal output is canonical. Streaming gateways can omit
147
+ // it after sending completed items; reconstruct only that sparse case,
148
+ // never from token deltas or before a successful response terminal.
149
+ const output = optionalArray(snapshot.output, "output must be an array");
150
+ for (const item of output) {
151
+ if (isJsonObject(item) && item.status !== undefined && item.status !== "completed") {
152
+ malformedResponse("unsuccessful output item");
153
+ }
154
+ }
155
+ return normalizeResponseBody({
156
+ ...snapshot,
157
+ output: output.length > 0 ? output : [...outputItems].sort(([a], [b]) => a - b).map(([, item]) => item),
158
+ });
159
+ }
160
+ if (typeof json.id === "string") id = json.id;
161
+ if (webSearchPerformed({ tool_usage: json.tool_usage })) toolUsage = json.tool_usage;
162
+ const choice = optionalArray(json.choices, "choices must be an array").find(
163
+ (value, index) => isJsonObject(value) && (value.index === 0 || (value.index === undefined && index === 0)),
164
+ );
165
+ if (!isJsonObject(choice)) continue;
166
+ if (choice.delta !== undefined && !isJsonObject(choice.delta)) malformedResponse("invalid choice delta");
167
+ const delta = isJsonObject(choice.delta) ? choice.delta : undefined;
168
+ if (delta?.content != null) {
169
+ if (typeof delta.content !== "string") malformedResponse("invalid choice content");
170
+ if (completed && delta.content) malformedResponse("content after completion");
171
+ chunks.push(delta.content);
172
+ }
173
+ for (const annotation of optionalArray(delta?.annotations, "choice annotations must be an array")) {
174
+ annotations.push(annotation);
175
+ }
176
+ if (choice.finish_reason != null) {
177
+ if (choice.finish_reason !== "stop") malformedResponse("unsuccessful choice completion");
178
+ completed = true;
179
+ }
180
+ }
181
+ // readSseEvents intentionally swallows aborts. Never turn one into an
182
+ // EOF error, or return a partial answer after a successful choice chunk.
183
+ signal.throwIfAborted();
184
+ if (!chat || !completed) malformedResponse("stream ended before successful completion");
185
+ // Usage-only chunks may follow finish_reason, so consume through DONE/EOF.
186
+ return { id, tool_usage: toolUsage, choices: [{ message: { content: chunks.join(""), annotations } }] };
187
+ } catch (error) {
188
+ signal.throwIfAborted();
189
+ if (
190
+ error instanceof SearchProviderError ||
191
+ (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError"))
192
+ ) {
193
+ throw error;
194
+ }
195
+ malformedResponse("invalid event stream");
196
+ }
197
+ }
198
+
199
+ async function cancelResponseBody(response: Response): Promise<void> {
200
+ // readSseEvents cancels its iterator on early return/error, propagating
201
+ // cancellation through its abortable pipe. Cancel bodies not owned by it too.
202
+ if (!response.body || response.body.locked) return;
203
+ try {
204
+ await response.body.cancel();
205
+ } catch {
206
+ // Cleanup must not replace the provider error or caller's abort reason.
207
+ }
208
+ }
209
+
210
+ async function readResponseText(response: Response, signal: AbortSignal): Promise<string> {
211
+ try {
212
+ signal.throwIfAborted();
213
+ const body = response.body?.pipeThrough(new TransformStream<Uint8Array, Uint8Array>(), { signal });
214
+ const text = await (body ? new Response(body).text() : response.text());
215
+ signal.throwIfAborted();
216
+ return text;
217
+ } catch (error) {
218
+ signal.throwIfAborted();
219
+ throw error;
220
+ }
61
221
  }
62
222
 
63
223
  /**
@@ -184,6 +344,7 @@ export class OpenAICompatibleSearchProvider extends SearchProvider {
184
344
  ];
185
345
  const responsesBody = {
186
346
  model,
347
+ stream: true,
187
348
  input: messages,
188
349
  tools: [{ type: "web_search" }],
189
350
  temperature: params.temperature,
@@ -191,18 +352,20 @@ export class OpenAICompatibleSearchProvider extends SearchProvider {
191
352
  };
192
353
  const chatBody = {
193
354
  model,
355
+ stream: true,
194
356
  messages,
195
357
  web_search_options: {},
196
358
  temperature: params.temperature,
197
359
  max_tokens: params.maxOutputTokens,
198
360
  };
199
361
 
362
+ const signal = withHardTimeout(params.signal, "llm");
200
363
  const post = (api: "openai-responses" | "openai-completions", payload: unknown) =>
201
364
  fetch(endpoint(baseUrl, api), {
202
365
  method: "POST",
203
366
  headers,
204
367
  body: JSON.stringify(payload),
205
- signal: withHardTimeout(params.signal, "llm"),
368
+ signal,
206
369
  });
207
370
 
208
371
  // Web search is a Responses-API capability: many OpenAI-compatible
@@ -211,20 +374,34 @@ export class OpenAICompatibleSearchProvider extends SearchProvider {
211
374
  // stale knowledge. Prefer `/responses` regardless of the model's chat wire,
212
375
  // and fall back to `/chat/completions` only when `/responses` is absent.
213
376
  let response = await post("openai-responses", responsesBody);
214
- if (response.status === 404 || response.status === 405) {
377
+ const chat = response.status === 404 || response.status === 405;
378
+ if (chat) {
379
+ await cancelResponseBody(response);
380
+ signal.throwIfAborted();
215
381
  response = await post("openai-completions", chatBody);
216
382
  }
217
- const text = await response.text();
218
- if (!response.ok) {
219
- const classified = classifyProviderHttpError(this.id, response.status, text);
220
- if (classified) throw classified;
221
- throw new SearchProviderError(
222
- this.id,
223
- `OpenAI-compatible web search error (${response.status}): ${text}`,
224
- response.status,
225
- );
383
+ let json: JsonObject;
384
+ try {
385
+ signal.throwIfAborted();
386
+ if (!response.ok) {
387
+ const text = await readResponseText(response, signal);
388
+ const classified = classifyProviderHttpError(this.id, response.status, text);
389
+ if (classified) throw classified;
390
+ throw new SearchProviderError(
391
+ this.id,
392
+ `OpenAI-compatible web search error (${response.status}): ${text}`,
393
+ response.status,
394
+ );
395
+ }
396
+ const streaming =
397
+ response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() === "text/event-stream";
398
+ json = streaming
399
+ ? await readStreamBody(response, chat, signal)
400
+ : validateJsonSuccess(parseJsonObject(await readResponseText(response, signal)), chat);
401
+ signal.throwIfAborted();
402
+ } finally {
403
+ await cancelResponseBody(response);
226
404
  }
227
- const json = parseResponseBody(text);
228
405
  const citations = parseCitations(json);
229
406
  const answer = textFromResponse(json);
230
407
  const limit = params.limit ?? params.numSearchResults ?? 10;