@gajae-code/coding-agent 0.18.12 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/CHANGELOG.md +467 -0
  2. package/dist/types/capability/rule.d.ts +7 -2
  3. package/dist/types/cli/append-system-prompt.d.ts +6 -0
  4. package/dist/types/cli/args.d.ts +7 -2
  5. package/dist/types/cli/customize-doctor-precedence.d.ts +16 -0
  6. package/dist/types/cli/customize-doctor.d.ts +4 -3
  7. package/dist/types/cli/public-command-errors.d.ts +1 -0
  8. package/dist/types/cli/root-flags.d.ts +0 -12
  9. package/dist/types/cli/update-cli.d.ts +7 -2
  10. package/dist/types/cli-main.d.ts +0 -6
  11. package/dist/types/commands/launch.d.ts +0 -6
  12. package/dist/types/commands/migrate.d.ts +1 -1
  13. package/dist/types/commands/sdk.d.ts +8 -0
  14. package/dist/types/commit/map-reduce/index.d.ts +4 -4
  15. package/dist/types/config/autorouting-tier-map.d.ts +23 -1
  16. package/dist/types/config/config-hot-reload.d.ts +5 -0
  17. package/dist/types/config/file-lock.d.ts +44 -2
  18. package/dist/types/config/model-profile-activation.d.ts +19 -5
  19. package/dist/types/config/model-registry.d.ts +16 -3
  20. package/dist/types/config/model-resolver.d.ts +5 -4
  21. package/dist/types/config/settings-schema.d.ts +11 -1
  22. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  23. package/dist/types/customization/import.d.ts +2 -0
  24. package/dist/types/customization/types.d.ts +1 -1
  25. package/dist/types/dap/session.d.ts +11 -2
  26. package/dist/types/discovery/helpers.d.ts +7 -2
  27. package/dist/types/edit/index.d.ts +5 -0
  28. package/dist/types/edit/modes/patch.d.ts +9 -0
  29. package/dist/types/eval/py/runtime.d.ts +9 -0
  30. package/dist/types/export/ttsr.d.ts +19 -0
  31. package/dist/types/extensibility/extensions/types.d.ts +5 -1
  32. package/dist/types/extensibility/slash-commands.d.ts +1 -0
  33. package/dist/types/gjc-runtime/ralplan-current-final.d.ts +54 -0
  34. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +5 -0
  35. package/dist/types/gjc-runtime/tmux-sessions.d.ts +24 -0
  36. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +6 -0
  37. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
  38. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  39. package/dist/types/hindsight/content.d.ts +5 -6
  40. package/dist/types/internal-urls/types.d.ts +2 -0
  41. package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -4
  42. package/dist/types/main.d.ts +23 -18
  43. package/dist/types/migrate/action-planner.d.ts +10 -1
  44. package/dist/types/migrate/adapters/codex.d.ts +1 -1
  45. package/dist/types/migrate/adapters/index.d.ts +35 -3
  46. package/dist/types/migrate/hook-module.d.ts +23 -0
  47. package/dist/types/migrate/report.d.ts +2 -1
  48. package/dist/types/migrate/types.d.ts +69 -2
  49. package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
  50. package/dist/types/modes/components/model-selector.d.ts +43 -0
  51. package/dist/types/modes/prompt-suggestion-controller.d.ts +1 -1
  52. package/dist/types/modes/utils/host-status.d.ts +15 -11
  53. package/dist/types/runtime-mcp/project-host-values.d.ts +11 -5
  54. package/dist/types/sdk/broker/broker-exit.d.ts +1 -1
  55. package/dist/types/sdk/broker/broker.d.ts +22 -1
  56. package/dist/types/sdk/broker/ensure.d.ts +5 -2
  57. package/dist/types/sdk/broker/hop.d.ts +9 -0
  58. package/dist/types/sdk/broker/lifecycle.d.ts +22 -0
  59. package/dist/types/sdk/broker/session-index.d.ts +2 -0
  60. package/dist/types/sdk/broker/transport.d.ts +2 -0
  61. package/dist/types/sdk/bus/index.d.ts +21 -0
  62. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +7 -1
  63. package/dist/types/sdk/bus/telegram-daemon.d.ts +2 -0
  64. package/dist/types/sdk/bus/threaded-render.d.ts +2 -1
  65. package/dist/types/sdk/host/control/operations.d.ts +5 -1
  66. package/dist/types/sdk/host/events.d.ts +4 -0
  67. package/dist/types/sdk/host/host.d.ts +2 -1
  68. package/dist/types/sdk/host/prompt-image-upload.d.ts +43 -0
  69. package/dist/types/sdk/host/session-runtime.d.ts +8 -8
  70. package/dist/types/sdk/host/surface-policy.d.ts +2 -1
  71. package/dist/types/sdk/lifecycle/service.d.ts +12 -0
  72. package/dist/types/sdk/prompt-status.d.ts +11 -4
  73. package/dist/types/session/agent-session.d.ts +50 -8
  74. package/dist/types/session/session-manager.d.ts +16 -0
  75. package/dist/types/skill-state/workflow-mutation-guard.d.ts +13 -1
  76. package/dist/types/slash-commands/helpers/mcp.d.ts +5 -0
  77. package/dist/types/ssh/ssh-executor.d.ts +8 -1
  78. package/dist/types/system-prompt.d.ts +3 -2
  79. package/dist/types/task/role-body.d.ts +11 -0
  80. package/dist/types/tools/awaiting-input.d.ts +28 -0
  81. package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -0
  82. package/dist/types/tools/bash-skill-urls.d.ts +4 -0
  83. package/dist/types/tools/bash.d.ts +7 -0
  84. package/dist/types/tools/descriptors.d.ts +13 -2
  85. package/dist/types/tools/fetch.d.ts +6 -0
  86. package/dist/types/tools/irc.d.ts +5 -3
  87. package/dist/types/tools/tmux-self-injection-guard.d.ts +2 -0
  88. package/dist/types/tools/ultragoal-ask-guard.d.ts +1 -1
  89. package/dist/types/tools/write.d.ts +2 -0
  90. package/dist/types/utils/file-mentions.d.ts +5 -0
  91. package/dist/types/utils/image-limits.d.ts +7 -0
  92. package/dist/types/utils/image-loading.d.ts +0 -1
  93. package/dist/types/utils/open.d.ts +0 -1
  94. package/dist/types/utils/pasted-image-loading.d.ts +1 -5
  95. package/dist/types/utils/prompt-suggestion.d.ts +2 -2
  96. package/dist/types/utils/shell-snapshot.d.ts +2 -0
  97. package/dist/types/utils/title-generator.d.ts +13 -3
  98. package/dist/types/web/search/providers/codex.d.ts +6 -7
  99. package/package.json +9 -8
  100. package/scripts/acp-conformance-agent.ts +12 -2
  101. package/scripts/generate-sdk-operation-inventory.ts +16 -0
  102. package/src/capability/fs.ts +31 -3
  103. package/src/capability/rule.ts +11 -2
  104. package/src/cli/append-system-prompt.ts +12 -0
  105. package/src/cli/args.ts +11 -7
  106. package/src/cli/customize-doctor-precedence.ts +31 -0
  107. package/src/cli/customize-doctor.ts +49 -39
  108. package/src/cli/fast-help.ts +0 -2
  109. package/src/cli/migrate-cli.ts +14 -3
  110. package/src/cli/public-command-errors.ts +2 -0
  111. package/src/cli/root-flags.ts +0 -2
  112. package/src/cli/update-cli.ts +247 -6
  113. package/src/commands/harness.ts +25 -15
  114. package/src/commands/migrate.ts +3 -3
  115. package/src/commands/sdk.ts +81 -11
  116. package/src/commit/agentic/agent.ts +6 -6
  117. package/src/commit/analysis/conventional.ts +14 -5
  118. package/src/commit/map-reduce/index.ts +7 -7
  119. package/src/commit/pipeline.ts +12 -12
  120. package/src/config/autorouting-tier-map.ts +20 -1
  121. package/src/config/config-file.ts +106 -3
  122. package/src/config/config-hot-reload.ts +8 -0
  123. package/src/config/file-lock.ts +1431 -304
  124. package/src/config/keybindings.ts +154 -7
  125. package/src/config/model-profile-activation.ts +177 -39
  126. package/src/config/model-registry.ts +380 -55
  127. package/src/config/model-resolver.ts +49 -21
  128. package/src/config/settings-schema.ts +13 -1
  129. package/src/config/settings.ts +2 -0
  130. package/src/coordinator-mcp/server.ts +37 -2
  131. package/src/crash/sanitize.ts +3 -1
  132. package/src/customization/import.ts +276 -7
  133. package/src/customization/inventory.ts +24 -8
  134. package/src/customization/types.ts +1 -0
  135. package/src/dap/client.ts +3 -2
  136. package/src/dap/session.ts +161 -36
  137. package/src/debug/crash-diagnostics.ts +87 -5
  138. package/src/debug/report-bundle.ts +71 -2
  139. package/src/discovery/builtin.ts +4 -3
  140. package/src/discovery/helpers.ts +24 -14
  141. package/src/edit/index.ts +191 -5
  142. package/src/edit/modes/patch.ts +27 -0
  143. package/src/eval/py/runtime.ts +44 -4
  144. package/src/exec/non-interactive-env.ts +0 -1
  145. package/src/export/html/index.ts +60 -20
  146. package/src/export/ttsr.ts +176 -1
  147. package/src/extensibility/extensions/loader.ts +2 -0
  148. package/src/extensibility/extensions/ouroboros-ooo-bridge.ts +63 -1
  149. package/src/extensibility/extensions/types.ts +5 -1
  150. package/src/extensibility/hooks/loader.ts +10 -2
  151. package/src/extensibility/plugins/marketplace/manager.ts +36 -10
  152. package/src/extensibility/slash-commands.ts +5 -0
  153. package/src/gjc-runtime/gc-runtime.ts +4 -1
  154. package/src/gjc-runtime/launch-tmux.ts +23 -13
  155. package/src/gjc-runtime/ralplan-current-final.ts +294 -0
  156. package/src/gjc-runtime/ralplan-runtime.ts +95 -1
  157. package/src/gjc-runtime/state-writer.ts +109 -5
  158. package/src/gjc-runtime/tmux-sessions.ts +93 -13
  159. package/src/gjc-runtime/ultragoal-change-set.ts +90 -11
  160. package/src/gjc-runtime/ultragoal-guard.ts +9 -1
  161. package/src/gjc-runtime/ultragoal-runtime.ts +15 -5
  162. package/src/harness-control-plane/finalize.ts +11 -0
  163. package/src/harness-control-plane/receipt-spool.ts +105 -28
  164. package/src/harness-control-plane/storage.ts +124 -6
  165. package/src/hindsight/content.ts +116 -16
  166. package/src/hindsight/mental-models.ts +22 -10
  167. package/src/hindsight/state.ts +7 -4
  168. package/src/hooks/native-skill-hook.ts +3 -1
  169. package/src/internal-urls/artifact-protocol.ts +3 -1
  170. package/src/internal-urls/docs-index.generated.ts +14 -14
  171. package/src/internal-urls/local-protocol.ts +28 -3
  172. package/src/internal-urls/types.ts +2 -0
  173. package/src/lsp/client.ts +2 -1
  174. package/src/lsp/clients/biome-client.ts +2 -0
  175. package/src/lsp/clients/swiftlint-client.ts +2 -0
  176. package/src/main.ts +330 -92
  177. package/src/memories/index.ts +25 -2
  178. package/src/migrate/action-planner.ts +517 -2
  179. package/src/migrate/adapters/claude-code.ts +141 -3
  180. package/src/migrate/adapters/codex.ts +57 -5
  181. package/src/migrate/adapters/index.ts +240 -5
  182. package/src/migrate/adapters/opencode.ts +4 -0
  183. package/src/migrate/executor.ts +141 -3
  184. package/src/migrate/hook-module.ts +193 -0
  185. package/src/migrate/report.ts +45 -1
  186. package/src/migrate/types.ts +73 -3
  187. package/src/modes/acp/acp-agent.ts +10 -1
  188. package/src/modes/components/agent-dashboard.ts +20 -4
  189. package/src/modes/components/customization/customization-dashboard.ts +7 -0
  190. package/src/modes/components/customization/import-wizard.ts +29 -3
  191. package/src/modes/components/model-selector.ts +157 -103
  192. package/src/modes/components/welcome.ts +3 -1
  193. package/src/modes/controllers/command-controller.ts +271 -16
  194. package/src/modes/controllers/event-controller.ts +1 -1
  195. package/src/modes/controllers/extension-ui-controller.ts +31 -1
  196. package/src/modes/controllers/goal-mode-controller.ts +79 -10
  197. package/src/modes/controllers/input-controller.ts +2 -0
  198. package/src/modes/controllers/plan-mode-controller.ts +49 -4
  199. package/src/modes/controllers/runtime-mcp-command-controller.ts +7 -6
  200. package/src/modes/controllers/selector-controller.ts +14 -1
  201. package/src/modes/interactive-mode.ts +55 -4
  202. package/src/modes/prompt-suggestion-controller.ts +1 -1
  203. package/src/modes/runtime-init.ts +5 -1
  204. package/src/modes/utils/host-status.ts +21 -10
  205. package/src/prompts/agent-fragments/browser-aside.md +2 -2
  206. package/src/prompts/system/irc-incoming.md +3 -1
  207. package/src/prompts/tools/irc.md +1 -1
  208. package/src/runtime-mcp/manager.ts +8 -3
  209. package/src/runtime-mcp/project-host-values.ts +12 -8
  210. package/src/runtime-mcp/tool-bridge.ts +68 -4
  211. package/src/runtime-mcp/transports/stdio.ts +2 -1
  212. package/src/sdk/broker/broker-exit.ts +2 -0
  213. package/src/sdk/broker/broker.ts +143 -4
  214. package/src/sdk/broker/ensure.ts +74 -6
  215. package/src/sdk/broker/hop.ts +13 -1
  216. package/src/sdk/broker/lifecycle-ledger.ts +3 -0
  217. package/src/sdk/broker/lifecycle.ts +102 -6
  218. package/src/sdk/broker/session-index.ts +29 -4
  219. package/src/sdk/broker/transport.ts +17 -1
  220. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  221. package/src/sdk/bus/index.ts +408 -14
  222. package/src/sdk/bus/session-control.ts +2 -0
  223. package/src/sdk/bus/slack-daemon.ts +105 -9
  224. package/src/sdk/bus/telegram-daemon-contract.ts +7 -1
  225. package/src/sdk/bus/telegram-daemon.ts +10 -2
  226. package/src/sdk/bus/threaded-render.ts +20 -1
  227. package/src/sdk/cli/session-cli.ts +2 -0
  228. package/src/sdk/host/control/dispatch.ts +13 -1
  229. package/src/sdk/host/control/operations.ts +10 -1
  230. package/src/sdk/host/events.ts +67 -4
  231. package/src/sdk/host/host.ts +8 -2
  232. package/src/sdk/host/prompt-image-upload.ts +438 -0
  233. package/src/sdk/host/sdk-surface-parity.test.ts +15 -0
  234. package/src/sdk/host/session-runtime.test.ts +1537 -24
  235. package/src/sdk/host/session-runtime.ts +332 -36
  236. package/src/sdk/host/surface-policy.ts +4 -0
  237. package/src/sdk/host/websocket-transport.lifecycle.test.ts +90 -2
  238. package/src/sdk/host/websocket-transport.ts +19 -7
  239. package/src/sdk/lifecycle/service.ts +14 -1
  240. package/src/sdk/prompt-status.ts +6 -4
  241. package/src/sdk/protocol/adapter-validation.ts +28 -1
  242. package/src/sdk/protocol/operation-inventory.generated.json +160 -0
  243. package/src/sdk/protocol/operation-registry.ts +33 -3
  244. package/src/sdk/session.ts +123 -36
  245. package/src/session/agent-session.ts +1614 -329
  246. package/src/session/history-storage.ts +182 -12
  247. package/src/session/internal/managed-session-scope.ts +47 -18
  248. package/src/session/internal/managed-session-storage.ts +71 -1
  249. package/src/session/session-manager.ts +90 -5
  250. package/src/session-import/redact.ts +53 -2
  251. package/src/skill-state/workflow-mutation-guard.ts +1498 -18
  252. package/src/slash-commands/builtin-registry.ts +6 -3
  253. package/src/slash-commands/helpers/mcp.ts +11 -3
  254. package/src/ssh/ssh-executor.ts +9 -2
  255. package/src/stt/recorder.ts +26 -9
  256. package/src/system-prompt.ts +61 -16
  257. package/src/task/executor.ts +12 -8
  258. package/src/task/role-body.ts +84 -0
  259. package/src/task/token-log-mode.test.ts +162 -0
  260. package/src/task/token-log.ts +91 -2
  261. package/src/tools/archive-reader.ts +650 -37
  262. package/src/tools/ask.ts +1 -0
  263. package/src/tools/ast-grep.ts +7 -2
  264. package/src/tools/auto-generated-guard.ts +12 -3
  265. package/src/tools/awaiting-input.ts +114 -0
  266. package/src/tools/bash-allowed-prefixes.ts +61 -1
  267. package/src/tools/bash-skill-urls.ts +58 -5
  268. package/src/tools/bash.ts +99 -26
  269. package/src/tools/browser/tab-worker.ts +39 -13
  270. package/src/tools/browser.ts +17 -7
  271. package/src/tools/cron.ts +10 -1
  272. package/src/tools/debug.ts +9 -2
  273. package/src/tools/descriptors.test.ts +4 -4
  274. package/src/tools/descriptors.ts +16 -4
  275. package/src/tools/eval.ts +15 -0
  276. package/src/tools/fetch.ts +90 -2
  277. package/src/tools/gh.ts +27 -12
  278. package/src/tools/image-gen.ts +20 -6
  279. package/src/tools/irc.ts +5 -3
  280. package/src/tools/monitor.ts +9 -1
  281. package/src/tools/output-meta.ts +51 -3
  282. package/src/tools/read.ts +9 -2
  283. package/src/tools/recipe/runner.ts +23 -12
  284. package/src/tools/render-utils.ts +2 -2
  285. package/src/tools/search.ts +3 -2
  286. package/src/tools/ssh.ts +274 -8
  287. package/src/tools/subagent.ts +10 -4
  288. package/src/tools/tmux-self-injection-guard.ts +3301 -122
  289. package/src/tools/tool-catalog.generated.ts +1 -1
  290. package/src/tools/ultragoal-ask-guard.ts +13 -1
  291. package/src/tools/write.ts +7 -0
  292. package/src/tui/tree-list.ts +15 -1
  293. package/src/utils/commit-message-generator.ts +3 -2
  294. package/src/utils/external-editor.ts +21 -7
  295. package/src/utils/file-mentions.ts +46 -6
  296. package/src/utils/git.ts +38 -7
  297. package/src/utils/image-limits.ts +8 -0
  298. package/src/utils/image-loading.ts +1 -1
  299. package/src/utils/open.ts +73 -4
  300. package/src/utils/pasted-image-loading.ts +9 -8
  301. package/src/utils/prompt-suggestion.ts +5 -4
  302. package/src/utils/shell-snapshot.ts +9 -1
  303. package/src/utils/title-generator.ts +65 -20
  304. package/src/web/search/index.ts +2 -1
  305. package/src/web/search/providers/codex.ts +29 -32
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Generate session titles using a smol, fast model.
2
+ * Generate session titles with the `default` model role.
3
3
  */
4
4
  import * as path from "node:path";
5
5
 
@@ -10,6 +10,7 @@ import type { ModelRegistry } from "../config/model-registry";
10
10
  import { resolveRoleSelection } from "../config/model-resolver";
11
11
  import type { Settings } from "../config/settings";
12
12
  import titleSystemPrompt from "../prompts/system/title-system.md" with { type: "text" };
13
+ import { collectEnvSecrets, createSecretObfuscator, loadSecrets } from "../secrets";
13
14
  import { syncCmuxWorkspaceTitle } from "./cmux-workspace";
14
15
  import { syncHerdrPaneTitle } from "./herdr-pane";
15
16
 
@@ -37,8 +38,13 @@ const MAX_TITLE_WORDS = 12;
37
38
  * Build a bounded conversation digest for title regeneration. Regeneration
38
39
  * reads several messages so a throwaway first prompt can be corrected, while
39
40
  * the automatic first-message path intentionally uses only its initial input.
41
+ * `obfuscateText`, when set, runs on each kept message's full text before the
42
+ * per-message character cut so a secret cannot be split by that cut.
40
43
  */
41
- export function buildConversationTitleInput(messages: readonly AgentMessage[]): string | undefined {
44
+ export function buildConversationTitleInput(
45
+ messages: readonly AgentMessage[],
46
+ obfuscateText?: (text: string) => string,
47
+ ): string | undefined {
42
48
  const userMessages: string[] = [];
43
49
  for (const message of messages) {
44
50
  if (message.role !== "user") continue;
@@ -51,7 +57,10 @@ export function buildConversationTitleInput(messages: readonly AgentMessage[]):
51
57
  .map(block => block.text)
52
58
  .join("\n");
53
59
  const trimmed = text.trim();
54
- if (trimmed) userMessages.push(trimmed);
60
+ if (!trimmed) continue;
61
+ // Emptiness uses the trimmed text. With an obfuscator, keep the original
62
+ // so a secret that includes edge whitespace still matches, then trim.
63
+ userMessages.push(obfuscateText ? text : trimmed);
55
64
  }
56
65
 
57
66
  if (userMessages.length === 0) return undefined;
@@ -60,13 +69,19 @@ export function buildConversationTitleInput(messages: readonly AgentMessage[]):
60
69
  userMessages.length > MAX_TITLE_CONVERSATION_MESSAGES
61
70
  ? [userMessages[0]!, ...userMessages.slice(-(MAX_TITLE_CONVERSATION_MESSAGES - 1))]
62
71
  : userMessages;
72
+ // Obfuscate the full kept text first. The 400-character cut below, and the
73
+ // later 2,000-character cap, then keep their existing limits.
74
+ const preparedMessages = obfuscateText
75
+ ? keptMessages.map(message => obfuscateText(message).trim()).filter(message => message.length > 0)
76
+ : keptMessages;
77
+ if (preparedMessages.length === 0) return undefined;
63
78
  // Reserve separator and ellipsis room so the digest is already within the
64
79
  // generator's MAX_INPUT_CHARS bound; its existing truncation is a no-op here.
65
80
  const maxMessageChars = Math.min(
66
81
  MAX_TITLE_MESSAGE_CHARS,
67
- Math.floor((MAX_INPUT_CHARS - (keptMessages.length - 1) - keptMessages.length) / keptMessages.length),
82
+ Math.floor((MAX_INPUT_CHARS - (preparedMessages.length - 1) - preparedMessages.length) / preparedMessages.length),
68
83
  );
69
- return keptMessages
84
+ return preparedMessages
70
85
  .map(message => (message.length > maxMessageChars ? `${message.slice(0, maxMessageChars)}…` : message))
71
86
  .join("\n");
72
87
  }
@@ -87,8 +102,13 @@ const setTitleTool: Tool = {
87
102
  },
88
103
  };
89
104
 
90
- function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel?: Model<Api>): Model<Api> | undefined {
91
- const availableModels = registry.getAvailable();
105
+ function getTitleModel(
106
+ registry: ModelRegistry,
107
+ settings: Settings,
108
+ currentModel?: Model<Api>,
109
+ credentialSessionId?: string,
110
+ ): Model<Api> | undefined {
111
+ const availableModels = registry.getAvailable(credentialSessionId);
92
112
  if (availableModels.length === 0) return undefined;
93
113
 
94
114
  const titleModel = resolveRoleSelection(["default"], settings, availableModels, registry)?.model;
@@ -99,6 +119,32 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
99
119
  return undefined;
100
120
  }
101
121
 
122
+ /**
123
+ * Obfuscate the full title input, then truncate, then wrap.
124
+ * A secret that crosses the character cap is replaced before the cut, and the
125
+ * `<user-message>` fence stays outside the truncated body.
126
+ */
127
+ export function prepareTitleOutboundText(firstMessage: string, obfuscateText?: (text: string) => string): string {
128
+ const source = obfuscateText ? obfuscateText(firstMessage) : firstMessage;
129
+ const truncated = source.length > MAX_INPUT_CHARS ? `${source.slice(0, MAX_INPUT_CHARS)}…` : source;
130
+ return `<user-message>\n${truncated}\n</user-message>`;
131
+ }
132
+
133
+ export async function titleInputObfuscator(
134
+ settings: Settings,
135
+ activeCwd?: string,
136
+ ): Promise<((text: string) => string) | undefined> {
137
+ const reader = settings as Partial<Pick<Settings, "get" | "getCwd" | "getAgentDir">>;
138
+ if (typeof reader.get !== "function" || reader.get("secrets.enabled") !== true) return undefined;
139
+ const cwd = activeCwd ?? (typeof reader.getCwd === "function" ? reader.getCwd() : process.cwd());
140
+ const agentDir = typeof reader.getAgentDir === "function" ? reader.getAgentDir() : cwd;
141
+ const entries = [...collectEnvSecrets(), ...(await loadSecrets(cwd, agentDir))];
142
+ if (entries.length === 0) return undefined;
143
+ const obfuscator = createSecretObfuscator(entries);
144
+ if (!obfuscator.hasSecrets()) return undefined;
145
+ return text => obfuscator.obfuscate(text);
146
+ }
147
+
102
148
  /**
103
149
  * Generate a title for a session based on the provided user-message input.
104
150
  * Automatic titles pass the first user message; regeneration passes a bounded
@@ -106,13 +152,14 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
106
152
  *
107
153
  * @param firstMessage The first user message or bounded conversation digest
108
154
  * @param registry Model registry
109
- * @param settings Settings used to resolve the smol role
155
+ * @param settings Settings used to resolve the `default` model role
110
156
  * @param sessionId Optional session id for sticky API key selection
111
157
  * @param currentModel Current model (used to derive title model)
112
158
  * @param metadataResolver Optional resolver evaluated after credential selection
113
159
  * to produce request metadata (e.g. user_id for session attribution). Using a
114
160
  * resolver instead of a pre-evaluated value ensures the metadata's account_uuid
115
161
  * reflects the credential actually selected for this request.
162
+ * @param activeCwd Active session cwd for project-scoped secret lookup.
116
163
  */
117
164
  export async function generateSessionTitle(
118
165
  firstMessage: string,
@@ -121,23 +168,17 @@ export async function generateSessionTitle(
121
168
  sessionId?: string,
122
169
  currentModel?: Model<Api>,
123
170
  metadataResolver?: (provider: string) => Record<string, unknown> | undefined,
171
+ activeCwd?: string,
124
172
  ): Promise<string | null> {
125
- const model = getTitleModel(registry, settings, currentModel);
173
+ const model = getTitleModel(registry, settings, currentModel, sessionId);
126
174
  if (!model) {
127
175
  logger.debug("title-generator: no title model found");
128
176
  return null;
129
177
  }
130
178
 
131
- // Truncate message if too long
132
- const truncatedMessage =
133
- firstMessage.length > MAX_INPUT_CHARS ? `${firstMessage.slice(0, MAX_INPUT_CHARS)}…` : firstMessage;
134
- const userMessage = `<user-message>
135
- ${truncatedMessage}
136
- </user-message>`;
137
-
138
179
  const apiKey = await registry.getApiKey(model, sessionId);
139
180
  if (!apiKey) {
140
- logger.debug("title-generator: no API key for smol model", {
181
+ logger.debug("title-generator: no API key for title model", {
141
182
  provider: model.provider,
142
183
  id: model.id,
143
184
  });
@@ -147,6 +188,7 @@ ${truncatedMessage}
147
188
  // request is already recorded; metadataResolver can then return the correct
148
189
  // account_uuid rather than the snapshot-at-call-site value.
149
190
  const metadata = metadataResolver?.(model.provider);
191
+ const userMessage = prepareTitleOutboundText(firstMessage, await titleInputObfuscator(settings, activeCwd));
150
192
 
151
193
  // Title generation is a 3-6 word task, but some reasoning backends ignore
152
194
  // disableReasoning. Keep the normal cheap budget for non-reasoning models
@@ -187,7 +229,10 @@ ${truncatedMessage}
187
229
  logger.debug("title-generator: response error", {
188
230
  model: request.model,
189
231
  stopReason: response.stopReason,
190
- errorMessage: response.errorMessage,
232
+ errorMessage:
233
+ typeof response.errorMessage === "string"
234
+ ? redactCrashSecrets(response.errorMessage)
235
+ : response.errorMessage,
191
236
  });
192
237
  return null;
193
238
  }
@@ -196,7 +241,7 @@ ${truncatedMessage}
196
241
 
197
242
  logger.debug("title-generator: response", {
198
243
  model: request.model,
199
- title,
244
+ title: redactCrashSecrets(title),
200
245
  usage: response.usage,
201
246
  stopReason: response.stopReason,
202
247
  });
@@ -209,7 +254,7 @@ ${truncatedMessage}
209
254
  } catch (err) {
210
255
  logger.debug("title-generator: error", {
211
256
  model: request.model,
212
- error: err instanceof Error ? err.message : String(err),
257
+ error: redactCrashSecrets(err instanceof Error ? err.message : String(err)),
213
258
  });
214
259
  return null;
215
260
  }
@@ -15,6 +15,7 @@ import webSearchSystemPrompt from "../../prompts/system/web-search.md" with { ty
15
15
  import webSearchDescription from "../../prompts/tools/web-search.md" with { type: "text" };
16
16
  import { discoverAuthStorage } from "../../sdk";
17
17
  import type { ToolSession } from "../../tools";
18
+ import { wrapUntrustedContent } from "../../tools/fetch";
18
19
  import { formatAge } from "../../tools/render-utils";
19
20
  import { throwIfAborted } from "../../tools/tool-errors";
20
21
  import {
@@ -157,7 +158,7 @@ export function formatSearchResponseForLlm(response: SearchResponse): string {
157
158
  }
158
159
  }
159
160
 
160
- return `<untrusted-content>\n${parts.join("\n").replace(/<\/untrusted-content>/gi, "&lt;/untrusted-content>")}\n</untrusted-content>`;
161
+ return wrapUntrustedContent(parts.join("\n"));
161
162
  }
162
163
 
163
164
  interface ExecuteSearchOptions {
@@ -7,8 +7,9 @@
7
7
  * SQLite store, never POSTs the broker sentinel to an OpenAI token endpoint.
8
8
  */
9
9
  import * as os from "node:os";
10
- import { type AuthStorage, getBundledModels } from "@gajae-code/ai/core";
10
+ import type { AuthStorage } from "@gajae-code/ai/core";
11
11
  import { OPENAI_HEADER_VALUES } from "@gajae-code/ai/providers/openai-codex/constants";
12
+ import { fetchCodexModels } from "@gajae-code/ai/utils/discovery/codex";
12
13
  import { decodeJwt } from "@gajae-code/ai/utils/oauth/openai-codex";
13
14
  import { $env, readSseJson, sanitizeHeaderComponent } from "@gajae-code/utils";
14
15
  import packageJson from "../../../../package.json" with { type: "json" };
@@ -21,17 +22,9 @@ import { classifyProviderHttpError, withHardTimeout } from "./utils";
21
22
 
22
23
  const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
23
24
  const CODEX_RESPONSES_PATH = "/codex/responses";
24
- const FALLBACK_MODEL = "gpt-5.5";
25
- const DEFAULT_MODEL_PREFERENCES = [
26
- "gpt-5.5",
27
- "gpt-5.4",
28
- "gpt-5-codex",
29
- "gpt-5",
30
- "gpt-5.3-codex",
31
- "gpt-5.2-codex",
32
- "gpt-5.1-codex",
33
- "gpt-5-codex-mini",
34
- ];
25
+ // Reviewed search candidates, additionally filtered by current account discovery.
26
+ // Upstream Codex web-search metadata informs this policy, not bundle presence.
27
+ const DEFAULT_MODEL_PREFERENCES = ["gpt-6.1-sol", "gpt-6-luna"];
35
28
  const JWT_CLAIM_PATH = "https://api.openai.com/auth";
36
29
  const DEFAULT_INSTRUCTIONS =
37
30
  "You are a helpful assistant with web search capabilities. Search the web to answer the user's question accurately and cite your sources.";
@@ -41,21 +34,26 @@ function getConfiguredModel(): string | undefined {
41
34
  return configuredModel ? configuredModel : undefined;
42
35
  }
43
36
 
44
- function getDefaultModelCandidates(): string[] {
45
- const bundledModels = getBundledModels("openai-codex");
46
- const bundledIds = new Set(bundledModels.map(model => model.id));
47
- const candidates = DEFAULT_MODEL_PREFERENCES.filter(modelId => bundledIds.has(modelId));
48
-
49
- if (candidates.length > 0) {
50
- return candidates;
37
+ async function getDefaultModelCandidates(
38
+ auth: { accessToken: string; accountId: string },
39
+ signal: AbortSignal | undefined,
40
+ ): Promise<string[]> {
41
+ signal?.throwIfAborted();
42
+ const discovery = await fetchCodexModels({ ...auth, signal: withHardTimeout(signal, "llm") });
43
+ signal?.throwIfAborted();
44
+ if (!discovery) {
45
+ throw new SearchProviderError("codex", "Codex model discovery unavailable", 503);
51
46
  }
52
-
53
- const nonMini = bundledModels.find(model => !model.id.includes("mini") && !model.id.includes("spark"));
54
- if (nonMini) {
55
- return [nonMini.id];
47
+ const advertisedIds = new Set(discovery.models.map(model => model.id));
48
+ const candidates = DEFAULT_MODEL_PREFERENCES.filter(modelId => advertisedIds.has(modelId));
49
+ if (candidates.length === 0) {
50
+ throw new SearchProviderError(
51
+ "codex",
52
+ "No preferred Codex web-search models are advertised for this account",
53
+ 503,
54
+ );
56
55
  }
57
-
58
- return bundledModels[0]?.id ? [bundledModels[0].id] : [FALLBACK_MODEL];
56
+ return candidates;
59
57
  }
60
58
 
61
59
  function shouldRetryWithNextDefaultModel(error: unknown): boolean {
@@ -337,12 +335,11 @@ async function callCodexSearch(
337
335
  * Executes a web search using OpenAI code provider's built-in web search tool.
338
336
  *
339
337
  * Default-model behavior:
340
- * - If `PI_CODEX_WEB_SEARCH_MODEL` is set, use it exactly once and surface any
341
- * upstream error verbatim.
342
- * - Otherwise prefer ChatGPT-account-safe bundled defaults (GPT-5.5, GPT-5.4,
343
- * GPT-5 code backend, …) and retry the next candidate only when OpenAI code backend returns the
344
- * known 400 "model is not supported" family. This avoids selecting
345
- * `gpt-5-codex-mini` first on ChatGPT accounts, which OpenAI rejects.
338
+ * - Use `GJC_OPENAI_CODE_WEB_SEARCH_MODEL` (then `PI_CODEX_WEB_SEARCH_MODEL`)
339
+ * exactly once when configured and surface upstream errors without model retries.
340
+ * - Otherwise intersect current account discovery with GPT-6.1 Sol, then GPT-6 Luna.
341
+ * Fail closed when discovery is unavailable or neither candidate is advertised.
342
+ * Retry the next advertised candidate only for the known unsupported-model 400 family.
346
343
  */
347
344
  export async function searchCodex(params: SearchParams): Promise<SearchResponse> {
348
345
  const auth = await findCodexAuth(params.authStorage, params.sessionId, params.signal);
@@ -353,7 +350,7 @@ export async function searchCodex(params: SearchParams): Promise<SearchResponse>
353
350
  }
354
351
 
355
352
  const configuredModel = getConfiguredModel();
356
- const modelCandidates = configuredModel ? [configuredModel] : getDefaultModelCandidates();
353
+ const modelCandidates = configuredModel ? [configuredModel] : await getDefaultModelCandidates(auth, params.signal);
357
354
 
358
355
  let result:
359
356
  | {