@sayknow-cli/coding-agent 0.3.7 → 0.3.8

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 (248) hide show
  1. package/dist/types/append-only-mode.d.ts +31 -0
  2. package/dist/types/capability/rule.d.ts +8 -0
  3. package/dist/types/cli/completion-cli.d.ts +41 -0
  4. package/dist/types/cli/config-cli.d.ts +1 -0
  5. package/dist/types/cli/fixture-report.d.ts +20 -0
  6. package/dist/types/cli/fixture-report.test.d.ts +1 -0
  7. package/dist/types/cli/nofile-limit.d.ts +11 -0
  8. package/dist/types/cli/notify-cli.d.ts +7 -0
  9. package/dist/types/cli/update-cli.d.ts +22 -0
  10. package/dist/types/cli.d.ts +124 -1
  11. package/dist/types/commands/completion.d.ts +29 -0
  12. package/dist/types/commands/config.d.ts +3 -0
  13. package/dist/types/commands/deep-interview.d.ts +3 -0
  14. package/dist/types/config/file-lock.d.ts +7 -6
  15. package/dist/types/config/keybindings.d.ts +2 -2
  16. package/dist/types/config/model-profile-activation.d.ts +6 -0
  17. package/dist/types/config/model-profiles.d.ts +1 -0
  18. package/dist/types/config/settings-schema.d.ts +105 -1
  19. package/dist/types/config/settings.d.ts +8 -0
  20. package/dist/types/discovery/index.d.ts +1 -0
  21. package/dist/types/export/html/template.generated.d.ts +1 -1
  22. package/dist/types/export/ttsr.d.ts +14 -3
  23. package/dist/types/extensibility/extensions/runner.d.ts +2 -1
  24. package/dist/types/extensibility/extensions/types.d.ts +8 -0
  25. package/dist/types/goals/runtime.d.ts +1 -0
  26. package/dist/types/hooks/native-skill-hook.d.ts +12 -0
  27. package/dist/types/main.d.ts +4 -1
  28. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  29. package/dist/types/modes/components/model-selector.d.ts +4 -0
  30. package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
  31. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  32. package/dist/types/modes/components/status-line.d.ts +9 -0
  33. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  34. package/dist/types/modes/controllers/event-controller.d.ts +5 -0
  35. package/dist/types/modes/controllers/input-controller.d.ts +1 -3
  36. package/dist/types/modes/interactive-mode.d.ts +2 -0
  37. package/dist/types/modes/print-mode.d.ts +13 -1
  38. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
  39. package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
  40. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
  41. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
  42. package/dist/types/modes/types.d.ts +1 -0
  43. package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
  44. package/dist/types/notifications/config.d.ts +14 -1
  45. package/dist/types/notifications/daemon-paths.d.ts +1 -0
  46. package/dist/types/notifications/index.d.ts +6 -3
  47. package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
  48. package/dist/types/notifications/telegram-daemon.d.ts +23 -3
  49. package/dist/types/notifications/telegram-reference.d.ts +0 -5
  50. package/dist/types/notifications/threaded-render.d.ts +6 -0
  51. package/dist/types/runtime-api-key.d.ts +7 -0
  52. package/dist/types/sdk.d.ts +3 -0
  53. package/dist/types/session/agent-session.d.ts +61 -6
  54. package/dist/types/session/agent-storage.d.ts +1 -0
  55. package/dist/types/session/file-mention-pruning.d.ts +8 -0
  56. package/dist/types/session/messages.d.ts +4 -0
  57. package/dist/types/session/session-manager.d.ts +17 -1
  58. package/dist/types/skc-runtime/deep-interview-ambiguity.d.ts +39 -0
  59. package/dist/types/skc-runtime/deep-interview-recorder.d.ts +10 -1
  60. package/dist/types/skc-runtime/deep-interview-state.d.ts +17 -2
  61. package/dist/types/skc-runtime/session-state-sidecar.d.ts +7 -0
  62. package/dist/types/skc-runtime/state-writer.d.ts +3 -0
  63. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
  64. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +120 -13
  65. package/dist/types/skill-state/active-state.d.ts +18 -1
  66. package/dist/types/system-prompt.d.ts +18 -2
  67. package/dist/types/task/executor.d.ts +3 -0
  68. package/dist/types/task/fork-context-budget.d.ts +7 -0
  69. package/dist/types/task/token-log.d.ts +30 -0
  70. package/dist/types/task/token-log.test.d.ts +1 -0
  71. package/dist/types/tools/bisect.d.ts +81 -0
  72. package/dist/types/tools/computer.d.ts +2 -2
  73. package/dist/types/tools/index.d.ts +1 -0
  74. package/dist/types/tools/search.d.ts +2 -2
  75. package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
  76. package/dist/types/utils/file-mentions.d.ts +14 -0
  77. package/dist/types/utils/git.d.ts +32 -0
  78. package/dist/types/web/scrapers/youtube.d.ts +8 -0
  79. package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
  80. package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
  81. package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
  82. package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
  83. package/package.json +8 -8
  84. package/src/append-only-mode.ts +58 -0
  85. package/src/autoresearch/prompt.md +1 -1
  86. package/src/autoresearch/tools/log-experiment.ts +1 -1
  87. package/src/capability/rule.ts +9 -0
  88. package/src/cli/completion-cli.ts +234 -0
  89. package/src/cli/config-cli.ts +61 -9
  90. package/src/cli/fast-help.ts +1 -0
  91. package/src/cli/fixture-report.test.ts +230 -0
  92. package/src/cli/fixture-report.ts +171 -0
  93. package/src/cli/nofile-limit.ts +48 -0
  94. package/src/cli/notify-cli.ts +92 -8
  95. package/src/cli/telegram-cli.ts +1 -1
  96. package/src/cli/update-cli.ts +85 -9
  97. package/src/cli.ts +29 -1
  98. package/src/commands/completion.ts +84 -0
  99. package/src/commands/config.ts +2 -0
  100. package/src/commands/contribution-prep.ts +1 -1
  101. package/src/commands/deep-interview.ts +2 -1
  102. package/src/config/file-lock.ts +73 -34
  103. package/src/config/keybindings.ts +2 -2
  104. package/src/config/model-profile-activation.ts +65 -0
  105. package/src/config/model-profiles.ts +31 -2
  106. package/src/config/settings-schema.ts +68 -1
  107. package/src/config/settings.ts +22 -0
  108. package/src/defaults/skc/skills/team/SKILL.md +17 -14
  109. package/src/defaults/skc/skills/ultragoal/SKILL.md +59 -4
  110. package/src/defaults/skc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
  111. package/src/defaults/skc-defaults.ts +9 -0
  112. package/src/discovery/helpers.ts +10 -0
  113. package/src/discovery/index.ts +1 -0
  114. package/src/export/html/template.generated.ts +1 -1
  115. package/src/export/html/template.js +60 -2
  116. package/src/export/ttsr.ts +96 -18
  117. package/src/extensibility/extensions/runner.ts +183 -219
  118. package/src/extensibility/extensions/types.ts +9 -0
  119. package/src/extensibility/skc-plugins/runtime-adapters.ts +156 -44
  120. package/src/goals/runtime.ts +10 -2
  121. package/src/hindsight/backend.ts +1 -3
  122. package/src/hooks/native-skill-hook.ts +124 -4
  123. package/src/internal-urls/docs-index.generated.ts +14 -5
  124. package/src/main.ts +95 -43
  125. package/src/memories/index.ts +12 -1
  126. package/src/modes/components/assistant-message.ts +241 -82
  127. package/src/modes/components/custom-editor.ts +1 -0
  128. package/src/modes/components/model-selector.ts +54 -11
  129. package/src/modes/components/queued-message-selector.ts +92 -0
  130. package/src/modes/components/settings-selector.ts +4 -0
  131. package/src/modes/components/status-line.ts +167 -54
  132. package/src/modes/components/tool-execution.ts +44 -19
  133. package/src/modes/components/welcome.ts +181 -44
  134. package/src/modes/controllers/command-controller.ts +40 -4
  135. package/src/modes/controllers/event-controller.ts +66 -47
  136. package/src/modes/controllers/extension-ui-controller.ts +4 -10
  137. package/src/modes/controllers/input-controller.ts +180 -18
  138. package/src/modes/controllers/selector-controller.ts +127 -24
  139. package/src/modes/interactive-mode.ts +51 -16
  140. package/src/modes/print-mode.ts +43 -4
  141. package/src/modes/prompt-action-autocomplete.ts +21 -20
  142. package/src/modes/rpc/rpc-mode.ts +29 -3
  143. package/src/modes/rpc/rpc-types.ts +9 -0
  144. package/src/modes/shared/agent-wire/command-validation.ts +4 -0
  145. package/src/modes/shared/agent-wire/event-contract.ts +22 -0
  146. package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
  147. package/src/modes/shared/agent-wire/scopes.ts +1 -0
  148. package/src/modes/types.ts +1 -0
  149. package/src/modes/utils/hotkeys-markdown.ts +1 -0
  150. package/src/modes/utils/ui-helpers.ts +20 -7
  151. package/src/notifications/config.ts +25 -5
  152. package/src/notifications/daemon-paths.ts +2 -0
  153. package/src/notifications/index.ts +117 -14
  154. package/src/notifications/lifecycle-commands.ts +37 -7
  155. package/src/notifications/lifecycle-orchestrator.ts +19 -6
  156. package/src/notifications/telegram-daemon-control.ts +5 -3
  157. package/src/notifications/telegram-daemon.ts +302 -51
  158. package/src/notifications/telegram-reference.ts +0 -16
  159. package/src/notifications/threaded-render.ts +17 -1
  160. package/src/prompts/agents/architect.md +19 -10
  161. package/src/prompts/agents/critic.md +16 -18
  162. package/src/prompts/agents/executor.md +14 -11
  163. package/src/prompts/agents/planner.md +18 -13
  164. package/src/prompts/ci-green-request.md +3 -8
  165. package/src/prompts/goals/goal-continuation.md +0 -5
  166. package/src/prompts/goals/goal-mode-active.md +0 -5
  167. package/src/prompts/system/agent-creation-architect.md +2 -2
  168. package/src/prompts/system/plan-mode-active.md +1 -1
  169. package/src/prompts/system/plan-mode-subagent.md +3 -3
  170. package/src/prompts/system/project-prompt.md +0 -13
  171. package/src/prompts/system/subagent-system-prompt.md +3 -2
  172. package/src/prompts/system/system-prompt.md +29 -17
  173. package/src/prompts/system/volatile-project-context.md +10 -0
  174. package/src/prompts/system/web-search.md +4 -7
  175. package/src/prompts/tools/apply-patch.md +6 -0
  176. package/src/prompts/tools/ast-edit.md +1 -0
  177. package/src/prompts/tools/bash.md +1 -1
  178. package/src/prompts/tools/bisect.md +22 -0
  179. package/src/prompts/tools/browser.md +1 -1
  180. package/src/prompts/tools/computer.md +2 -2
  181. package/src/prompts/tools/cron.md +0 -2
  182. package/src/prompts/tools/eval.md +2 -2
  183. package/src/prompts/tools/find.md +1 -1
  184. package/src/prompts/tools/github.md +6 -6
  185. package/src/prompts/tools/goal.md +1 -3
  186. package/src/prompts/tools/image-gen.md +12 -5
  187. package/src/prompts/tools/irc.md +5 -4
  188. package/src/prompts/tools/job.md +2 -2
  189. package/src/prompts/tools/lsp.md +1 -1
  190. package/src/prompts/tools/monitor.md +2 -2
  191. package/src/prompts/tools/patch.md +2 -2
  192. package/src/prompts/tools/replace.md +2 -21
  193. package/src/prompts/tools/rewind.md +1 -1
  194. package/src/prompts/tools/search-tool-bm25.md +1 -11
  195. package/src/prompts/tools/search.md +1 -1
  196. package/src/prompts/tools/skill.md +3 -2
  197. package/src/prompts/tools/ssh.md +2 -1
  198. package/src/prompts/tools/subagent.md +2 -2
  199. package/src/prompts/tools/task.md +4 -5
  200. package/src/prompts/tools/todo-write.md +1 -1
  201. package/src/prompts/tools/vim.md +1 -0
  202. package/src/prompts/tools/web-search.md +2 -0
  203. package/src/prompts/tools/write.md +8 -2
  204. package/src/runtime-api-key.ts +16 -0
  205. package/src/sdk.ts +52 -28
  206. package/src/session/agent-session.ts +1011 -141
  207. package/src/session/agent-storage.ts +4 -0
  208. package/src/session/file-mention-pruning.ts +64 -0
  209. package/src/session/messages.ts +4 -0
  210. package/src/session/session-manager.ts +103 -4
  211. package/src/skc-runtime/deep-interview-ambiguity.ts +206 -0
  212. package/src/skc-runtime/deep-interview-recorder.ts +62 -8
  213. package/src/skc-runtime/deep-interview-state.ts +44 -2
  214. package/src/skc-runtime/session-state-sidecar.ts +263 -20
  215. package/src/skc-runtime/state-runtime.ts +12 -2
  216. package/src/skc-runtime/state-writer.ts +15 -0
  217. package/src/skc-runtime/ultragoal-guard.ts +55 -66
  218. package/src/skc-runtime/ultragoal-receipt-freshness.ts +315 -0
  219. package/src/skc-runtime/ultragoal-runtime.ts +1663 -159
  220. package/src/skc-runtime/workflow-manifest.generated.json +113 -2
  221. package/src/skc-runtime/workflow-manifest.ts +74 -1
  222. package/src/skill-state/active-state.ts +141 -5
  223. package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
  224. package/src/slash-commands/builtin-registry.ts +218 -71
  225. package/src/system-prompt.ts +57 -16
  226. package/src/task/executor.ts +132 -24
  227. package/src/task/fork-context-advisory.ts +2 -7
  228. package/src/task/fork-context-budget.ts +9 -0
  229. package/src/task/index.ts +13 -6
  230. package/src/task/output-manager.ts +20 -8
  231. package/src/task/receipt.ts +26 -7
  232. package/src/task/token-log.test.ts +188 -0
  233. package/src/task/token-log.ts +179 -0
  234. package/src/tools/bisect.ts +367 -0
  235. package/src/tools/computer.ts +246 -43
  236. package/src/tools/index.ts +3 -0
  237. package/src/tools/output-meta.ts +116 -4
  238. package/src/tools/search.ts +8 -2
  239. package/src/tools/skill.ts +15 -0
  240. package/src/tools/telegram-send.ts +6 -0
  241. package/src/tools/ultragoal-ask-guard.ts +39 -5
  242. package/src/utils/file-mentions.ts +39 -7
  243. package/src/utils/git.ts +73 -0
  244. package/src/web/scrapers/youtube.ts +24 -12
  245. package/src/workflow/workflow-intent-diff.ts +29 -6
  246. package/src/workflow/workflow-intent-report-consumer.ts +221 -0
  247. package/src/workflow/workflow-intent-report-schema.ts +129 -0
  248. package/src/workflow/workflow-intent-report.ts +135 -0
package/src/main.ts CHANGED
@@ -38,6 +38,7 @@ import type { ExtensionUIContext } from "./extensibility/extensions/types";
38
38
  import type { InteractiveMode } from "./modes/interactive-mode";
39
39
  import { initTheme, stopThemeWatcher } from "./modes/theme/theme";
40
40
  import type { SubmittedUserInput } from "./modes/types";
41
+ import { applyCliRuntimeApiKeyOverride } from "./runtime-api-key";
41
42
  import type { MCPManager } from "./runtime-mcp";
42
43
  import {
43
44
  type CreateAgentSessionOptions,
@@ -52,6 +53,7 @@ import { runStartupCredentialAutoImportIfNeeded } from "./setup/credential-auto-
52
53
  import { formatModelOnboardingGuidance } from "./setup/model-onboarding-guidance";
53
54
  import { executeBuiltinSlashCommand } from "./slash-commands/builtin-registry";
54
55
  import { resolvePromptInput } from "./system-prompt";
56
+ import { persistTaskTokenLog, resolveTaskTokenLogDir, taskTokenLogFromUsage } from "./task/token-log";
55
57
  import type { LspStartupServerInfo } from "./tools";
56
58
  import { getDisplayChangelogEntries, getInstalledVersionChangelogEntry, getNewEntries } from "./utils/changelog";
57
59
  import type { EventBus } from "./utils/event-bus";
@@ -190,6 +192,11 @@ function applyExtensionFlagValues(session: AgentSession, rawArgs: string[]): Map
190
192
  return extensionRunner.getFlagValues();
191
193
  }
192
194
 
195
+ type CreateSessionForMain = (
196
+ options: CreateAgentSessionOptions,
197
+ context?: { skipPostCreateModelRefresh?: boolean },
198
+ ) => Promise<CreateAgentSessionResult>;
199
+
193
200
  type AcpSessionFactory = (cwd: string) => Promise<AgentSession>;
194
201
 
195
202
  export interface AcpSessionFactoryOptions {
@@ -200,7 +207,7 @@ export interface AcpSessionFactoryOptions {
200
207
  modelRegistry: ModelRegistry;
201
208
  parsedArgs: Pick<Args, "apiKey" | "default" | "model" | "mpreset" | "thinking">;
202
209
  rawArgs: string[];
203
- createSession: (options: CreateAgentSessionOptions) => Promise<CreateAgentSessionResult>;
210
+ createSession: CreateSessionForMain;
204
211
  }
205
212
 
206
213
  export async function applyStartupModelProfiles(args: {
@@ -222,8 +229,11 @@ export async function applyStartupModelProfiles(args: {
222
229
  // override it. startupModel covers the eager path; session.model covers the
223
230
  // deferred `--model <pattern>` path resolved inside createAgentSession.
224
231
  const explicitModel = args.parsedArgs.model ? (args.startupModel ?? args.session.model) : undefined;
225
-
226
232
  const defaultProfile = args.settings.get("modelProfile.default");
233
+ if (defaultProfile || args.parsedArgs.mpreset) {
234
+ await args.modelRegistry.refresh("online-if-uncached");
235
+ }
236
+
227
237
  if (defaultProfile) {
228
238
  await applyProfile(defaultProfile, false);
229
239
  }
@@ -264,19 +274,23 @@ export async function applyStartupModelProfilesOrExit(
264
274
  export function createAcpSessionFactory(args: AcpSessionFactoryOptions): AcpSessionFactory {
265
275
  return async cwd => {
266
276
  const nextSettings = await args.settings.cloneForCwd(cwd);
277
+ const hasStartupProfile = Boolean(nextSettings.get("modelProfile.default") || args.parsedArgs.mpreset);
267
278
  const nextSessionManager = SessionManager.create(cwd, args.sessionDir);
268
279
  const agentId = `acp:${nextSessionManager.getSessionId()}`;
269
- const { session: nextSession } = await args.createSession({
270
- ...args.baseOptions,
271
- cwd,
272
- sessionManager: nextSessionManager,
273
- settings: nextSettings,
274
- authStorage: args.authStorage,
275
- modelRegistry: args.modelRegistry,
276
- agentId,
277
- hasUI: false,
278
- enableMCP: false,
279
- });
280
+ const { session: nextSession } = await args.createSession(
281
+ {
282
+ ...args.baseOptions,
283
+ cwd,
284
+ sessionManager: nextSessionManager,
285
+ settings: nextSettings,
286
+ authStorage: args.authStorage,
287
+ modelRegistry: args.modelRegistry,
288
+ agentId,
289
+ hasUI: false,
290
+ enableMCP: false,
291
+ },
292
+ { skipPostCreateModelRefresh: hasStartupProfile },
293
+ );
280
294
  await applyStartupModelProfilesOrExit({
281
295
  session: nextSession,
282
296
  settings: nextSettings,
@@ -285,9 +299,7 @@ export function createAcpSessionFactory(args: AcpSessionFactoryOptions): AcpSess
285
299
  startupModel: args.baseOptions.model,
286
300
  startupThinkingLevel: args.baseOptions.thinkingLevel,
287
301
  });
288
- if (args.parsedArgs.apiKey && !args.baseOptions.model && nextSession.model) {
289
- args.authStorage.setRuntimeApiKey(nextSession.model.provider, args.parsedArgs.apiKey);
290
- }
302
+ applyCliRuntimeApiKeyOverride(args.authStorage, args.parsedArgs.apiKey, nextSession.model);
291
303
  applyExtensionFlagValues(nextSession, args.rawArgs);
292
304
  return nextSession;
293
305
  };
@@ -396,30 +408,28 @@ async function getChangelogForDisplay(parsed: Args): Promise<string | undefined>
396
408
  return undefined;
397
409
  }
398
410
 
399
- const lastVersion = settings.get("lastChangelogVersion");
400
- if (lastVersion === VERSION) {
401
- // Steady state: user already saw the current version's changelog. Skip the file read + parse.
411
+ const entries = getDisplayChangelogEntries();
412
+ if (entries.length === 0) {
402
413
  return undefined;
403
414
  }
404
415
 
405
- const entries = getDisplayChangelogEntries();
406
-
416
+ const lastVersion = settings.get("lastChangelogVersion");
407
417
  if (!lastVersion) {
408
- if (entries.length > 0) {
409
- settings.set("lastChangelogVersion", VERSION);
410
- await flushChangelogVersion();
411
- return getInstalledVersionChangelogEntry(entries, VERSION)?.content;
412
- }
413
- } else {
418
+ settings.set("lastChangelogVersion", VERSION);
419
+ await flushChangelogVersion();
420
+ return getInstalledVersionChangelogEntry(entries, VERSION)?.content;
421
+ }
422
+
423
+ if (lastVersion !== VERSION) {
414
424
  const newEntries = getNewEntries(entries, lastVersion);
425
+ settings.set("lastChangelogVersion", VERSION);
426
+ await flushChangelogVersion();
415
427
  if (newEntries.length > 0) {
416
- settings.set("lastChangelogVersion", VERSION);
417
- await flushChangelogVersion();
418
428
  return newEntries.map(e => e.content).join("\n\n");
419
429
  }
420
430
  }
421
431
 
422
- return undefined;
432
+ return getInstalledVersionChangelogEntry(entries, VERSION)?.content;
423
433
  }
424
434
 
425
435
  async function flushChangelogVersion(): Promise<void> {
@@ -505,8 +515,8 @@ export async function createSessionManager(
505
515
  }
506
516
  return manager;
507
517
  }
508
- // Default case (new session) returns undefined, SDK will create one
509
- return undefined;
518
+ const sessionDir = parsed.sessionDir ?? SessionManager.getDefaultSessionDir(cwd, activeSettings.getAgentDir());
519
+ return SessionManager.create(cwd, sessionDir);
510
520
  }
511
521
 
512
522
  async function maybeAutoChdir(parsed: Args): Promise<void> {
@@ -945,10 +955,51 @@ export async function runRootCommand(
945
955
  modelRegistry,
946
956
  settingsInstance,
947
957
  );
958
+ // Resolve the token-log dir lazily on the first chat-usage event: a fresh
959
+ // launch's session dir does not exist yet at this point (the SDK creates it),
960
+ // so eager resolution would miss it. Re-resolve when the SessionManager id
961
+ // changes mid-run (fork/new) so root turns keep landing in the SAME
962
+ // `<session>/token-logs` dir the task executor uses for subagent turns.
963
+ let rootTokenLogDir: string | undefined;
964
+ let rootTokenLogSessionId: string | undefined;
965
+ let rootTokenLogResolved = false;
966
+ let rootTokenTurn = 0;
967
+ const baseTelemetry = sessionOptions.telemetry;
968
+ sessionOptions.telemetry = {
969
+ ...(baseTelemetry ?? {}),
970
+ onChatUsage: async event => {
971
+ await baseTelemetry?.onChatUsage?.(event);
972
+ const currentSessionId = sessionManager?.getSessionId();
973
+ if (!rootTokenLogResolved || (currentSessionId && currentSessionId !== rootTokenLogSessionId)) {
974
+ rootTokenLogDir = await resolveTaskTokenLogDir(process.cwd(), sessionManager);
975
+ // Reset the per-session turn counter when the log dir moves to a new
976
+ // session so each session's log starts at turn 1.
977
+ if (rootTokenLogSessionId !== undefined && currentSessionId !== rootTokenLogSessionId) rootTokenTurn = 0;
978
+ rootTokenLogSessionId = currentSessionId;
979
+ rootTokenLogResolved = true;
980
+ }
981
+ if (!rootTokenLogDir) return;
982
+ rootTokenTurn += 1;
983
+ await persistTaskTokenLog(
984
+ taskTokenLogFromUsage(event.usage, {
985
+ subagentId: "root",
986
+ agent: event.agent?.name ?? "main",
987
+ // Monotonic 1-based sequence of persisted usage events for this
988
+ // session (event.stepNumber is 0-based and -1 for oneshot spans).
989
+ turn: rootTokenTurn,
990
+ at: new Date().toISOString(),
991
+ model: event.model,
992
+ cost: event.cost,
993
+ }),
994
+ { dir: rootTokenLogDir },
995
+ );
996
+ },
997
+ };
948
998
  sessionOptions.authStorage = authStorage;
949
999
  sessionOptions.modelRegistry = modelRegistry;
950
1000
  sessionOptions.hasUI = isInteractive || mode === "rpc-ui";
951
1001
  sessionOptions.settings = settingsInstance;
1002
+ const hasRootStartupProfile = Boolean(settingsInstance.get("modelProfile.default") || parsedArgs.mpreset);
952
1003
 
953
1004
  // Research-mode (RLM) preset: augment session options before session creation.
954
1005
  deps.rlmPreset?.applyOptions(sessionOptions, settingsInstance);
@@ -961,18 +1012,19 @@ export async function runRootCommand(
961
1012
  );
962
1013
  process.exit(1);
963
1014
  }
964
- if (sessionOptions.model) {
965
- authStorage.setRuntimeApiKey(sessionOptions.model.provider, parsedArgs.apiKey);
966
- }
1015
+ applyCliRuntimeApiKeyOverride(authStorage, parsedArgs.apiKey, sessionOptions.model);
967
1016
  }
968
1017
 
969
1018
  const createAgentSessionImpl = deps.createAgentSession ?? createAgentSession;
970
- const createSession = async (options: CreateAgentSessionOptions): Promise<CreateAgentSessionResult> => {
1019
+ const createSession: CreateSessionForMain = async (options, context): Promise<CreateAgentSessionResult> => {
971
1020
  const result = await logger.time("createAgentSession", createAgentSessionImpl, options);
972
1021
  // Kick off background model discovery only after createAgentSession finishes its parallel
973
1022
  // discovery arms; running these concurrently contends for the event loop and stretches
974
- // every parallel arm by ~30ms.
975
- modelRegistry.refreshInBackground();
1023
+ // every parallel arm by ~30ms. Startup model profiles do their own foreground refresh
1024
+ // before activation so project-scoped defaults can resolve freshly discovered models.
1025
+ if (!context?.skipPostCreateModelRefresh) {
1026
+ modelRegistry.refreshInBackground();
1027
+ }
976
1028
  return result;
977
1029
  };
978
1030
 
@@ -989,11 +1041,11 @@ export async function runRootCommand(
989
1041
  });
990
1042
  await (deps.runAcpMode ?? (await import("./modes/acp")).runAcpMode)(createAcpSession);
991
1043
  } else {
992
- const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } =
993
- await createSession(sessionOptions);
994
- if (parsedArgs.apiKey && !sessionOptions.model && session.model) {
995
- authStorage.setRuntimeApiKey(session.model.provider, parsedArgs.apiKey);
996
- }
1044
+ const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager, eventBus } = await createSession(
1045
+ sessionOptions,
1046
+ { skipPostCreateModelRefresh: hasRootStartupProfile },
1047
+ );
1048
+ applyCliRuntimeApiKeyOverride(authStorage, parsedArgs.apiKey, session.model);
997
1049
 
998
1050
  // Research-mode (RLM) preset: hard tool-boundary assertion after the registry is assembled.
999
1051
  if (deps.rlmPreset?.onSessionCreated) {
@@ -1083,7 +1083,18 @@ async function resolveMemoryModel(options: {
1083
1083
  });
1084
1084
  if (resolved.model) return resolved.model;
1085
1085
  }
1086
- return session.model ?? modelRegistry.getAll()[0];
1086
+ if (session.model) return session.model;
1087
+ // No configured role and no active session model (e.g. background startup
1088
+ // before lazy model resolution): prefer the most recently used model over
1089
+ // raw registry order. `getAll()[0]` can be a retired model the provider
1090
+ // now rejects with 404, which permanently fails consolidation jobs.
1091
+ const allModels = modelRegistry.getAll();
1092
+ const usageOrder = session.settings.getStorage()?.getModelUsageOrder() ?? [];
1093
+ for (const usedKey of usageOrder) {
1094
+ const match = allModels.find(model => `${model.provider}/${model.id}` === usedKey);
1095
+ if (match) return match;
1096
+ }
1097
+ return allModels[0];
1087
1098
  }
1088
1099
 
1089
1100
  function loadMemoryConfig(settings: Settings): MemoryRuntimeConfig {