@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
package/src/main.ts CHANGED
@@ -70,6 +70,7 @@ import { newMasterAttestationEpoch, resolveSessionLocator, SessionIndex } from "
70
70
  import type { AgentSession } from "./session/agent-session";
71
71
  import { ManagedAppendIdentityMismatchError } from "./session/internal/managed-session-storage";
72
72
  import { SessionMigrationBusyError } from "./session/internal/session-open-errors";
73
+ import { setPrimaryControlSurface } from "./session/primary-control-surface";
73
74
  import {
74
75
  type ResumeSessionIdentity,
75
76
  resolveResumableSession,
@@ -82,6 +83,7 @@ import {
82
83
  SessionTranscriptOversizedError,
83
84
  type StrictSessionOpenResult,
84
85
  } from "./session/session-manager";
86
+ import { resolveStartupAuthConfig } from "./session/startup-auth-config";
85
87
  import { runStartupCredentialAutoImportIfNeeded } from "./setup/credential-auto-import";
86
88
  import {
87
89
  readOnboardingState,
@@ -472,7 +474,10 @@ async function applyStartupModelProfilesWithPolicy(
472
474
  const applyProfile = async (
473
475
  profileName: string,
474
476
  persistDefault: boolean,
475
- options: { thinkingLevelOverride?: CreateAgentSessionOptions["thinkingLevel"] } = {},
477
+ options: {
478
+ thinkingLevelOverride?: CreateAgentSessionOptions["thinkingLevel"];
479
+ tolerateCredentialError?: boolean;
480
+ } = {},
476
481
  ): Promise<boolean> => {
477
482
  try {
478
483
  await activateModelProfile(
@@ -481,14 +486,22 @@ async function applyStartupModelProfilesWithPolicy(
481
486
  );
482
487
  return true;
483
488
  } catch (error) {
484
- if (onCredentialError && error instanceof ModelProfileCredentialError) {
485
- onCredentialError(error);
489
+ if (error instanceof ModelProfileCredentialError && (onCredentialError || options.tolerateCredentialError)) {
490
+ if (onCredentialError) onCredentialError(error);
491
+ else process.stderr.write(`${chalk.yellow(`Warning: ${error.message}`)}\n`);
486
492
  return false;
487
493
  }
488
494
  throw error;
489
495
  }
490
496
  };
491
497
 
498
+ // An explicit startup selector (--mpreset or --model) fully replaces the
499
+ // persisted default profile for this session, so a failing default must not
500
+ // abort a non-interactive run: it is reported as a warning and the explicit
501
+ // selection proceeds. Without an explicit selector the historic fatal
502
+ // contract is unchanged.
503
+ const tolerateDefaultProfileFailure = args.parsedArgs.mpreset !== undefined || args.parsedArgs.model !== undefined;
504
+
492
505
  // Capture the explicitly-selected startup model BEFORE profile activation can
493
506
  // override it. startupModel covers the eager path; session.model covers the
494
507
  // deferred `--model <pattern>` path resolved inside createAgentSession.
@@ -505,6 +518,7 @@ async function applyStartupModelProfilesWithPolicy(
505
518
  thinkingLevelOverride: args.settings.has("defaultThinkingLevel")
506
519
  ? args.settings.get("defaultThinkingLevel")
507
520
  : undefined,
521
+ tolerateCredentialError: tolerateDefaultProfileFailure,
508
522
  })) && applied;
509
523
  }
510
524
  if (args.parsedArgs.mpreset) {
@@ -520,14 +534,15 @@ async function applyStartupModelProfilesWithPolicy(
520
534
  applied = await applyConfiguredProfiles();
521
535
  } catch (error) {
522
536
  if (error instanceof ModelProfileCredentialError) throw error;
523
- await args.modelRegistry.refresh("online-if-uncached");
537
+ await args.modelRegistry.refresh("online-if-uncached", args.session.credentialSessionId);
524
538
  refreshedOnline = true;
525
539
  applied = await applyConfiguredProfiles();
526
540
  }
527
- if (applied && !refreshedOnline) args.modelRegistry.refreshInBackground();
541
+ if (applied && !refreshedOnline)
542
+ args.modelRegistry.refreshInBackground("online-if-uncached", args.session.credentialSessionId);
528
543
  } else {
529
544
  if (defaultProfile || args.parsedArgs.mpreset) {
530
- await args.modelRegistry.refresh("online-if-uncached");
545
+ await args.modelRegistry.refresh("online-if-uncached", args.session.credentialSessionId);
531
546
  }
532
547
  await applyConfiguredProfiles();
533
548
  }
@@ -683,17 +698,61 @@ export const BARE_RESUME_OPEN_ERROR = "Could not open the selected session. Use
683
698
  const SESSION_ARTIFACT_CAPACITY_RECOVERY_MESSAGE =
684
699
  "The selected legacy session's artifacts exceed the supported migration capacity. Archive or remove only that legacy session's artifacts after confirming they are no longer needed, then retry.";
685
700
 
686
- function operatorFacingSessionOpenMessage(value: unknown): string | undefined {
701
+ export class SessionForkDeclinedError extends Error {
702
+ readonly code = "session_fork_declined" as const;
703
+ readonly #sessionId: string;
704
+ readonly #projectDirectory: string;
705
+
706
+ constructor(sessionId: string, projectDirectory: string) {
707
+ super(
708
+ `Session "${sessionId}" is in another project (${projectDirectory}). Re-run from that directory, or accept the fork when prompted.`,
709
+ );
710
+ this.name = "SessionForkDeclinedError";
711
+ this.#sessionId = sessionId;
712
+ this.#projectDirectory = projectDirectory;
713
+ }
714
+
715
+ get sessionId(): string {
716
+ return this.#sessionId;
717
+ }
718
+
719
+ get projectDirectory(): string {
720
+ return this.#projectDirectory;
721
+ }
722
+ }
723
+
724
+ export class SessionNotFoundError extends Error {
725
+ readonly code = "session_not_found" as const;
726
+ readonly #sessionId: string;
727
+
728
+ constructor(sessionId: string) {
729
+ super(`Session "${sessionId}" not found.`);
730
+ this.name = "SessionNotFoundError";
731
+ this.#sessionId = sessionId;
732
+ }
733
+
734
+ get sessionId(): string {
735
+ return this.#sessionId;
736
+ }
737
+ }
738
+
739
+ export function operatorFacingSessionOpenMessage(value: unknown): string | undefined {
687
740
  const code =
688
- value instanceof SessionArtifactCapacityError
741
+ value instanceof SessionForkDeclinedError
689
742
  ? value.code
690
- : value instanceof SessionMigrationBusyError
743
+ : value instanceof SessionNotFoundError
691
744
  ? value.code
692
- : value instanceof SessionTranscriptOversizedError
745
+ : value instanceof SessionArtifactCapacityError
693
746
  ? value.code
694
- : typeof value === "string"
695
- ? value
696
- : undefined;
747
+ : value instanceof SessionMigrationBusyError
748
+ ? value.code
749
+ : value instanceof SessionTranscriptOversizedError
750
+ ? value.code
751
+ : typeof value === "string"
752
+ ? value
753
+ : undefined;
754
+ if (code === "session_fork_declined" && value instanceof SessionForkDeclinedError) return value.message;
755
+ if (code === "session_not_found" && value instanceof SessionNotFoundError) return value.message;
697
756
  if (code === "artifact_capacity_exceeded") return SESSION_ARTIFACT_CAPACITY_RECOVERY_MESSAGE;
698
757
  if (code === "oversized") return SESSION_OVERSIZED_RECOVERY_MESSAGE;
699
758
  if (code === "migration_busy") return new SessionMigrationBusyError().message;
@@ -738,6 +797,7 @@ export async function runInteractiveMode(
738
797
  resumeAction?: "continue-tail" | "open-idle",
739
798
  startDeferredMcpConfig?: CreateAgentSessionResult["startDeferredMcpConfig"],
740
799
  startDeferredModelProfiles?: DeferredModelProfileStartup,
800
+ options?: { stopAfterFirstPaint?: boolean },
741
801
  ): Promise<void> {
742
802
  const mode = createInteractiveMode
743
803
  ? createInteractiveMode({
@@ -759,7 +819,7 @@ export async function runInteractiveMode(
759
819
  eventBus,
760
820
  );
761
821
 
762
- await initializeInteractiveModeWithStartupUpdate(mode, startupUpdate);
822
+ await logger.time("interactive:init", () => initializeInteractiveModeWithStartupUpdate(mode, startupUpdate));
763
823
  try {
764
824
  await persistCoordinatorRuntimeInputReady();
765
825
  } catch (error) {
@@ -772,7 +832,10 @@ export async function runInteractiveMode(
772
832
  normalInteractive: true,
773
833
  automation:
774
834
  /^(?:1|true|yes|on)$/i.test(process.env.CI ?? "") ||
775
- /^(?:1|true|yes|on)$/i.test(process.env.GJC_AUTOMATION ?? ""),
835
+ /^(?:1|true|yes|on)$/i.test(process.env.GJC_AUTOMATION ?? "") ||
836
+ // The timing probe must never block on a fresh-profile onboarding
837
+ // selector waiting for input that will never come.
838
+ options?.stopAfterFirstPaint === true,
776
839
  initialMessage,
777
840
  initialMessages,
778
841
  initialImages,
@@ -785,7 +848,14 @@ export async function runInteractiveMode(
785
848
  if (shouldOfferOnboarding(onboardingState)) await mode.showFrictionlessOnboarding();
786
849
  }
787
850
  }
788
- mode.renderInitialMessages(undefined, { preserveExistingChat: true });
851
+ logger.time("interactive:firstPaint", () => mode.renderInitialMessages(undefined, { preserveExistingChat: true }));
852
+ if (options?.stopAfterFirstPaint) {
853
+ // GJC_TIMING=x probe: the cold-start measurement ends here, so tear the UI
854
+ // down with the same shutdown/stop order the interactive exit path uses.
855
+ await mode.shutdown();
856
+ mode.stop();
857
+ return;
858
+ }
789
859
 
790
860
  for (const notify of notifs) {
791
861
  if (!notify) {
@@ -986,7 +1056,7 @@ export async function createSessionManager(
986
1056
  parsed.sessionDir ? undefined : activeSettings.getAgentDir(),
987
1057
  );
988
1058
  if (!match) {
989
- throw new Error(`Session "${forkSource}" not found.`);
1059
+ throw new SessionNotFoundError(forkSource);
990
1060
  }
991
1061
  return await SessionManager.forkFrom(
992
1062
  match.session.path,
@@ -1017,7 +1087,7 @@ export async function createSessionManager(
1017
1087
  parsed.sessionDir ? undefined : activeSettings.getAgentDir(),
1018
1088
  );
1019
1089
  if (!match) {
1020
- throw new Error(`Session "${sessionArg}" not found.`);
1090
+ throw new SessionNotFoundError(sessionArg);
1021
1091
  }
1022
1092
  if (match.scope === "global") {
1023
1093
  const normalizedCwd = normalizePathForComparison(cwd);
@@ -1025,7 +1095,7 @@ export async function createSessionManager(
1025
1095
  if (normalizedCwd !== normalizedMatchCwd) {
1026
1096
  const shouldFork = await promptForkSession(match.session);
1027
1097
  if (!shouldFork) {
1028
- throw new Error(`Session "${sessionArg}" is in another project (${match.session.cwd}).`);
1098
+ throw new SessionForkDeclinedError(sessionArg, match.session.cwd);
1029
1099
  }
1030
1100
  return await SessionManager.forkFrom(
1031
1101
  match.session.path,
@@ -1297,8 +1367,9 @@ async function buildSessionOptions(
1297
1367
  options.rules = [];
1298
1368
  }
1299
1369
 
1300
- // General extension modules stay preloaded/explicit, while the SDK performs
1301
- // bounded native/Claude/Codex hook discovery through the canonical adapter.
1370
+ // Session startup discovers extension modules from the canonical locations, so
1371
+ // these stay at their defaults. `--extension` / `--no-extensions` are retired
1372
+ // ACP-only launch flags and deliberately do not feed the local startup path.
1302
1373
  options.disableExtensionDiscovery = false;
1303
1374
  options.additionalExtensionPaths = [];
1304
1375
 
@@ -1311,6 +1382,7 @@ export interface RunRootCommandDependencies {
1311
1382
  createAgentSession?: typeof createAgentSession;
1312
1383
  createSessionManager?: typeof createSessionManager;
1313
1384
  discoverAuthStorage?: typeof discoverAuthStorage;
1385
+ resolveStartupAuthConfig?: typeof resolveStartupAuthConfig;
1314
1386
  runAcpMode?: (options?: { agentDir?: string }) => Promise<void>;
1315
1387
  settings?: Settings;
1316
1388
  suppressProcessExit?: boolean;
@@ -1496,8 +1568,15 @@ export async function runRootCommand(
1496
1568
 
1497
1569
  const notifs: (InteractiveModeNotify | null)[] = [];
1498
1570
 
1499
- // Create AuthStorage and ModelRegistry upfront
1500
- const authStorage = await logger.time("discoverModels", deps.discoverAuthStorage ?? discoverAuthStorage);
1571
+ // Keep store authority and persistent pin intent in the same startup snapshot.
1572
+ // Injected discoverers own their auth unless they also supply a config resolver.
1573
+ const agentDir = deps.settings?.getAgentDir() ?? getAgentDir();
1574
+ const resolveAuthConfig =
1575
+ deps.resolveStartupAuthConfig ?? (deps.discoverAuthStorage ? undefined : resolveStartupAuthConfig);
1576
+ const startupAuthConfig = await resolveAuthConfig?.(agentDir);
1577
+ const authStorage = await logger.time("discoverModels", () =>
1578
+ (deps.discoverAuthStorage ?? discoverAuthStorage)(agentDir, startupAuthConfig),
1579
+ );
1501
1580
  const modelRegistry = new ModelRegistry(authStorage);
1502
1581
 
1503
1582
  if (parsedArgs.version) {
@@ -1838,6 +1917,7 @@ export async function runRootCommand(
1838
1917
  let rootStartupCacheAdmissionAttempted = false;
1839
1918
  sessionOptions.authStorage = authStorage;
1840
1919
  sessionOptions.modelRegistry = modelRegistry;
1920
+ sessionOptions.startupAuthConfig = startupAuthConfig;
1841
1921
  sessionOptions.modelRegistryStartupMutation = {
1842
1922
  owner: "cli-root",
1843
1923
  onAttempt: () => {
@@ -1847,9 +1927,16 @@ export async function runRootCommand(
1847
1927
  sessionOptions.hasUI = isInteractive;
1848
1928
  sessionOptions.notificationHostModeSupported = isInteractive;
1849
1929
  sessionOptions.sdkHostModeSupported = isInteractive;
1930
+ setPrimaryControlSurface(sessionOptions, "cli");
1850
1931
  sessionOptions.settings = settingsInstance;
1851
1932
  sessionOptions.masterModeContext = masterModeContext;
1852
- if (isInteractive && sessionOptions.mcpConfigPath) {
1933
+ // Interactive launches paint before MCP connects: the deferred starter runs
1934
+ // after first paint and a startup turn barrier keeps the first prompt from
1935
+ // racing tool registration. A no-op whenever no MCP servers apply — exact
1936
+ // `--mcp-config`, conventional autoload, or plugin bundles (plugins never
1937
+ // defer; their connect evidence is published during session creation).
1938
+ // ACP/print/SDK sessions connect eagerly and are not carved out here.
1939
+ if (isInteractive) {
1853
1940
  sessionOptions.deferMcpConfigStartup = true;
1854
1941
  }
1855
1942
  // ACP is not carved out: `gjc acp` is broker-backed and never builds a local
@@ -1958,7 +2045,7 @@ export async function runRootCommand(
1958
2045
  // every parallel arm by ~30ms. Startup model profiles do their own foreground refresh
1959
2046
  // before activation so project-scoped defaults can resolve freshly discovered models.
1960
2047
  if (!context?.skipPostCreateModelRefresh && !rootStartupCacheAdmissionAttempted) {
1961
- modelRegistry.refreshInBackground();
2048
+ modelRegistry.refreshInBackground("online-if-uncached", result.session.credentialSessionId);
1962
2049
  }
1963
2050
  return result;
1964
2051
  };
@@ -2062,10 +2149,12 @@ export async function runRootCommand(
2062
2149
  }
2063
2150
 
2064
2151
  if (isInteractive) {
2065
- let exitForTiming = false;
2152
+ const timingEnv = $pickenv("GJC_TIMING", "PI_TIMING");
2153
+ const exitForTiming = timingEnv === "x";
2154
+ let changelogMarkdown: string | undefined;
2066
2155
  try {
2067
2156
  startupUpdate.startBeforeInteractiveInitialization();
2068
- const changelogMarkdown = await logger.time(
2157
+ changelogMarkdown = await logger.time(
2069
2158
  "main:getChangelogForDisplay",
2070
2159
  deps.getChangelogForDisplay ?? getChangelogForDisplay,
2071
2160
  parsedArgs,
@@ -2082,9 +2171,8 @@ export async function runRootCommand(
2082
2171
  process.stdout.write(`${chalk.dim(`Model scope: ${modelList} ${chalk.gray("(Alt+N to cycle)")}`)}\n`);
2083
2172
  }
2084
2173
 
2085
- if ($pickenv("GJC_TIMING", "PI_TIMING")) {
2174
+ if (timingEnv && !exitForTiming) {
2086
2175
  logger.printTimings();
2087
- exitForTiming = $pickenv("GJC_TIMING", "PI_TIMING") === "x";
2088
2176
  }
2089
2177
 
2090
2178
  if (!exitForTiming) {
@@ -2121,6 +2209,39 @@ export async function runRootCommand(
2121
2209
  }
2122
2210
 
2123
2211
  if (exitForTiming) {
2212
+ // GJC_TIMING=x measures through the first transcript paint, so the
2213
+ // cold-start number includes interactive init and time-to-first-render.
2214
+ // The probe's deliverable is the timing report: a mode boot or teardown
2215
+ // failure is logged, but session disposal still precedes the exit (the
2216
+ // contract under startup-update-contract) and the report still prints.
2217
+ // This block sits outside the try/catch so the mock-throwing exit in
2218
+ // tests cannot unwind into a second disposal.
2219
+ try {
2220
+ await runInteractiveMode(
2221
+ session,
2222
+ VERSION,
2223
+ changelogMarkdown,
2224
+ notifs,
2225
+ startupUpdate,
2226
+ parsedArgs.messages,
2227
+ setToolUIContext,
2228
+ lspServers,
2229
+ mcpManager,
2230
+ eventBus,
2231
+ initialMessage,
2232
+ initialImages,
2233
+ deps.createInteractiveMode,
2234
+ bareResumeAction,
2235
+ startDeferredMcpConfig,
2236
+ startDeferredModelProfiles,
2237
+ { stopAfterFirstPaint: true },
2238
+ );
2239
+ } catch (error) {
2240
+ logger.warn("Timing probe interactive boot failed", { error: String(error) });
2241
+ }
2242
+ // Report before teardown so Total ends at first paint, not at the end
2243
+ // of session persistence and resource cleanup.
2244
+ logger.printTimings();
2124
2245
  await session.dispose();
2125
2246
  process.exit(0);
2126
2247
  }
@@ -973,14 +973,36 @@ function hasExactKeys(value: Record<string, unknown>, expectedKeys: string[], al
973
973
  function redactSecrets(input: string): string {
974
974
  let out = input;
975
975
  const patterns = [
976
+ // A PEM block is redacted whole and therefore runs first; the narrower rules
977
+ // would otherwise consume its base64 body and leave a truncated key behind.
978
+ /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
976
979
  /(?:sk|pk|rk|tok|key|secret|token|password)[-_A-Za-z0-9]{12,}/g,
977
- /[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
978
- /(?:AKIA|ASIA)[A-Z0-9]{16}/g,
980
+ // The leading boundary is anchored. Without it the first segment re-tries
981
+ // every offset of a long token-character run before failing to find `.`,
982
+ // which is quadratic: 200 KB of ordinary prose cost ~10.6s.
983
+ /(?<![A-Za-z0-9_-])[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
984
+ // ABIA (bearer) and ACCA (context) complete the AWS identifier prefixes the
985
+ // session-import and crash scrubbers already list.
986
+ /(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}/g,
979
987
  // GitHub tokens carry no keyword the first pattern recognizes, so without
980
988
  // this they reached MEMORY.md and memory_summary.md verbatim — and the
981
989
  // summary is injected into every later session. Same shapes the
982
990
  // contribution-prep scrubber already covers.
983
991
  /\b(?:gh[opsur]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/g,
992
+ // Google API keys are a fixed 39-character shape carrying no keyword.
993
+ /\bAIza[0-9A-Za-z_-]{35}\b/g,
994
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g,
995
+ // The shapes `crash/upstream/envelope.ts` classifies as credential-like.
996
+ // Stripe and Hugging Face separate with `_`, so the keyword rule above,
997
+ // which requires `sk`/`pk`/`rk` followed by `[-_A-Za-z0-9]`, still misses
998
+ // `hf_` and `npm_` entirely.
999
+ /\bnpm_[A-Za-z0-9]{20,}\b/g,
1000
+ /\bglpat-[A-Za-z0-9_-]{20,}\b/g,
1001
+ /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
1002
+ /\bhf_[A-Za-z0-9]{20,}\b/g,
1003
+ // Basic-auth credentials in a URL. The scheme repetition is bounded for the
1004
+ // same reason the boundary above is anchored.
1005
+ /(?<![A-Za-z0-9+.-])[a-z][a-z0-9+.-]{0,15}:\/\/[^/\s:@]{1,256}:[^/\s@]{1,256}@/gi,
984
1006
  ];
985
1007
  for (const pattern of patterns) {
986
1008
  out = out.replace(pattern, "[REDACTED]");