@asm-agent/coding-agent 0.8.7 → 0.9.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 (195) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +2 -7
  3. package/dist/asm-agent-runtime/src/__pycache__/asm_cm_driver.cpython-313.pyc +0 -0
  4. package/dist/asm-agent-runtime/src/__pycache__/phase76_address_head.cpython-313.pyc +0 -0
  5. package/dist/asm-agent-runtime/src/rlm/__init__.py +1 -1
  6. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-312.pyc +0 -0
  7. package/dist/asm-agent-runtime/src/rlm/__pycache__/__init__.cpython-313.pyc +0 -0
  8. package/dist/asm-agent-runtime/src/rlm/__pycache__/_winjob.cpython-313.pyc +0 -0
  9. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-312.pyc +0 -0
  10. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-313.pyc +0 -0
  11. package/dist/asm-agent-runtime/src/rlm/__pycache__/harness.cpython-313.pyc +0 -0
  12. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-312.pyc +0 -0
  13. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-313.pyc +0 -0
  14. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-312.pyc +0 -0
  15. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-313.pyc +0 -0
  16. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
  17. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-313.pyc +0 -0
  18. package/dist/asm-agent-runtime/src/rlm/__pycache__/skill.cpython-313.pyc +0 -0
  19. package/dist/asm-agent-runtime/src/rlm/bash.py +94 -31
  20. package/dist/asm-agent-runtime/src/rlm/repl.md +3 -3
  21. package/dist/asm-agent-runtime/src/rlm/repl.py +46 -45
  22. package/dist/asm-agent-runtime/test/__pycache__/test_agent_message_skill.cpython-313.pyc +0 -0
  23. package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-313.pyc +0 -0
  24. package/dist/asm-agent-runtime/test/__pycache__/test_harness.cpython-313.pyc +0 -0
  25. package/dist/asm-agent-runtime/test/__pycache__/test_mcp.cpython-313.pyc +0 -0
  26. package/dist/asm-agent-runtime/test/__pycache__/test_mcp_base.cpython-313.pyc +0 -0
  27. package/dist/asm-agent-runtime/test/__pycache__/test_repl.cpython-313.pyc +0 -0
  28. package/dist/asm-agent-runtime/test/__pycache__/test_subagent_registry.cpython-313.pyc +0 -0
  29. package/dist/asm-agent-runtime/test/__pycache__/test_winjob.cpython-313.pyc +0 -0
  30. package/dist/asm-agent-runtime/test/test_bash.py +74 -19
  31. package/dist/asm-agent-runtime/test/test_repl.py +67 -3
  32. package/dist/asm-agent-runtime/uv.lock +446 -155
  33. package/dist/bundle/{anthropic-XSMQHGYB.js → anthropic-ZOZNPFXS.js} +2 -2
  34. package/dist/bundle/{azure-openai-responses-XLQAIKKH.js → azure-openai-responses-TDHCU647.js} +2 -2
  35. package/dist/bundle/{bundled-modules-5OCN73BM.js → bundled-modules-Q2YODC4B.js} +3 -3
  36. package/dist/bundle/{chunk-IAQLPSMN.js → chunk-AMKLNEAK.js} +47 -47
  37. package/dist/bundle/{chunk-2LPSALBF.js → chunk-FPY7FFJS.js} +978 -1821
  38. package/dist/bundle/{chunk-E3YE2Q7A.js → chunk-G6SC4YYW.js} +4973 -1926
  39. package/dist/bundle/{chunk-YPNK4MRU.js → chunk-QLHAP3FT.js} +1 -1
  40. package/dist/bundle/{cli-main-QYX5R3LF.js → cli-main-277AG2BR.js} +3 -3
  41. package/dist/bundle/cli.js +1 -1
  42. package/dist/bundle/{google-ZXJEPWJA.js → google-Q3AJWUOY.js} +1 -1
  43. package/dist/bundle/{google-vertex-WAF3RIJ6.js → google-vertex-2CSNOUBS.js} +1 -1
  44. package/dist/bundle/{main-EOUAQCJH.js → main-TNXWJXIU.js} +3 -3
  45. package/dist/bundle/{mistral-GDZXSXRS.js → mistral-XRYJGLLG.js} +1 -1
  46. package/dist/bundle/{openai-codex-responses-7XMPW4LG.js → openai-codex-responses-C6BRO2BN.js} +2 -2
  47. package/dist/bundle/{openai-completions-LNSVGDMF.js → openai-completions-DNXCK2JW.js} +4 -4
  48. package/dist/bundle/{openai-responses-MFZOBMIT.js → openai-responses-BEMWVIXE.js} +2 -2
  49. package/dist/config.d.ts +0 -1
  50. package/dist/config.d.ts.map +1 -1
  51. package/dist/config.js +0 -3
  52. package/dist/config.js.map +1 -1
  53. package/dist/core/agent-session-config.d.ts +1 -2
  54. package/dist/core/agent-session-config.d.ts.map +1 -1
  55. package/dist/core/agent-session-config.js +0 -3
  56. package/dist/core/agent-session-config.js.map +1 -1
  57. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  58. package/dist/core/agent-session-runtime.js +0 -8
  59. package/dist/core/agent-session-runtime.js.map +1 -1
  60. package/dist/core/agent-session-services.d.ts +0 -2
  61. package/dist/core/agent-session-services.d.ts.map +1 -1
  62. package/dist/core/agent-session-services.js +0 -22
  63. package/dist/core/agent-session-services.js.map +1 -1
  64. package/dist/core/agent-session.d.ts +8 -1
  65. package/dist/core/agent-session.d.ts.map +1 -1
  66. package/dist/core/agent-session.js +100 -22
  67. package/dist/core/agent-session.js.map +1 -1
  68. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  69. package/dist/core/kernel/bootstrap.js +1 -1
  70. package/dist/core/kernel/bootstrap.js.map +1 -1
  71. package/dist/core/kernel/repl-manager.d.ts +32 -8
  72. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  73. package/dist/core/kernel/repl-manager.js +401 -102
  74. package/dist/core/kernel/repl-manager.js.map +1 -1
  75. package/dist/core/kernel/shared.d.ts +11 -7
  76. package/dist/core/kernel/shared.d.ts.map +1 -1
  77. package/dist/core/kernel/shared.js +2 -5
  78. package/dist/core/kernel/shared.js.map +1 -1
  79. package/dist/core/mcp/mcp-manager.d.ts +4 -2
  80. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  81. package/dist/core/mcp/mcp-manager.js +9 -7
  82. package/dist/core/mcp/mcp-manager.js.map +1 -1
  83. package/dist/core/model-registry.d.ts.map +1 -1
  84. package/dist/core/model-registry.js +1 -1
  85. package/dist/core/model-registry.js.map +1 -1
  86. package/dist/core/ollama-models.d.ts +3 -1
  87. package/dist/core/ollama-models.d.ts.map +1 -1
  88. package/dist/core/ollama-models.js +31 -10
  89. package/dist/core/ollama-models.js.map +1 -1
  90. package/dist/core/prime-inference-auth.d.ts +0 -9
  91. package/dist/core/prime-inference-auth.d.ts.map +1 -1
  92. package/dist/core/prime-inference-auth.js +0 -50
  93. package/dist/core/prime-inference-auth.js.map +1 -1
  94. package/dist/core/provider-display-names.d.ts.map +1 -1
  95. package/dist/core/provider-display-names.js +0 -1
  96. package/dist/core/provider-display-names.js.map +1 -1
  97. package/dist/core/session-manager.d.ts +0 -4
  98. package/dist/core/session-manager.d.ts.map +1 -1
  99. package/dist/core/session-manager.js +0 -22
  100. package/dist/core/session-manager.js.map +1 -1
  101. package/dist/core/settings-manager.d.ts +4 -16
  102. package/dist/core/settings-manager.d.ts.map +1 -1
  103. package/dist/core/settings-manager.js +13 -43
  104. package/dist/core/settings-manager.js.map +1 -1
  105. package/dist/core/slash-commands.d.ts.map +1 -1
  106. package/dist/core/slash-commands.js +0 -5
  107. package/dist/core/slash-commands.js.map +1 -1
  108. package/dist/core/tools/acp-mcp.d.ts +6 -0
  109. package/dist/core/tools/acp-mcp.d.ts.map +1 -0
  110. package/dist/core/tools/acp-mcp.js +86 -0
  111. package/dist/core/tools/acp-mcp.js.map +1 -0
  112. package/dist/core/tools/index.d.ts +1 -0
  113. package/dist/core/tools/index.d.ts.map +1 -1
  114. package/dist/core/tools/index.js +1 -0
  115. package/dist/core/tools/index.js.map +1 -1
  116. package/dist/core/tools/ipython.d.ts.map +1 -1
  117. package/dist/core/tools/ipython.js +9 -4
  118. package/dist/core/tools/ipython.js.map +1 -1
  119. package/dist/main.d.ts.map +1 -1
  120. package/dist/main.js +2 -11
  121. package/dist/main.js.map +1 -1
  122. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  123. package/dist/modes/acp/acp-mode.js +1 -1
  124. package/dist/modes/acp/acp-mode.js.map +1 -1
  125. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +0 -2
  126. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  127. package/dist/modes/agent-connection/daemon-agent-connection.js +0 -2
  128. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  129. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  130. package/dist/modes/agents-view/agents-view-mode.js +0 -19
  131. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  132. package/dist/modes/daemon/daemon-mode.d.ts +1 -10
  133. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  134. package/dist/modes/daemon/daemon-mode.js +134 -274
  135. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  136. package/dist/modes/daemon/daemon-protocol.d.ts +11 -5
  137. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  138. package/dist/modes/daemon/daemon-protocol.js +6 -8
  139. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  140. package/dist/modes/daemon/daemon-supervisor.d.ts +0 -3
  141. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  142. package/dist/modes/daemon/daemon-supervisor.js +18 -52
  143. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  144. package/dist/modes/daemon/daemon-worker-protocol.d.ts +1 -6
  145. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  146. package/dist/modes/daemon/daemon-worker-protocol.js +0 -2
  147. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  148. package/dist/modes/interactive/auth-flows.d.ts +0 -2
  149. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  150. package/dist/modes/interactive/auth-flows.js +1 -101
  151. package/dist/modes/interactive/auth-flows.js.map +1 -1
  152. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  153. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/assistant-message.js +13 -3
  155. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  156. package/dist/modes/interactive/components/footer.d.ts +1 -1
  157. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  158. package/dist/modes/interactive/components/footer.js +2 -2
  159. package/dist/modes/interactive/components/footer.js.map +1 -1
  160. package/dist/modes/interactive/components/mermaid.d.ts +12 -0
  161. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  162. package/dist/modes/interactive/components/mermaid.js +63 -0
  163. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  164. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  165. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  166. package/dist/modes/interactive/components/settings-selector.js +10 -0
  167. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  168. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  169. package/dist/modes/interactive/feature-hints.js +0 -4
  170. package/dist/modes/interactive/feature-hints.js.map +1 -1
  171. package/dist/modes/interactive/interactive-mode.d.ts +1 -7
  172. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  173. package/dist/modes/interactive/interactive-mode.js +23 -279
  174. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  175. package/dist/utils/version-check.d.ts.map +1 -1
  176. package/dist/utils/version-check.js +1 -1
  177. package/dist/utils/version-check.js.map +1 -1
  178. package/docs/settings.md +1 -31
  179. package/docs/usage.md +1 -4
  180. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  181. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  182. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  183. package/examples/extensions/sandbox/package-lock.json +2 -2
  184. package/examples/extensions/sandbox/package.json +1 -1
  185. package/examples/extensions/with-deps/package-lock.json +2 -2
  186. package/examples/extensions/with-deps/package.json +1 -1
  187. package/package.json +6 -5
  188. package/dist/core/agent-traces.d.ts +0 -130
  189. package/dist/core/agent-traces.d.ts.map +0 -1
  190. package/dist/core/agent-traces.js +0 -746
  191. package/dist/core/agent-traces.js.map +0 -1
  192. package/dist/core/telemetry.d.ts +0 -92
  193. package/dist/core/telemetry.d.ts.map +0 -1
  194. package/dist/core/telemetry.js +0 -633
  195. package/dist/core/telemetry.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EAAE,KAAK,IAAI,EAAwB,MAAM,eAAe,CAAC;AAuBhE,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAG9B,MAAM,gCAAgC,CAAC;AAexC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAMnE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAQ/D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAc3D,OAAO,EAuBN,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAqD1B,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC5C,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE5C,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAE3G;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAEjH;AAyBD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAEzF;AAGD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,kBAAkB,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKtG;AAED,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,gCAAgC,GAAG,2BAA2B,CAAC;AAE3E,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAOxF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAInF;AAED,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,2BAA2B,GAAG;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iCAAiC,CAAC,EAAE,OAAO,CAAC;CAC5C,GACC,OAAO,CAET;AAED,wBAAgB,uCAAuC,CACtD,oBAAoB,EAAE,OAAO,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAET;AAED,MAAM,WAAW,yBAAyB;IACzC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wCAAwC,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAWpG;AAED,MAAM,WAAW,uCAAuC;IACvD,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,oDAAoD,CACnE,OAAO,EAAE,uCAAuC,GAC9C,OAAO,CAOT;AAED,MAAM,WAAW,iCAAiC;IACjD,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,2CAA2C,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAM/G;AAED,KAAK,gCAAgC,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;AAEtH,UAAU,uCAAuC;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,gCAAgC,CAAC;CAC1C;AAED,wBAAsB,mDAAmD,CACxE,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uCAA4C,GACnD,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CASrC;AAmID,wBAAsB,oBAAoB,CACzC,MAAM,EAAE,IAAI,EACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,cAAc,CAAC,CA4CzB;AA4LD,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,yBAAyB,GAAG,yBAAyB,CAE7G;AAED,wBAAgB,4BAA4B,CAC3C,cAAc,EAAE,yBAAyB,EACzC,qBAAqB,CAAC,EAAE,yBAAyB,GAC/C,yBAAyB,CA2B3B;AAqND,wBAAgB,4CAA4C,CAC3D,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,WAAW,EAAE,MAAM,GACjB,cAAc,GAAG,SAAS,CAQ5B;AAyFD,MAAM,WAAW,WAAW;IAC3B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,sBA+pB/D","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport { join, resolve } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { type Api, type ImageContent, type Model, modelsAreEqual } from \"@asm-agent/ai\";\nimport { registerBuiltinMcpOAuthProviders } from \"@asm-agent/ai/mcp\";\nimport { ProcessTerminal, setKeybindings, TUI } from \"@asm-agent/tui\";\nimport chalk from \"chalk\";\nimport { type Args, type Mode, parseArgs } from \"./cli/args.js\";\nimport { formatTopLevelHelp } from \"./cli/command-registry.js\";\nimport {\n\tensureInteractiveDaemonRunning,\n\tisDaemonSessionSummary,\n\tlistActiveDaemonSessionSummaries,\n\tprobeRunningDaemonSessions,\n\tStaleDaemonError,\n\tshutdownDaemonAndWait,\n} from \"./cli/daemon-launch.js\";\nimport { confirmDaemonSessionLoss, type DaemonSessionLossCopy, pluralizeSessions } from \"./cli/daemon-stop-confirm.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { buildInitialMessage } from \"./cli/initial-message.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { installOwnedSessionRecoveryTracking, isOwnedSessionWorkerProcess } from \"./cli/owned-session-worker.js\";\nimport { handlePublicCommand } from \"./cli/public-command.js\";\nimport {\n\tlooksLikeSessionPath,\n\tresolveSessionPath,\n\tSessionSelectorError,\n\tSessionSelectorNotFoundError,\n} from \"./cli/session-resolver.js\";\nimport { APP_NAME, expandTildePath, getAgentDir, getSessionDirEnvOverride, VERSION } from \"./config.js\";\nimport {\n\ttype AgentExecutionMode,\n\ttype AgentSessionRuntimeConfig,\n\tmergeAgentSessionRuntimeConfig,\n\tmergeAutonomousConfig,\n} from \"./core/agent-session-config.js\";\nimport {\n\ttype AgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"./core/agent-session-runtime.js\";\nimport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./core/agent-session-services.js\";\nimport { formatNoModelsAvailableMessage } from \"./core/auth-guidance.js\";\nimport { AuthStorage } from \"./core/auth-storage.js\";\nimport { exportFromFile } from \"./core/export-html/index.js\";\nimport type { ExtensionFactory } from \"./core/extensions/types.js\";\nimport { KeybindingsManager } from \"./core/keybindings.js\";\nimport { installFileLogSink, setLogContext } from \"./core/logging.js\";\nimport type { ModelRegistry } from \"./core/model-registry.js\";\nimport { findInitialModel, resolveCliModel, resolveModelScope, type ScopedModel } from \"./core/model-resolver.js\";\nimport { restoreStdout, takeOverStdout } from \"./core/output-guard.js\";\nimport type { CreateAgentSessionOptions } from \"./core/sdk.js\";\nimport {\n\tformatMissingSessionCwdPrompt,\n\tgetMissingSessionCwdIssue,\n\tMissingSessionCwdError,\n\ttype SessionCwdIssue,\n} from \"./core/session-cwd.js\";\nimport { canonicalSessionPath, SessionAlreadyActiveError } from \"./core/session-lease.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { isTelemetryEnabled } from \"./core/telemetry.js\";\nimport { printTimings, resetTimings, time } from \"./core/timings.js\";\nimport { runMigrations, showDeprecationWarnings } from \"./migrations.js\";\nimport { isDaemonCatalogProcess, runDaemonCatalogProcess } from \"./modes/daemon/daemon-catalog-process.js\";\nimport { deserializeDaemonError } from \"./modes/daemon/daemon-errors.js\";\nimport { collectDaemonClientEnv, collectDaemonLaunchEnv } from \"./modes/daemon/daemon-protocol.js\";\nimport {\n\tDAEMON_WORKER_ACTIVE_SESSION_ID_ENV,\n\tisDaemonWorkerProcess,\n\trequireDaemonWorkerAuthenticationToken,\n\twaitForDaemonWorkerStartupGate,\n} from \"./modes/daemon/daemon-worker-protocol.js\";\nimport {\n\ttype AgentConnection,\n\ttype AgentsViewScopeKey,\n\tClientPromptStashStore,\n\tcreateInteractiveModeLocalSessionHost,\n\tcreateInteractiveModeUiServicesFromServices,\n\tDaemonAgentConnection,\n\tDaemonCapabilityUnavailableError,\n\tDaemonClient,\n\tdefaultDaemonSocketPath,\n\tInProcessAgentConnection,\n\tInteractiveMode,\n\tnormalizeSocketPath,\n\tresolveAttachModelFallbackMessage,\n\trunAcpMode,\n\trunAcpModeWithConnection,\n\trunAgentsViewMode,\n\trunDaemonMode,\n\trunDaemonSupervisorMode,\n\trunPrintMode,\n\trunPrintModeWithConnection,\n\trunRpcMode,\n\trunRpcModeWithConnection,\n\ttype SessionSummary,\n} from \"./modes/index.js\";\nimport { ExtensionSelectorComponent } from \"./modes/interactive/components/extension-selector.js\";\nimport { shouldRunOnboarding } from \"./modes/interactive/onboarding.js\";\nimport { initTheme, preloadCodeHighlighter, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { handleConfigCommand } from \"./package-manager-cli.js\";\nimport { isLocalPath } from \"./utils/paths.js\";\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\t// If stdin is a TTY, we're running interactively - don't read stdin\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\nfunction collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nfunction reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst color = diagnostic.type === \"error\" ? chalk.red : diagnostic.type === \"warning\" ? chalk.yellow : chalk.dim;\n\t\tconst prefix = diagnostic.type === \"error\" ? \"Error: \" : diagnostic.type === \"warning\" ? \"Warning: \" : \"\";\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\nexport type ClientMode = AgentExecutionMode;\n/** Compatibility view of the CLI's internal daemon process entrypoint. */\nexport type AppMode = ClientMode | \"daemon\";\n\nexport function shouldRejectNonInteractiveAttach(attachAgent: string | undefined, appMode: AppMode): boolean {\n\treturn attachAgent !== undefined && appMode !== \"interactive\";\n}\n\nexport function shouldRejectNonInteractiveBareResume(resume: true | string | undefined, appMode: AppMode): boolean {\n\treturn resume === true && appMode !== \"interactive\";\n}\n\nfunction resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {\n\tif (parsed.mode === \"daemon\") {\n\t\treturn \"daemon\";\n\t}\n\tif (parsed.mode === \"rpc\") {\n\t\treturn \"rpc\";\n\t}\n\tif (parsed.mode === \"acp\") {\n\t\treturn \"acp\";\n\t}\n\tif (parsed.mode === \"json\") {\n\t\treturn \"json\";\n\t}\n\tif (parsed.print || !stdinIsTTY) {\n\t\treturn \"print\";\n\t}\n\treturn \"interactive\";\n}\n\nfunction toPrintOutputMode(appMode: AppMode): Exclude<Mode, \"rpc\" | \"acp\" | \"daemon\"> {\n\treturn appMode === \"json\" ? \"json\" : \"text\";\n}\n\nexport function isClientOwnedDaemonSession(appMode: AppMode, noSession?: boolean): boolean {\n\treturn appMode !== \"acp\" || noSession === true;\n}\n\n// `asm-agent agents` opens the agents view directly.\nexport function parseAgentsViewCommand(args: string[]): { explicitAgentsView: boolean; args: string[] } {\n\tif (args[0] === \"agents\") {\n\t\treturn { explicitAgentsView: true, args: args.slice(1) };\n\t}\n\treturn { explicitAgentsView: false, args };\n}\n\nexport interface DaemonClientStartupDecision {\n\tappMode: AppMode;\n\tstartupBenchmark: boolean;\n\tnoSession?: boolean;\n\thelp?: boolean;\n\tlistModels?: string | true;\n}\n\nexport type InteractiveDaemonStartupDecision = DaemonClientStartupDecision;\n\n/** Retained for callers that only classify persistent interactive startup. */\nexport function shouldUseDaemonInteractive(options: DaemonClientStartupDecision): boolean {\n\treturn (\n\t\toptions.appMode === \"interactive\" &&\n\t\t!options.startupBenchmark &&\n\t\t!options.noSession &&\n\t\toptions.listModels === undefined\n\t);\n}\n\nexport function shouldUseDaemonClient(options: DaemonClientStartupDecision): boolean {\n\treturn (\n\t\toptions.appMode !== \"daemon\" && !options.startupBenchmark && !options.help && options.listModels === undefined\n\t);\n}\n\nexport function shouldUseDaemonClientRuntime(\n\toptions: DaemonClientStartupDecision & {\n\t\townedSessionWorker?: boolean;\n\t\thasProcessLocalExtensionFactories?: boolean;\n\t},\n): boolean {\n\treturn shouldUseDaemonClient(options) && !options.ownedSessionWorker && !options.hasProcessLocalExtensionFactories;\n}\n\nexport function shouldEnsureInteractiveDaemonForStartup(\n\tuseDaemonInteractive: boolean,\n\tattachAgent: string | undefined,\n): boolean {\n\treturn useDaemonInteractive && attachAgent === undefined;\n}\n\nexport interface AgentsViewStartupDecision {\n\tuseDaemonInteractive: boolean;\n\tneedsOnboarding: boolean;\n\texplicitAgentsView?: boolean;\n\tresume?: true | string;\n\tcontinue?: boolean;\n\tfork?: string;\n}\n\nexport function shouldOpenAgentsViewForDaemonInteractive(options: AgentsViewStartupDecision): boolean {\n\tconst bareResume = options.resume === true;\n\tconst requestsAgentsView = bareResume || (options.explicitAgentsView && !options.needsOnboarding);\n\treturn (\n\t\toptions.useDaemonInteractive &&\n\t\t// A selector, continuation, or fork must open its target directly rather than the agents view.\n\t\t!!requestsAgentsView &&\n\t\ttypeof options.resume !== \"string\" &&\n\t\t!options.continue &&\n\t\t!options.fork\n\t);\n}\n\nexport interface DaemonInteractiveSessionManagerDecision {\n\tresume?: true | string;\n\tcontinue?: boolean;\n\tfork?: string;\n\thasActiveDaemonSession?: boolean;\n}\n\nexport function shouldUseEphemeralSessionManagerForDaemonInteractive(\n\toptions: DaemonInteractiveSessionManagerDecision,\n): boolean {\n\treturn (\n\t\t!options.hasActiveDaemonSession &&\n\t\t(options.resume === undefined || options.resume === true) &&\n\t\t!options.continue &&\n\t\t!options.fork\n\t);\n}\n\nexport interface DaemonActiveSessionLookupDecision {\n\tuseDaemonInteractive: boolean;\n\tresumeSelector?: string;\n\texplicitAttach?: boolean;\n}\n\nexport function shouldEnsureDaemonBeforeActiveSessionLookup(options: DaemonActiveSessionLookupDecision): boolean {\n\treturn (\n\t\toptions.useDaemonInteractive &&\n\t\toptions.resumeSelector !== undefined &&\n\t\t(options.explicitAttach || !looksLikeSessionPath(options.resumeSelector))\n\t);\n}\n\ntype ActiveDaemonSessionSummaryLookup = (socketPath: string, selector: string) => Promise<SessionSummary | undefined>;\n\ninterface ActiveDaemonSessionSummaryLookupOptions {\n\tfallbackOnError?: boolean;\n\tlookup?: ActiveDaemonSessionSummaryLookup;\n}\n\nexport async function findActiveDaemonSessionSummaryForInteractiveStartup(\n\tsocketPath: string,\n\tselector: string,\n\toptions: ActiveDaemonSessionSummaryLookupOptions = {},\n): Promise<SessionSummary | undefined> {\n\ttry {\n\t\treturn await (options.lookup ?? findActiveDaemonSessionSummary)(socketPath, selector);\n\t} catch (error) {\n\t\tif (options.fallbackOnError === false) {\n\t\t\tthrow error;\n\t\t}\n\t\treturn undefined;\n\t}\n}\n\nasync function prepareInitialMessage(\n\tparsed: Args,\n\tautoResizeImages: boolean,\n\tstdinContent?: string,\n): Promise<{\n\tinitialMessage?: string;\n\tinitialImages?: ImageContent[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn buildInitialMessage({ parsed, stdinContent });\n\t}\n\n\tconst { text, images } = await processFileArguments(parsed.fileArgs, { autoResizeImages });\n\treturn buildInitialMessage({\n\t\tparsed,\n\t\tfileText: text,\n\t\tfileImages: images,\n\t\tstdinContent,\n\t});\n}\n\n/** Prompt user for yes/no confirmation */\nasync function promptConfirm(message: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\trl.question(`${message} [y/N] `, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\");\n\t\t});\n\t});\n}\n\n// Only busy sessions (streaming, compacting, or pending messages) lose work;\n// idle loaded sessions reload from disk on the fresh daemon.\nconst STARTUP_SESSION_LOSS_COPY: DaemonSessionLossCopy = {\n\tbusyDetail(count) {\n\t\tconst { noun, pronoun } = pluralizeSessions(count);\n\t\treturn `A background service from a different ASM Agent version is running with ${count} busy ${noun}. Stopping it will terminate ${pronoun}.`;\n\t},\n\tunlistableDetail:\n\t\t\"A background service from a different ASM Agent version is running and its sessions could not be listed. Stopping it may terminate active sessions.\",\n\tquestion: \"Stop it and continue?\",\n\tnonTtyHint: 'Run \"asm-agent shutdown\" to stop it, then retry.',\n};\n\n// The promise to keep after awaiting readiness. Wrapped in an object so it\n// survives `await` (which would otherwise flatten a returned Promise to void).\ntype DaemonReadyResult = { ready: Promise<void> | undefined };\n\n// A stale-version daemon couldn't be taken over automatically (busy or stuck).\n// Offer to stop it (default No) and start a fresh daemon, or exit. Returns the\n// fresh ready promise so callers stop re-handling the original rejection.\nasync function takeOverStaleDaemonOrExit(socketPath: string): Promise<DaemonReadyResult> {\n\tconst probe = await probeRunningDaemonSessions(socketPath);\n\tconst confirmed = await confirmDaemonSessionLoss(probe, { force: false, copy: STARTUP_SESSION_LOSS_COPY });\n\tif (!confirmed) {\n\t\t// Non-TTY already printed the reason; at a TTY the user declined.\n\t\tif (process.stdin.isTTY) {\n\t\t\tconsole.error(chalk.dim(\"Cancelled.\"));\n\t\t}\n\t\tprocess.exit(1);\n\t}\n\tif (!(await shutdownDaemonAndWait(socketPath))) {\n\t\tconsole.error(\n\t\t\tchalk.red(`Could not stop the background service on ${socketPath}. Run \"asm-agent shutdown\" and retry.`),\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\tconst ready = ensureInteractiveDaemonRunning(socketPath);\n\ttry {\n\t\tawait ready;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Could not start the background service: ${message}`));\n\t\tprocess.exit(1);\n\t}\n\treturn { ready };\n}\n\n// Resolves the daemon-ready promise, returning the promise to keep (the same\n// one on success, or the fresh one from a stale-daemon takeover) so repeat\n// calls don't re-handle the original rejection.\nasync function awaitDaemonReady(daemonReady: Promise<void> | undefined): Promise<DaemonReadyResult> {\n\tif (!daemonReady) {\n\t\treturn { ready: daemonReady };\n\t}\n\ttry {\n\t\tawait daemonReady;\n\t\treturn { ready: daemonReady };\n\t} catch (error) {\n\t\tif (error instanceof StaleDaemonError) {\n\t\t\treturn takeOverStaleDaemonOrExit(error.socketPath);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nfunction validateForkFlags(parsed: Args): void {\n\tif (!parsed.fork) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --fork cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction forkSessionOrExit(sourcePath: string, cwd: string, sessionDir?: string): SessionManager {\n\ttry {\n\t\treturn SessionManager.forkFrom(sourcePath, cwd, sessionDir);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction getResumeSelector(parsed: Pick<Args, \"resume\">): string | undefined {\n\treturn typeof parsed.resume === \"string\" ? parsed.resume : undefined;\n}\n\nexport async function createSessionManager(\n\tparsed: Args,\n\tcwd: string,\n\tsessionDir: string | undefined,\n): Promise<SessionManager> {\n\tconst explicitCwdOverride = parsed.cwd ? cwd : undefined;\n\n\tif (parsed.noSession) {\n\t\treturn SessionManager.inMemory();\n\t}\n\n\tif (parsed.fork) {\n\t\tconst resolved = await resolveSessionPath(parsed.fork, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\tcase \"global\":\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t}\n\t}\n\n\tconst resumeSelector = getResumeSelector(parsed);\n\tif (resumeSelector) {\n\t\tconst resolved = await resolveSessionPath(resumeSelector, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\t\treturn SessionManager.open(resolved.path, sessionDir, explicitCwdOverride);\n\n\t\t\tcase \"global\": {\n\t\t\t\tconsole.log(chalk.yellow(`Session found in different project: ${resolved.cwd}`));\n\t\t\t\tconst shouldFork = await promptConfirm(\"Fork this session into current directory?\");\n\t\t\t\tif (!shouldFork) {\n\t\t\t\t\tconsole.log(chalk.dim(\"Aborted.\"));\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (parsed.continue) {\n\t\treturn SessionManager.continueRecent(cwd, sessionDir);\n\t}\n\n\treturn SessionManager.create(cwd, sessionDir);\n}\n\nfunction buildSessionOptions(\n\tconfig: AgentSessionRuntimeConfig,\n\tscopedModels: ScopedModel[],\n\thasExistingSession: boolean,\n\tmodelRegistry: ModelRegistry,\n\tsettingsManager: SettingsManager,\n): {\n\toptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n} {\n\tconst options: CreateAgentSessionOptions = {};\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tlet cliThinkingFromModel = false;\n\n\t// Model from CLI\n\t// - supports --provider <name> --model <pattern>\n\t// - supports --model <provider>/<pattern>\n\tif (config.model) {\n\t\tconst resolved = resolveCliModel({\n\t\t\tcliProvider: config.provider,\n\t\t\tcliModel: config.model,\n\t\t\tmodelRegistry,\n\t\t});\n\t\tif (resolved.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: resolved.warning });\n\t\t}\n\t\tif (resolved.error) {\n\t\t\tdiagnostics.push({ type: \"error\", message: resolved.error });\n\t\t}\n\t\tif (resolved.model) {\n\t\t\toptions.model = resolved.model;\n\t\t\t// Allow \"--model <pattern>:<thinking>\" as a shorthand.\n\t\t\t// Explicit --thinking still takes precedence (applied later).\n\t\t\tif (!config.thinking && resolved.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = resolved.thinkingLevel;\n\t\t\t\tcliThinkingFromModel = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!options.model && scopedModels.length > 0 && !hasExistingSession) {\n\t\t// Check if saved default is in scoped models - use it if so, otherwise first scoped model\n\t\tconst savedProvider = settingsManager.getDefaultProvider();\n\t\tconst savedModelId = settingsManager.getDefaultModel();\n\t\tconst savedModel = savedProvider && savedModelId ? modelRegistry.find(savedProvider, savedModelId) : undefined;\n\t\tconst savedInScope = savedModel ? scopedModels.find((sm) => modelsAreEqual(sm.model, savedModel)) : undefined;\n\n\t\tif (savedInScope) {\n\t\t\toptions.model = savedInScope.model;\n\t\t\t// Use thinking level from scoped model config if explicitly set\n\t\t\tif (!config.thinking && savedInScope.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = savedInScope.thinkingLevel;\n\t\t\t}\n\t\t} else {\n\t\t\toptions.model = scopedModels[0].model;\n\t\t\t// Use thinking level from first scoped model if explicitly set\n\t\t\tif (!config.thinking && scopedModels[0].thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Thinking level from CLI (takes precedence over scoped model thinking levels set above)\n\tif (config.thinking) {\n\t\toptions.thinkingLevel = config.thinking;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\t// Keep thinking level undefined when not explicitly set in the model pattern.\n\t// Undefined means \"inherit current session thinking level\" during cycling.\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels.map((sm) => ({\n\t\t\tmodel: sm.model,\n\t\t\tthinkingLevel: sm.thinkingLevel,\n\t\t}));\n\t}\n\n\t// API key from CLI - set in authStorage\n\t// (handled by caller before createAgentSession)\n\n\t// Tools\n\tif (config.noTools) {\n\t\toptions.noTools = \"all\";\n\t} else if (config.noBuiltinTools) {\n\t\toptions.noTools = \"builtin\";\n\t}\n\tif (config.tools) {\n\t\toptions.tools = [...config.tools];\n\t}\n\tif (config.autonomous) {\n\t\toptions.autonomous = mergeAutonomousConfig(undefined, config.autonomous);\n\t}\n\n\treturn { options, cliThinkingFromModel, diagnostics };\n}\n\nfunction resolveCliPaths(cwd: string, paths: string[] | undefined): string[] | undefined {\n\treturn paths?.map((value) => (isLocalPath(value) ? resolve(cwd, value) : value));\n}\n\nfunction runtimeAutonomousConfigFromArgs(parsed: Args): AgentSessionRuntimeConfig[\"autonomous\"] {\n\tconst hasAutonomousOptions =\n\t\tparsed.autonomous === true ||\n\t\tparsed.autonomousGates !== undefined ||\n\t\tparsed.autonomousGateRetries !== undefined ||\n\t\tparsed.autonomousGateTimeoutMs !== undefined ||\n\t\tparsed.autonomousMaxContinuations !== undefined ||\n\t\tparsed.autonomousMaxTurns !== undefined ||\n\t\tparsed.autonomousMaxTokens !== undefined ||\n\t\tparsed.autonomousTimeoutMs !== undefined;\n\tif (!hasAutonomousOptions) {\n\t\treturn undefined;\n\t}\n\tconst hasGateOptions =\n\t\tparsed.autonomousGates !== undefined ||\n\t\tparsed.autonomousGateRetries !== undefined ||\n\t\tparsed.autonomousGateTimeoutMs !== undefined;\n\treturn {\n\t\tenabled: true,\n\t\tmaxContinuations: parsed.autonomousMaxContinuations,\n\t\tmaxTurns: parsed.autonomousMaxTurns,\n\t\tmaxTokens: parsed.autonomousMaxTokens,\n\t\ttimeoutMs: parsed.autonomousTimeoutMs,\n\t\tgates: hasGateOptions\n\t\t\t? {\n\t\t\t\t\tcommands: parsed.autonomousGates,\n\t\t\t\t\tmaxRetries: parsed.autonomousGateRetries,\n\t\t\t\t\ttimeoutMs: parsed.autonomousGateTimeoutMs,\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n\nfunction runtimeConfigFromArgs(\n\tparsed: Args,\n\tcwd: string,\n\tagentDir: string,\n\tsessionDir: string | undefined,\n\tappMode: AppMode,\n\ttelemetryDisabled?: true,\n): AgentSessionRuntimeConfig {\n\treturn {\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionDir,\n\t\tprovider: parsed.provider,\n\t\tmodel: parsed.model,\n\t\tapiKey: parsed.apiKey,\n\t\tsystemPrompt: parsed.systemPrompt,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tthinking: parsed.thinking,\n\t\tmodels: parsed.models,\n\t\ttools: parsed.tools,\n\t\tnoTools: parsed.noTools,\n\t\tnoBuiltinTools: parsed.noBuiltinTools,\n\t\textensions: resolveCliPaths(cwd, parsed.extensions),\n\t\tnoExtensions: parsed.noExtensions,\n\t\tskills: resolveCliPaths(cwd, parsed.skills),\n\t\tnoSkills: parsed.noSkills,\n\t\tpromptTemplates: resolveCliPaths(cwd, parsed.promptTemplates),\n\t\tnoPromptTemplates: parsed.noPromptTemplates,\n\t\tthemes: resolveCliPaths(cwd, parsed.themes),\n\t\tnoThemes: parsed.noThemes,\n\t\tnoContextFiles: parsed.noContextFiles,\n\t\tautonomous: runtimeAutonomousConfigFromArgs(parsed),\n\t\textensionFlagValues: parsed.unknownFlags.size > 0 ? Object.fromEntries(parsed.unknownFlags.entries()) : undefined,\n\t\texecutionMode: appMode === \"daemon\" ? undefined : appMode,\n\t\ttelemetryDisabled,\n\t\t// Serialized refine for print/json/rpc: the client's appMode is NOT\n\t\t// \"daemon\" here — it's \"print\", \"json\", or \"rpc\". The daemon worker\n\t\t// receives this flag via AgentSessionRuntimeConfig and uses it\n\t\t// instead of its own appMode=\"daemon\".\n\t\tserializedRefine: appMode !== \"interactive\" && appMode !== \"daemon\",\n\t\tinitialGoal: parsed.goal ? { objective: parsed.goal, tokenBudget: parsed.goalTokenBudget } : undefined,\n\t};\n}\n\ninterface PreparedRuntimeServices {\n\tservices: AgentSessionServices;\n\tscopedModels: ScopedModel[];\n\tsessionOptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\nexport function daemonServerDefaultSessionConfig(config: AgentSessionRuntimeConfig): AgentSessionRuntimeConfig {\n\treturn { ...config, initialGoal: undefined };\n}\n\nexport function resolveRuntimeSessionOptions(\n\tsessionOptions: CreateAgentSessionOptions,\n\truntimeSessionOptions?: CreateAgentSessionOptions,\n): CreateAgentSessionOptions {\n\treturn {\n\t\tmodel: runtimeSessionOptions?.model ?? sessionOptions.model,\n\t\tthinkingLevel: runtimeSessionOptions?.thinkingLevel ?? sessionOptions.thinkingLevel,\n\t\tserviceTier: runtimeSessionOptions?.serviceTier ?? sessionOptions.serviceTier,\n\t\tscopedModels: runtimeSessionOptions?.scopedModels ?? sessionOptions.scopedModels,\n\t\ttools: runtimeSessionOptions?.tools ?? sessionOptions.tools,\n\t\tnoTools: runtimeSessionOptions?.noTools ?? sessionOptions.noTools,\n\t\tcustomTools: runtimeSessionOptions?.customTools ?? sessionOptions.customTools,\n\t\tinitialActiveToolNames: runtimeSessionOptions?.initialActiveToolNames,\n\t\tallowedToolNames: runtimeSessionOptions?.allowedToolNames,\n\t\tincludeGoals: runtimeSessionOptions?.includeGoals,\n\t\tincludeCompactSkill: runtimeSessionOptions?.includeCompactSkill,\n\t\trlmHeartbeatController: runtimeSessionOptions?.rlmHeartbeatController,\n\t\tagentMessageController: runtimeSessionOptions?.agentMessageController,\n\t\tagentObserveController: runtimeSessionOptions?.agentObserveController,\n\t\tautonomous:\n\t\t\t(runtimeSessionOptions?.rlmDepth ?? 0) > 0\n\t\t\t\t? mergeAutonomousConfig(sessionOptions.autonomous, { ...runtimeSessionOptions?.autonomous, enabled: false })\n\t\t\t\t: mergeAutonomousConfig(sessionOptions.autonomous, runtimeSessionOptions?.autonomous),\n\t\trlmDepth: runtimeSessionOptions?.rlmDepth,\n\t\trlmMaxDepth: runtimeSessionOptions?.rlmMaxDepth,\n\t\trlmSessionDir: runtimeSessionOptions?.rlmSessionDir,\n\t\trlmParentNodeId: runtimeSessionOptions?.rlmParentNodeId,\n\t\trlmParentAgent: runtimeSessionOptions?.rlmParentAgent,\n\t\tsubagentRuntimeHost: runtimeSessionOptions?.subagentRuntimeHost,\n\t};\n}\n\nasync function prepareRuntimeServices(options: {\n\tconfig: AgentSessionRuntimeConfig;\n\tcwd: string;\n\tagentDir: string;\n\tsessionManager: SessionManager;\n\textensionFactories?: ExtensionFactory[];\n\tsessionOptionsOverride?: CreateAgentSessionOptions;\n}): Promise<PreparedRuntimeServices> {\n\tconst { config, sessionManager } = options;\n\tconst effectiveAgentDir = config.agentDir ?? options.agentDir;\n\tconst authStorage = AuthStorage.create(join(effectiveAgentDir, \"auth.json\"), {\n\t\tusePrimeCliConfig: false,\n\t});\n\tconst services = await createAgentSessionServices({\n\t\tcwd: options.cwd,\n\t\tagentDir: effectiveAgentDir,\n\t\tauthStorage,\n\t\textensionFlagValues: new Map(Object.entries(config.extensionFlagValues ?? {})),\n\t\t// Subagents share the parent's Herdr pane; their own reporter would race\n\t\t// the parent's and a subagent quit would release the still-active pane.\n\t\tnoBuiltinHerdrReporter: (options.sessionOptionsOverride?.rlmDepth ?? 0) > 0,\n\t\ttelemetryDisabled: config.telemetryDisabled,\n\t\tresourceLoaderOptions: {\n\t\t\tadditionalExtensionPaths: config.extensions,\n\t\t\tadditionalSkillPaths: config.skills,\n\t\t\tadditionalPromptTemplatePaths: config.promptTemplates,\n\t\t\tadditionalThemePaths: config.themes,\n\t\t\tnoExtensions: config.noExtensions,\n\t\t\tnoSkills: config.noSkills,\n\t\t\tnoPromptTemplates: config.noPromptTemplates,\n\t\t\tnoThemes: config.noThemes,\n\t\t\tnoContextFiles: config.noContextFiles,\n\t\t\tsystemPrompt: config.systemPrompt,\n\t\t\tappendSystemPrompt: config.appendSystemPrompt,\n\t\t\textensionFactories: options.extensionFactories,\n\t\t},\n\t});\n\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [\n\t\t...services.diagnostics,\n\t\t...collectSettingsDiagnostics(settingsManager, \"runtime creation\"),\n\t\t...resourceLoader.getExtensions().errors.map(({ path, error }) => ({\n\t\t\ttype: \"error\" as const,\n\t\t\tmessage: `Failed to load extension \"${path}\": ${error}`,\n\t\t})),\n\t];\n\n\tconst modelPatterns = config.models ?? settingsManager.getEnabledModels();\n\tconst scopedModels =\n\t\tmodelPatterns && modelPatterns.length > 0 ? await resolveModelScope(modelPatterns, modelRegistry) : [];\n\tconst {\n\t\toptions: sessionOptions,\n\t\tcliThinkingFromModel,\n\t\tdiagnostics: sessionOptionDiagnostics,\n\t} = buildSessionOptions(\n\t\tconfig,\n\t\tscopedModels,\n\t\tsessionManager.buildSessionContext().messages.length > 0,\n\t\tmodelRegistry,\n\t\tsettingsManager,\n\t);\n\tdiagnostics.push(...sessionOptionDiagnostics);\n\n\tconst effectiveSessionModel = options.sessionOptionsOverride?.model ?? sessionOptions.model;\n\tif (config.apiKey) {\n\t\tif (!effectiveSessionModel) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: \"--api-key requires a model to be specified via --model, --provider/--model, or --models\",\n\t\t\t});\n\t\t} else {\n\t\t\tauthStorage.setRuntimeApiKey(effectiveSessionModel.provider, config.apiKey);\n\t\t}\n\t}\n\n\treturn {\n\t\tservices,\n\t\tscopedModels,\n\t\tsessionOptions,\n\t\tcliThinkingFromModel,\n\t\tdiagnostics,\n\t};\n}\n\nasync function resolvePreparedStartupModel(options: {\n\tprepared: PreparedRuntimeServices;\n\tsessionManager: SessionManager;\n}): Promise<{ model: Model<Api> | undefined; modelFallbackMessage: string | undefined }> {\n\tconst { prepared, sessionManager } = options;\n\tconst { modelRegistry, settingsManager } = prepared.services;\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\n\tlet model = prepared.sessionOptions.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: prepared.scopedModels,\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\treturn { model, modelFallbackMessage };\n}\n\nasync function promptForMissingSessionCwd(\n\tissue: SessionCwdIssue,\n\tsettingsManager: SettingsManager,\n): Promise<string | undefined> {\n\tinitTheme(settingsManager.getTheme());\n\tsetKeybindings(KeybindingsManager.create());\n\n\treturn new Promise((resolve) => {\n\t\tconst ui = new TUI(new ProcessTerminal(), settingsManager.getShowHardwareCursor());\n\t\tui.setClearOnShrink(settingsManager.getClearOnShrink());\n\n\t\tlet settled = false;\n\t\tconst finish = (result: string | undefined) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tui.stop();\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\tformatMissingSessionCwdPrompt(issue),\n\t\t\t[\"Continue\", \"Cancel\"],\n\t\t\t(option) => finish(option === \"Continue\" ? issue.fallbackCwd : undefined),\n\t\t\t() => finish(undefined),\n\t\t\t{ tui: ui },\n\t\t);\n\t\tui.addChild(selector);\n\t\tui.setFocus(selector);\n\t\tui.start();\n\t});\n}\n\nfunction getDaemonSummaryActiveSessionId(summary: SessionSummary): string {\n\treturn summary.activeSessionId ?? summary.id;\n}\n\nfunction isUnknownActiveSessionError(message: string): boolean {\n\treturn message.startsWith(\"Unknown active session:\");\n}\n\nasync function findActiveDaemonSessionSummary(\n\tsocketPath: string,\n\tselector: string,\n): Promise<SessionSummary | undefined> {\n\tconst client = new DaemonClient(socketPath);\n\tawait client.connect(250);\n\n\ttry {\n\t\tconst response = await client.request({ type: \"get_state\", activeSessionId: selector }, 3000);\n\t\tif (!response.success) {\n\t\t\tif (isUnknownActiveSessionError(response.error)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthrow new Error(response.error);\n\t\t}\n\t\tif (!isDaemonSessionSummary(response.data)) {\n\t\t\tthrow new Error(\"Daemon returned an invalid active session summary\");\n\t\t}\n\t\treturn response.data;\n\t} finally {\n\t\tclient.close();\n\t}\n}\n\nfunction createSessionManagerForActiveDaemonSummary(summary: SessionSummary, fallbackCwd: string): SessionManager {\n\tconst cwd = summary.cwd || fallbackCwd;\n\tif (summary.sessionFile) {\n\t\ttry {\n\t\t\treturn SessionManager.open(summary.sessionFile, undefined, cwd);\n\t\t} catch {\n\t\t\treturn SessionManager.inMemory(cwd);\n\t\t}\n\t}\n\treturn SessionManager.inMemory(cwd);\n}\n\nfunction getInteractiveDaemonSessionPath(parsed: Args, sessionManager: SessionManager): string | undefined {\n\tif (!parsed.resume && !parsed.continue && !parsed.fork) {\n\t\treturn undefined;\n\t}\n\treturn sessionManager.getSessionFile();\n}\n\nexport function findActiveDaemonSessionSummaryForSessionFile(\n\tsummaries: readonly SessionSummary[],\n\tsessionPath: string,\n): SessionSummary | undefined {\n\tconst resolvedSessionPath = canonicalSessionPath(sessionPath);\n\treturn summaries.find(\n\t\t(summary) =>\n\t\t\tsummary.activeSessionId !== undefined &&\n\t\t\tsummary.sessionFile !== undefined &&\n\t\t\tcanonicalSessionPath(summary.sessionFile) === resolvedSessionPath,\n\t);\n}\n\nasync function createDaemonClientConnection(options: {\n\tsocketPath: string;\n\tconfig: AgentSessionRuntimeConfig;\n\tsessionPath?: string;\n\tcontinueRecent?: boolean;\n\tactiveSessionId?: string;\n\tclientOwned?: boolean;\n\tnoSession?: boolean;\n\tsupportsExtensionUi?: boolean;\n}): Promise<{ connection: DaemonAgentConnection; summary: SessionSummary }> {\n\t// Caller must have awaited ensureInteractiveDaemonRunning for this socket.\n\tconst client = new DaemonClient(options.socketPath);\n\tawait client.connect();\n\n\ttry {\n\t\tconst attach = async (summary: SessionSummary) => {\n\t\t\tconst connection = await DaemonAgentConnection.attach(client, getDaemonSummaryActiveSessionId(summary), {\n\t\t\t\tcloseClientOnDispose: true,\n\t\t\t\tsendClientEnv: true,\n\t\t\t\townedSession: options.clientOwned,\n\t\t\t\townedSessionRecoveryConfig: options.clientOwned ? options.config : undefined,\n\t\t\t\tsupportsExtensionUi: options.supportsExtensionUi,\n\t\t\t\trecoverDaemon: () => ensureInteractiveDaemonRunning(options.socketPath),\n\t\t\t\ttelemetryDisabled: options.config.telemetryDisabled,\n\t\t\t});\n\t\t\treturn { connection, summary };\n\t\t};\n\n\t\tif (options.activeSessionId) {\n\t\t\tconst summary = await findAttachedDaemonSessionSummary(client, options.activeSessionId);\n\t\t\treturn await attach(summary);\n\t\t}\n\n\t\tif (options.sessionPath && !options.clientOwned) {\n\t\t\tconst activeSummary = findActiveDaemonSessionSummaryForSessionFile(\n\t\t\t\tawait listActiveDaemonSessionSummaries(client),\n\t\t\t\toptions.sessionPath,\n\t\t\t);\n\t\t\tif (activeSummary && activeSummary.workerState !== \"failed\") {\n\t\t\t\treturn await attach(activeSummary);\n\t\t\t}\n\t\t}\n\t\tif (options.clientOwned) {\n\t\t\tawait client.waitForHello();\n\t\t\tif (!client.supportsServerCapability(\"client_owned_sessions\")) {\n\t\t\t\tthrow new DaemonCapabilityUnavailableError(\"create\", \"client_owned_sessions\");\n\t\t\t}\n\t\t}\n\n\t\tconst response = await client.request({\n\t\t\ttype: \"create\",\n\t\t\tconfig: options.config,\n\t\t\tsessionPath: options.sessionPath,\n\t\t\tcontinueRecent: options.continueRecent,\n\t\t\tnoSession: options.noSession,\n\t\t\tenv: collectDaemonClientEnv(),\n\t\t\tlifecycle: options.clientOwned ? \"client_owned\" : \"resident\",\n\t\t\tlaunchEnv: collectDaemonLaunchEnv(),\n\t\t});\n\t\tif (!response.success) {\n\t\t\tthrow deserializeDaemonError(response);\n\t\t}\n\t\tif (!isDaemonSessionSummary(response.data)) {\n\t\t\tthrow new Error(\"Daemon returned an invalid create response\");\n\t\t}\n\t\tconst summary = response.data;\n\t\treturn await attach(summary);\n\t} catch (error) {\n\t\tclient.close();\n\t\tthrow error;\n\t}\n}\n\nasync function findAttachedDaemonSessionSummary(\n\tclient: DaemonClient,\n\tactiveSessionId: string,\n): Promise<SessionSummary> {\n\tconst response = await client.request({ type: \"get_state\", activeSessionId });\n\tif (!response.success) {\n\t\tthrow new Error(response.error);\n\t}\n\tif (!isDaemonSessionSummary(response.data)) {\n\t\tthrow new Error(\"Daemon returned an invalid active session summary\");\n\t}\n\treturn response.data;\n}\n\nexport interface MainOptions {\n\textensionFactories?: ExtensionFactory[];\n}\n\nexport async function main(args: string[], options?: MainOptions) {\n\tresetTimings();\n\tif (isDaemonWorkerProcess()) {\n\t\twaitForDaemonWorkerStartupGate();\n\t}\n\tinstallFileLogSink();\n\tif (isDaemonCatalogProcess()) {\n\t\tawait runDaemonCatalogProcess();\n\t\treturn;\n\t}\n\t// Client and daemon are separate processes; both need these in their registry.\n\tregisterBuiltinMcpOAuthProviders();\n\tconst offlineMode = args.includes(\"--offline\") || isTruthyEnvFlag(process.env.PI_OFFLINE);\n\tif (offlineMode) {\n\t\tprocess.env.PI_OFFLINE = \"1\";\n\t\tprocess.env.PI_SKIP_VERSION_CHECK = \"1\";\n\t}\n\n\tconst publicCommand = await handlePublicCommand(args);\n\tif (publicCommand.handled) {\n\t\treturn;\n\t}\n\targs = publicCommand.args;\n\n\tif (await handleConfigCommand(args)) {\n\t\treturn;\n\t}\n\n\tconst explicitAgentsView = publicCommand.explicitAgentsView;\n\n\tconst parsed = parseArgs(args);\n\tif (parsed.diagnostics.length > 0) {\n\t\tfor (const d of parsed.diagnostics) {\n\t\t\tconst color = d.type === \"error\" ? chalk.red : chalk.yellow;\n\t\t\tconsole.error(color(`${d.type === \"error\" ? \"Error\" : \"Warning\"}: ${d.message}`));\n\t\t}\n\t\tif (parsed.diagnostics.some((d) => d.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"parseArgs\");\n\tconst appMode = resolveAppMode(parsed, process.stdin.isTTY);\n\n\tif (shouldRejectNonInteractiveAttach(publicCommand.attachAgent, appMode)) {\n\t\tconsole.error(chalk.red(\"Error: attach requires an interactive terminal\"));\n\t\tprocess.exit(1);\n\t}\n\tif (shouldRejectNonInteractiveBareResume(parsed.resume, appMode)) {\n\t\tconsole.error(chalk.red(\"Error: --resume without a session selector requires an interactive terminal\"));\n\t\tprocess.exit(1);\n\t}\n\tsetLogContext({ mode: appMode });\n\tconst shouldTakeOverStdout = appMode !== \"interactive\";\n\tif (shouldTakeOverStdout) {\n\t\ttakeOverStdout();\n\t}\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\tprocess.exit(0);\n\t}\n\tif (parsed.help) {\n\t\tconsole.log(formatTopLevelHelp());\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.export) {\n\t\tlet result: string;\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tresult = await exportFromFile(parsed.export, outputPath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconsole.log(`Exported to: ${result}`);\n\t\tprocess.exit(0);\n\t}\n\n\tif ((parsed.mode === \"rpc\" || parsed.mode === \"daemon\") && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC or daemon mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tvalidateForkFlags(parsed);\n\n\tconst cwd = parsed.cwd ? resolve(expandTildePath(parsed.cwd)) : process.cwd();\n\tif (parsed.cwd) {\n\t\ttry {\n\t\t\tprocess.chdir(cwd);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(chalk.red(`Error: Cannot use cwd ${cwd}: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (parsed.daemonSocket) {\n\t\t// After --cwd so a relative socket path resolves against the requested directory.\n\t\tparsed.daemonSocket = normalizeSocketPath(parsed.daemonSocket);\n\t}\n\n\t// Run migrations (pass cwd for project-local migrations)\n\tconst { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(cwd);\n\ttime(\"runMigrations\");\n\n\tconst agentDir = getAgentDir();\n\tconst startupSettingsManager = SettingsManager.create(cwd, agentDir);\n\treportDiagnostics(collectSettingsDiagnostics(startupSettingsManager, \"startup session lookup\"));\n\tconst startupBenchmark = isTruthyEnvFlag(process.env.PI_STARTUP_BENCHMARK);\n\tif (startupBenchmark && appMode !== \"interactive\") {\n\t\tconsole.error(chalk.red(\"Error: PI_STARTUP_BENCHMARK only supports interactive mode\"));\n\t\tprocess.exit(1);\n\t}\n\t// Programmatic factories are process-local functions and cannot be serialized to a daemon worker.\n\tconst hasProcessLocalExtensionFactories = (options?.extensionFactories?.length ?? 0) > 0;\n\tconst useDaemonClient = shouldUseDaemonClientRuntime({\n\t\tappMode,\n\t\tstartupBenchmark,\n\t\tnoSession: parsed.noSession,\n\t\tlistModels: parsed.listModels,\n\t\townedSessionWorker: isOwnedSessionWorkerProcess(),\n\t\thasProcessLocalExtensionFactories,\n\t});\n\tconst useDaemonInteractive = useDaemonClient && appMode === \"interactive\";\n\n\t// Decide the final runtime cwd before creating cwd-bound runtime services.\n\t// --resume may select a session from another project, so project-local\n\t// settings, resources, provider registrations, and models must be resolved only after\n\t// the target session cwd is known. The startup-cwd settings manager is used only for\n\t// sessionDir lookup during session selection.\n\tconst sessionDir =\n\t\t(parsed.sessionDir ? expandTildePath(parsed.sessionDir) : undefined) ??\n\t\tgetSessionDirEnvOverride() ??\n\t\tstartupSettingsManager.getSessionDir();\n\tconst daemonSocketPath = parsed.daemonSocket ?? defaultDaemonSocketPath();\n\t// Kick off daemon spawn/readiness immediately so it overlaps session-manager\n\t// and runtime-services preparation; attach only connects to an existing daemon.\n\tlet daemonReady = shouldEnsureInteractiveDaemonForStartup(useDaemonClient, publicCommand.attachAgent)\n\t\t? ensureInteractiveDaemonRunning(daemonSocketPath)\n\t\t: undefined;\n\t// Errors are rethrown at the await sites below; this only avoids an unhandled\n\t// rejection if startup exits before reaching them.\n\tdaemonReady?.catch(() => {});\n\tconst resumeSelector = getResumeSelector(parsed);\n\tconst shouldLookupDaemonActiveSession = shouldEnsureDaemonBeforeActiveSessionLookup({\n\t\tuseDaemonInteractive,\n\t\tresumeSelector,\n\t\texplicitAttach: publicCommand.attachAgent !== undefined,\n\t});\n\tif (shouldLookupDaemonActiveSession && daemonReady) {\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t}\n\tlet activeDaemonSessionSummary: SessionSummary | undefined;\n\tif (shouldLookupDaemonActiveSession && resumeSelector) {\n\t\ttry {\n\t\t\tactiveDaemonSessionSummary = await findActiveDaemonSessionSummaryForInteractiveStartup(\n\t\t\t\tdaemonSocketPath,\n\t\t\t\tresumeSelector,\n\t\t\t\t{ fallbackOnError: !publicCommand.attachAgent },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(chalk.red(`Error: Could not look up active agent '${resumeSelector}': ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (publicCommand.attachAgent && !activeDaemonSessionSummary) {\n\t\tconsole.error(chalk.red(`Error: No active agent found matching '${publicCommand.attachAgent}'`));\n\t\tprocess.exit(1);\n\t}\n\tlet sessionManager: SessionManager;\n\tif (activeDaemonSessionSummary) {\n\t\tsessionManager = createSessionManagerForActiveDaemonSummary(activeDaemonSessionSummary, cwd);\n\t} else if (\n\t\tuseDaemonInteractive &&\n\t\tshouldUseEphemeralSessionManagerForDaemonInteractive({\n\t\t\tresume: parsed.resume,\n\t\t\tcontinue: parsed.continue,\n\t\t\tfork: parsed.fork,\n\t\t})\n\t) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else {\n\t\ttry {\n\t\t\tsessionManager = await createSessionManager(parsed, cwd, sessionDir);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof SessionSelectorError)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst suggestion =\n\t\t\t\terror instanceof SessionSelectorNotFoundError && error.suggestion\n\t\t\t\t\t? ` Did you mean '${error.suggestion}'?`\n\t\t\t\t\t: \"\";\n\t\t\tconsole.error(chalk.red(`Error: ${error.message}.${suggestion}`));\n\t\t\tconsole.error(chalk.dim(`Open ${APP_NAME} and press left-arrow to browse sessions.`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tconst missingSessionCwdIssue = getMissingSessionCwdIssue(sessionManager, cwd);\n\tif (missingSessionCwdIssue) {\n\t\tif (appMode === \"interactive\") {\n\t\t\tconst selectedCwd = await promptForMissingSessionCwd(missingSessionCwdIssue, startupSettingsManager);\n\t\t\tif (!selectedCwd) {\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\tsessionManager = SessionManager.open(missingSessionCwdIssue.sessionFile!, sessionDir, selectedCwd);\n\t\t} else {\n\t\t\tconsole.error(chalk.red(new MissingSessionCwdError(missingSessionCwdIssue).message));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"createSessionManager\");\n\n\tconst telemetrySettingsManager =\n\t\tsessionManager.getCwd() === cwd\n\t\t\t? startupSettingsManager\n\t\t\t: SettingsManager.create(sessionManager.getCwd(), agentDir);\n\tconst telemetryDisabled = isTelemetryEnabled(telemetrySettingsManager) ? undefined : true;\n\tconst defaultSessionConfig = runtimeConfigFromArgs(\n\t\tparsed,\n\t\tsessionManager.getCwd(),\n\t\tagentDir,\n\t\tsessionDir,\n\t\tappMode,\n\t\ttelemetryDisabled,\n\t);\n\t// Verifier/headless clients pass initialGoal in each create request. The long-lived\n\t// daemon fallback must not seed that goal into unrelated future sessions.\n\tconst daemonDefaultSessionConfig = daemonServerDefaultSessionConfig(defaultSessionConfig);\n\tconst runtimeDefaultSessionConfig = appMode === \"daemon\" ? daemonDefaultSessionConfig : defaultSessionConfig;\n\tconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t\tsessionConfig,\n\t\tsessionOptions: runtimeSessionOptions,\n\t}) => {\n\t\tconst config = mergeAgentSessionRuntimeConfig(runtimeDefaultSessionConfig, sessionConfig);\n\t\tconst prepared = await prepareRuntimeServices({\n\t\t\tconfig,\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\textensionFactories: options?.extensionFactories,\n\t\t\tsessionOptionsOverride: runtimeSessionOptions,\n\t\t});\n\t\tconst { services, sessionOptions, diagnostics } = prepared;\n\t\tconst resolvedSessionOptions = resolveRuntimeSessionOptions(sessionOptions, runtimeSessionOptions);\n\n\t\tconst created = await createAgentSessionFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent,\n\t\t\t...resolvedSessionOptions,\n\t\t\t// Main agents boot their kernel in the background at session creation;\n\t\t\t// subagent sessions (rlmDepth > 0) keep the lazy first-call start.\n\t\t\tprewarmIpythonKernel: true,\n\t\t\t// Read serializedRefine from the merged runtime config (passed\n\t\t\t// from the JSON/print client through AgentSessionRuntimeConfig)\n\t\t\t// so it survives the daemon worker's appMode=\"daemon\" context.\n\t\t\tserializedRefine: config.serializedRefine ?? false,\n\t\t\texecutionMode: config.executionMode,\n\t\t\ttelemetryDisabled: config.telemetryDisabled,\n\t\t\t// Only seed initial goal for top-level sessions (rlmDepth 0).\n\t\t\tinitialGoal: (runtimeSessionOptions?.rlmDepth ?? 0) === 0 ? config.initialGoal : undefined,\n\t\t});\n\t\tconst cliThinkingOverride = config.thinking !== undefined || prepared.cliThinkingFromModel;\n\t\tif (created.session.model && cliThinkingOverride) {\n\t\t\tcreated.session.setThinkingLevel(created.session.thinkingLevel);\n\t\t}\n\n\t\treturn {\n\t\t\t...created,\n\t\t\tservices,\n\t\t\tdiagnostics,\n\t\t};\n\t};\n\ttime(\"createRuntime\");\n\t// Daemon mode never uses the bootstrap runtime, so skip the heavy\n\t// createAgentSessionRuntime below and start listening immediately; sessions\n\t// are created on demand through the daemon protocol via createRuntime.\n\t// --list-models still takes the full path to print and exit.\n\tif (appMode === \"daemon\" && parsed.listModels === undefined) {\n\t\tprintTimings();\n\t\tif (isDaemonWorkerProcess()) {\n\t\t\tawait runDaemonMode({\n\t\t\t\tsocketPath: parsed.daemonSocket,\n\t\t\t\tdefaultSessionConfig: daemonDefaultSessionConfig,\n\t\t\t\tcreateRuntime,\n\t\t\t\tworker: {\n\t\t\t\t\tauthenticationToken: requireDaemonWorkerAuthenticationToken(),\n\t\t\t\t\trestoreActiveSessionId: process.env[DAEMON_WORKER_ACTIVE_SESSION_ID_ENV],\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait runDaemonSupervisorMode({\n\t\t\t\tsocketPath: parsed.daemonSocket,\n\t\t\t\tdefaultSessionConfig: daemonDefaultSessionConfig,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\tif (useDaemonInteractive) {\n\t\tconst prepared = await prepareRuntimeServices({\n\t\t\tconfig: defaultSessionConfig,\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\textensionFactories: options?.extensionFactories,\n\t\t});\n\t\tconst { services, scopedModels } = prepared;\n\t\tconst { settingsManager } = services;\n\n\t\tconst startupModel = await resolvePreparedStartupModel({ prepared, sessionManager });\n\n\t\tconst stdinContent = await readPipedStdin();\n\t\ttime(\"readPipedStdin\");\n\n\t\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\t\tparsed,\n\t\t\tsettingsManager.getImageAutoResize(),\n\t\t\tstdinContent,\n\t\t);\n\t\ttime(\"prepareInitialMessage\");\n\t\tinitTheme(settingsManager.getTheme(), true);\n\t\ttime(\"initTheme\");\n\n\t\tif (deprecationWarnings.length > 0) {\n\t\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t\t}\n\n\t\treportDiagnostics(prepared.diagnostics);\n\t\tif (prepared.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ttime(\"prepareInteractiveServices\");\n\n\t\tif (scopedModels.length > 0 && (parsed.verbose || !settingsManager.getQuietStartup())) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst promptStashStore = new ClientPromptStashStore();\n\t\tconst daemonUiServices = createInteractiveModeUiServicesFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t});\n\t\tconst launchAgentsView = async (initialSession?: SessionSummary, initialScopeKey?: AgentsViewScopeKey) => {\n\t\t\tawait runAgentsViewMode({\n\t\t\t\tsocketPath: daemonSocketPath,\n\t\t\t\tconfig: defaultSessionConfig,\n\t\t\t\tuiServices: daemonUiServices,\n\t\t\t\trecoverDaemon: () => ensureInteractiveDaemonRunning(daemonSocketPath),\n\t\t\t\tcreateUiServicesForSession: async (summary) => {\n\t\t\t\t\tconst attachedSessionManager = createSessionManagerForActiveDaemonSummary(\n\t\t\t\t\t\tsummary,\n\t\t\t\t\t\tsessionManager.getCwd(),\n\t\t\t\t\t);\n\t\t\t\t\tconst attachedPrepared = await prepareRuntimeServices({\n\t\t\t\t\t\tconfig: mergeAgentSessionRuntimeConfig(defaultSessionConfig, {\n\t\t\t\t\t\t\tcwd: attachedSessionManager.getCwd(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tcwd: attachedSessionManager.getCwd(),\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t\tsessionManager: attachedSessionManager,\n\t\t\t\t\t\textensionFactories: options?.extensionFactories,\n\t\t\t\t\t});\n\t\t\t\t\treturn createInteractiveModeUiServicesFromServices({\n\t\t\t\t\t\tservices: attachedPrepared.services,\n\t\t\t\t\t\tsessionManager: attachedSessionManager,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmigratedProviders,\n\t\t\t\tmodelFallbackMessage: startupModel.modelFallbackMessage,\n\t\t\t\tpromptStashStore,\n\t\t\t\tstartupModelId: startupModel.model?.id,\n\t\t\t\tinitialSession,\n\t\t\t\tinitialScopeKey,\n\t\t\t\tverbose: parsed.verbose,\n\t\t\t});\n\t\t};\n\t\tif (\n\t\t\tshouldOpenAgentsViewForDaemonInteractive({\n\t\t\t\tuseDaemonInteractive: useDaemonInteractive && !parsed.noSession,\n\t\t\t\texplicitAgentsView,\n\t\t\t\tneedsOnboarding: shouldRunOnboarding({\n\t\t\t\t\tsettingsManager,\n\t\t\t\t\tmodelRegistry: services.modelRegistry,\n\t\t\t\t\tmodel: startupModel.model,\n\t\t\t\t}),\n\t\t\t\tresume: parsed.resume,\n\t\t\t\tcontinue: parsed.continue,\n\t\t\t\tfork: parsed.fork,\n\t\t\t})\n\t\t) {\n\t\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\t\tawait preloadCodeHighlighter();\n\t\t\tprintTimings();\n\t\t\tawait launchAgentsView();\n\t\t\treturn;\n\t\t}\n\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\t// A fresh default chat opens a real but message-less session; the lifecycle\n\t\t// axis treats it as a draft (hidden, discarded on detach if never used), so\n\t\t// no DeferredAgentConnection is needed to avoid creating it up front.\n\t\tconst isFreshDefaultSession =\n\t\t\t!activeDaemonSessionSummary && !getInteractiveDaemonSessionPath(parsed, sessionManager);\n\t\tconst { connection, summary } = await createDaemonClientConnection({\n\t\t\tsocketPath: daemonSocketPath,\n\t\t\tconfig: defaultSessionConfig,\n\t\t\tactiveSessionId: activeDaemonSessionSummary\n\t\t\t\t? getDaemonSummaryActiveSessionId(activeDaemonSessionSummary)\n\t\t\t\t: undefined,\n\t\t\tsessionPath: getInteractiveDaemonSessionPath(parsed, sessionManager),\n\t\t\tclientOwned: parsed.noSession,\n\t\t\tnoSession: parsed.noSession,\n\t\t\tsupportsExtensionUi: true,\n\t\t});\n\t\tconst agentConnection: AgentConnection = connection;\n\t\tconst attachModelFallbackMessage = isFreshDefaultSession\n\t\t\t? startupModel.modelFallbackMessage\n\t\t\t: resolveAttachModelFallbackMessage(summary, startupModel.modelFallbackMessage);\n\n\t\tconst interactiveMode = new InteractiveMode({\n\t\t\tagentConnection,\n\t\t\tdaemonSocketPath,\n\t\t\tuiServices: daemonUiServices,\n\t\t\tpromptStashStore,\n\t\t\tpromptStashSessionId: summary.sessionId,\n\t\t\tbindLocalSessionExtensions: false,\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage: attachModelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t\t// Resumed/attached daemon sessions are part of the same fleet; left\n\t\t\t// arrow takes them to the agents view like any other session. The agents\n\t\t\t// view was not rendered here, so we intentionally leave\n\t\t\t// agentsViewOwnsStartupNotices unset and let the in-session fallback run.\n\t\t\treturnToAgentsView: !parsed.noSession,\n\t\t\tsessionDepth: summary.rlmDepth,\n\t\t\t// Direct launch has only the attached summary; the live+passive+saved\n\t\t\t// unified catalog index is not built until the agents view opens. Retained\n\t\t\t// child snapshots augment this running-child fallback inside chat.\n\t\t\tsessionHasChildren: summary.hasRunningRlmChildren === true,\n\t\t});\n\n\t\tawait preloadCodeHighlighter();\n\t\tprintTimings();\n\t\tconst interactiveResult = await interactiveMode.run();\n\t\tif (parsed.noSession) {\n\t\t\treturn;\n\t\t}\n\t\tconst returnedSummary = {\n\t\t\t...summary,\n\t\t\t...interactiveResult.source,\n\t\t\tid: interactiveResult.source.activeSessionId ?? summary.id,\n\t\t};\n\t\tconst initialScopeKey =\n\t\t\tinteractiveResult.type === \"scoped_agents_view\"\n\t\t\t\t? {\n\t\t\t\t\t\tsessionId: interactiveResult.source.sessionId,\n\t\t\t\t\t\tactiveSessionId: interactiveResult.source.activeSessionId,\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tawait launchAgentsView(returnedSummary, initialScopeKey);\n\t\treturn;\n\t}\n\tif (useDaemonClient) {\n\t\tconst settingsManager = SettingsManager.create(sessionManager.getCwd(), agentDir);\n\t\tlet stdinContent: string | undefined;\n\t\tif (appMode !== \"rpc\" && appMode !== \"acp\") {\n\t\t\tstdinContent = await readPipedStdin();\n\t\t}\n\t\ttime(\"readPipedStdin\");\n\t\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\t\tparsed,\n\t\t\tsettingsManager.getImageAutoResize(),\n\t\t\tstdinContent,\n\t\t);\n\t\ttime(\"prepareInitialMessage\");\n\t\tinitTheme(settingsManager.getTheme(), false);\n\t\ttime(\"initTheme\");\n\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\tlet connection: DaemonAgentConnection;\n\t\tlet summary: SessionSummary;\n\t\ttry {\n\t\t\t({ connection, summary } = await createDaemonClientConnection({\n\t\t\t\tsocketPath: daemonSocketPath,\n\t\t\t\tconfig: defaultSessionConfig,\n\t\t\t\tsessionPath: parsed.noSession ? undefined : sessionManager.getSessionFile(),\n\t\t\t\tcontinueRecent: parsed.continue,\n\t\t\t\tclientOwned: isClientOwnedDaemonSession(appMode, parsed.noSession),\n\t\t\t\tnoSession: parsed.noSession,\n\t\t\t\tsupportsExtensionUi: appMode === \"rpc\",\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tif (error instanceof SessionAlreadyActiveError) {\n\t\t\t\tconsole.error(chalk.red(`Error: ${error.message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostics = summary.diagnostics ?? [];\n\t\treportDiagnostics(diagnostics);\n\t\tif (diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\t\tawait connection.dispose();\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!summary.model) {\n\t\t\tconsole.error(chalk.red(summary.modelFallbackMessage ?? formatNoModelsAvailableMessage()));\n\t\t\tawait connection.dispose();\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tprintTimings();\n\t\tif (appMode === \"rpc\") {\n\t\t\treturn await runRpcModeWithConnection(connection);\n\t\t}\n\t\tif (appMode === \"acp\") {\n\t\t\treturn await runAcpModeWithConnection(connection);\n\t\t}\n\t\tconst exitCode = await runPrintModeWithConnection(connection, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n\n\tlet runtime: AgentSessionRuntime;\n\ttry {\n\t\truntime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\tsessionConfig: defaultSessionConfig,\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof SessionAlreadyActiveError) {\n\t\t\tconsole.error(chalk.red(`Error: ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n\tconst { services, session, modelFallbackMessage } = runtime;\n\tinstallOwnedSessionRecoveryTracking(runtime);\n\tconst { settingsManager, modelRegistry } = services;\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(modelRegistry, searchPattern);\n\t\tprocess.exit(0);\n\t}\n\n\t// Read piped stdin content (if any) - skip for RPC/daemon modes which use other transports\n\tlet stdinContent: string | undefined;\n\tif (appMode !== \"rpc\" && appMode !== \"acp\" && appMode !== \"daemon\") {\n\t\tstdinContent = await readPipedStdin();\n\t}\n\ttime(\"readPipedStdin\");\n\n\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\tparsed,\n\t\tsettingsManager.getImageAutoResize(),\n\t\tstdinContent,\n\t);\n\ttime(\"prepareInitialMessage\");\n\tinitTheme(settingsManager.getTheme(), appMode === \"interactive\");\n\ttime(\"initTheme\");\n\n\t// Show deprecation warnings in interactive mode\n\tif (appMode === \"interactive\" && deprecationWarnings.length > 0) {\n\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t}\n\n\tconst scopedModels = [...session.scopedModels];\n\ttime(\"resolveModelScope\");\n\treportDiagnostics(runtime.diagnostics);\n\tif (runtime.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\tprocess.exit(1);\n\t}\n\ttime(\"createAgentSession\");\n\n\tif (appMode !== \"interactive\" && appMode !== \"daemon\" && !session.model) {\n\t\tconsole.error(chalk.red(formatNoModelsAvailableMessage()));\n\t\tprocess.exit(1);\n\t}\n\n\tif (appMode === \"rpc\") {\n\t\tprintTimings();\n\t\tawait runRpcMode(runtime);\n\t} else if (appMode === \"acp\") {\n\t\tprintTimings();\n\t\tawait runAcpMode(runtime);\n\t} else if (appMode === \"interactive\") {\n\t\tif (explicitAgentsView || parsed.resume === true) {\n\t\t\tconsole.error(chalk.yellow(\"Warning: the agents view needs the daemon; opening a normal chat instead\"));\n\t\t}\n\t\tif (scopedModels.length > 0 && (parsed.verbose || !settingsManager.getQuietStartup())) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst interactiveMode = new InteractiveMode({\n\t\t\tagentConnection: new InProcessAgentConnection(runtime),\n\t\t\tlocalSessionHost: createInteractiveModeLocalSessionHost(runtime),\n\t\t\tpromptStashStore: new ClientPromptStashStore(),\n\t\t\tpromptStashSessionId: session.sessionId,\n\t\t\tbindLocalSessionExtensions: true,\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t});\n\t\tif (startupBenchmark) {\n\t\t\tawait interactiveMode.init();\n\t\t\ttime(\"interactiveMode.init\");\n\t\t\tprintTimings();\n\t\t\tinteractiveMode.stop();\n\t\t\tstopThemeWatcher();\n\t\t\tif (process.stdout.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t\t}\n\t\t\tif (process.stderr.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stderr.once(\"drain\", resolve));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tawait preloadCodeHighlighter();\n\t\tprintTimings();\n\t\tawait interactiveMode.run();\n\t} else {\n\t\tprintTimings();\n\t\tconst exitCode = await runPrintMode(runtime, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n}\n"]}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,OAAO,EAAE,KAAK,IAAI,EAAwB,MAAM,eAAe,CAAC;AAuBhE,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAG9B,MAAM,gCAAgC,CAAC;AAexC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAMnE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAQ/D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAa3D,OAAO,EAuBN,KAAK,cAAc,EACnB,MAAM,kBAAkB,CAAC;AAqD1B,MAAM,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC5C,0EAA0E;AAC1E,MAAM,MAAM,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE5C,wBAAgB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAE3G;AAED,wBAAgB,oCAAoC,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAEjH;AAyBD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,OAAO,GAAG,OAAO,CAEzF;AAGD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,kBAAkB,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKtG;AAED,MAAM,WAAW,2BAA2B;IAC3C,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,MAAM,gCAAgC,GAAG,2BAA2B,CAAC;AAE3E,8EAA8E;AAC9E,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAOxF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,2BAA2B,GAAG,OAAO,CAInF;AAED,wBAAgB,4BAA4B,CAC3C,OAAO,EAAE,2BAA2B,GAAG;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iCAAiC,CAAC,EAAE,OAAO,CAAC;CAC5C,GACC,OAAO,CAET;AAED,wBAAgB,uCAAuC,CACtD,oBAAoB,EAAE,OAAO,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAET;AAED,MAAM,WAAW,yBAAyB;IACzC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,wCAAwC,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAWpG;AAED,MAAM,WAAW,uCAAuC;IACvD,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,oDAAoD,CACnE,OAAO,EAAE,uCAAuC,GAC9C,OAAO,CAOT;AAED,MAAM,WAAW,iCAAiC;IACjD,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,wBAAgB,2CAA2C,CAAC,OAAO,EAAE,iCAAiC,GAAG,OAAO,CAM/G;AAED,KAAK,gCAAgC,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;AAEtH,UAAU,uCAAuC;IAChD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,gCAAgC,CAAC;CAC1C;AAED,wBAAsB,mDAAmD,CACxE,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uCAA4C,GACnD,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CASrC;AAmID,wBAAsB,oBAAoB,CACzC,MAAM,EAAE,IAAI,EACZ,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,OAAO,CAAC,cAAc,CAAC,CA4CzB;AA0LD,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,yBAAyB,GAAG,yBAAyB,CAE7G;AAED,wBAAgB,4BAA4B,CAC3C,cAAc,EAAE,yBAAyB,EACzC,qBAAqB,CAAC,EAAE,yBAAyB,GAC/C,yBAAyB,CA2B3B;AAoND,wBAAgB,4CAA4C,CAC3D,SAAS,EAAE,SAAS,cAAc,EAAE,EACpC,WAAW,EAAE,MAAM,GACjB,cAAc,GAAG,SAAS,CAQ5B;AAwFD,MAAM,WAAW,WAAW;IAC3B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,CAAC;CACxC;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,WAAW,sBAkpB/D","sourcesContent":["/**\n * Main entry point for the coding agent CLI.\n *\n * This file handles CLI argument parsing and translates them into\n * createAgentSession() options. The SDK does the heavy lifting.\n */\n\nimport { join, resolve } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { type Api, type ImageContent, type Model, modelsAreEqual } from \"@asm-agent/ai\";\nimport { registerBuiltinMcpOAuthProviders } from \"@asm-agent/ai/mcp\";\nimport { ProcessTerminal, setKeybindings, TUI } from \"@asm-agent/tui\";\nimport chalk from \"chalk\";\nimport { type Args, type Mode, parseArgs } from \"./cli/args.js\";\nimport { formatTopLevelHelp } from \"./cli/command-registry.js\";\nimport {\n\tensureInteractiveDaemonRunning,\n\tisDaemonSessionSummary,\n\tlistActiveDaemonSessionSummaries,\n\tprobeRunningDaemonSessions,\n\tStaleDaemonError,\n\tshutdownDaemonAndWait,\n} from \"./cli/daemon-launch.js\";\nimport { confirmDaemonSessionLoss, type DaemonSessionLossCopy, pluralizeSessions } from \"./cli/daemon-stop-confirm.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { buildInitialMessage } from \"./cli/initial-message.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { installOwnedSessionRecoveryTracking, isOwnedSessionWorkerProcess } from \"./cli/owned-session-worker.js\";\nimport { handlePublicCommand } from \"./cli/public-command.js\";\nimport {\n\tlooksLikeSessionPath,\n\tresolveSessionPath,\n\tSessionSelectorError,\n\tSessionSelectorNotFoundError,\n} from \"./cli/session-resolver.js\";\nimport { APP_NAME, expandTildePath, getAgentDir, getSessionDirEnvOverride, VERSION } from \"./config.js\";\nimport {\n\ttype AgentExecutionMode,\n\ttype AgentSessionRuntimeConfig,\n\tmergeAgentSessionRuntimeConfig,\n\tmergeAutonomousConfig,\n} from \"./core/agent-session-config.js\";\nimport {\n\ttype AgentSessionRuntime,\n\ttype CreateAgentSessionRuntimeFactory,\n\tcreateAgentSessionRuntime,\n} from \"./core/agent-session-runtime.js\";\nimport {\n\ttype AgentSessionRuntimeDiagnostic,\n\ttype AgentSessionServices,\n\tcreateAgentSessionFromServices,\n\tcreateAgentSessionServices,\n} from \"./core/agent-session-services.js\";\nimport { formatNoModelsAvailableMessage } from \"./core/auth-guidance.js\";\nimport { AuthStorage } from \"./core/auth-storage.js\";\nimport { exportFromFile } from \"./core/export-html/index.js\";\nimport type { ExtensionFactory } from \"./core/extensions/types.js\";\nimport { KeybindingsManager } from \"./core/keybindings.js\";\nimport { installFileLogSink, setLogContext } from \"./core/logging.js\";\nimport type { ModelRegistry } from \"./core/model-registry.js\";\nimport { findInitialModel, resolveCliModel, resolveModelScope, type ScopedModel } from \"./core/model-resolver.js\";\nimport { restoreStdout, takeOverStdout } from \"./core/output-guard.js\";\nimport type { CreateAgentSessionOptions } from \"./core/sdk.js\";\nimport {\n\tformatMissingSessionCwdPrompt,\n\tgetMissingSessionCwdIssue,\n\tMissingSessionCwdError,\n\ttype SessionCwdIssue,\n} from \"./core/session-cwd.js\";\nimport { canonicalSessionPath, SessionAlreadyActiveError } from \"./core/session-lease.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { printTimings, resetTimings, time } from \"./core/timings.js\";\nimport { runMigrations, showDeprecationWarnings } from \"./migrations.js\";\nimport { isDaemonCatalogProcess, runDaemonCatalogProcess } from \"./modes/daemon/daemon-catalog-process.js\";\nimport { deserializeDaemonError } from \"./modes/daemon/daemon-errors.js\";\nimport { collectDaemonClientEnv, collectDaemonLaunchEnv } from \"./modes/daemon/daemon-protocol.js\";\nimport {\n\tDAEMON_WORKER_ACTIVE_SESSION_ID_ENV,\n\tisDaemonWorkerProcess,\n\trequireDaemonWorkerAuthenticationToken,\n\twaitForDaemonWorkerStartupGate,\n} from \"./modes/daemon/daemon-worker-protocol.js\";\nimport {\n\ttype AgentConnection,\n\ttype AgentsViewScopeKey,\n\tClientPromptStashStore,\n\tcreateInteractiveModeLocalSessionHost,\n\tcreateInteractiveModeUiServicesFromServices,\n\tDaemonAgentConnection,\n\tDaemonCapabilityUnavailableError,\n\tDaemonClient,\n\tdefaultDaemonSocketPath,\n\tInProcessAgentConnection,\n\tInteractiveMode,\n\tnormalizeSocketPath,\n\tresolveAttachModelFallbackMessage,\n\trunAcpMode,\n\trunAcpModeWithConnection,\n\trunAgentsViewMode,\n\trunDaemonMode,\n\trunDaemonSupervisorMode,\n\trunPrintMode,\n\trunPrintModeWithConnection,\n\trunRpcMode,\n\trunRpcModeWithConnection,\n\ttype SessionSummary,\n} from \"./modes/index.js\";\nimport { ExtensionSelectorComponent } from \"./modes/interactive/components/extension-selector.js\";\nimport { shouldRunOnboarding } from \"./modes/interactive/onboarding.js\";\nimport { initTheme, preloadCodeHighlighter, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { handleConfigCommand } from \"./package-manager-cli.js\";\nimport { isLocalPath } from \"./utils/paths.js\";\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY (interactive terminal).\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\t// If stdin is a TTY, we're running interactively - don't read stdin\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\nfunction collectSettingsDiagnostics(\n\tsettingsManager: SettingsManager,\n\tcontext: string,\n): AgentSessionRuntimeDiagnostic[] {\n\treturn settingsManager.drainErrors().map(({ scope, error }) => ({\n\t\ttype: \"warning\",\n\t\tmessage: `(${context}, ${scope} settings) ${error.message}`,\n\t}));\n}\n\nfunction reportDiagnostics(diagnostics: readonly AgentSessionRuntimeDiagnostic[]): void {\n\tfor (const diagnostic of diagnostics) {\n\t\tconst color = diagnostic.type === \"error\" ? chalk.red : diagnostic.type === \"warning\" ? chalk.yellow : chalk.dim;\n\t\tconst prefix = diagnostic.type === \"error\" ? \"Error: \" : diagnostic.type === \"warning\" ? \"Warning: \" : \"\";\n\t\tconsole.error(color(`${prefix}${diagnostic.message}`));\n\t}\n}\n\nfunction isTruthyEnvFlag(value: string | undefined): boolean {\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\nexport type ClientMode = AgentExecutionMode;\n/** Compatibility view of the CLI's internal daemon process entrypoint. */\nexport type AppMode = ClientMode | \"daemon\";\n\nexport function shouldRejectNonInteractiveAttach(attachAgent: string | undefined, appMode: AppMode): boolean {\n\treturn attachAgent !== undefined && appMode !== \"interactive\";\n}\n\nexport function shouldRejectNonInteractiveBareResume(resume: true | string | undefined, appMode: AppMode): boolean {\n\treturn resume === true && appMode !== \"interactive\";\n}\n\nfunction resolveAppMode(parsed: Args, stdinIsTTY: boolean): AppMode {\n\tif (parsed.mode === \"daemon\") {\n\t\treturn \"daemon\";\n\t}\n\tif (parsed.mode === \"rpc\") {\n\t\treturn \"rpc\";\n\t}\n\tif (parsed.mode === \"acp\") {\n\t\treturn \"acp\";\n\t}\n\tif (parsed.mode === \"json\") {\n\t\treturn \"json\";\n\t}\n\tif (parsed.print || !stdinIsTTY) {\n\t\treturn \"print\";\n\t}\n\treturn \"interactive\";\n}\n\nfunction toPrintOutputMode(appMode: AppMode): Exclude<Mode, \"rpc\" | \"acp\" | \"daemon\"> {\n\treturn appMode === \"json\" ? \"json\" : \"text\";\n}\n\nexport function isClientOwnedDaemonSession(appMode: AppMode, noSession?: boolean): boolean {\n\treturn appMode !== \"acp\" || noSession === true;\n}\n\n// `asm-agent agents` opens the agents view directly.\nexport function parseAgentsViewCommand(args: string[]): { explicitAgentsView: boolean; args: string[] } {\n\tif (args[0] === \"agents\") {\n\t\treturn { explicitAgentsView: true, args: args.slice(1) };\n\t}\n\treturn { explicitAgentsView: false, args };\n}\n\nexport interface DaemonClientStartupDecision {\n\tappMode: AppMode;\n\tstartupBenchmark: boolean;\n\tnoSession?: boolean;\n\thelp?: boolean;\n\tlistModels?: string | true;\n}\n\nexport type InteractiveDaemonStartupDecision = DaemonClientStartupDecision;\n\n/** Retained for callers that only classify persistent interactive startup. */\nexport function shouldUseDaemonInteractive(options: DaemonClientStartupDecision): boolean {\n\treturn (\n\t\toptions.appMode === \"interactive\" &&\n\t\t!options.startupBenchmark &&\n\t\t!options.noSession &&\n\t\toptions.listModels === undefined\n\t);\n}\n\nexport function shouldUseDaemonClient(options: DaemonClientStartupDecision): boolean {\n\treturn (\n\t\toptions.appMode !== \"daemon\" && !options.startupBenchmark && !options.help && options.listModels === undefined\n\t);\n}\n\nexport function shouldUseDaemonClientRuntime(\n\toptions: DaemonClientStartupDecision & {\n\t\townedSessionWorker?: boolean;\n\t\thasProcessLocalExtensionFactories?: boolean;\n\t},\n): boolean {\n\treturn shouldUseDaemonClient(options) && !options.ownedSessionWorker && !options.hasProcessLocalExtensionFactories;\n}\n\nexport function shouldEnsureInteractiveDaemonForStartup(\n\tuseDaemonInteractive: boolean,\n\tattachAgent: string | undefined,\n): boolean {\n\treturn useDaemonInteractive && attachAgent === undefined;\n}\n\nexport interface AgentsViewStartupDecision {\n\tuseDaemonInteractive: boolean;\n\tneedsOnboarding: boolean;\n\texplicitAgentsView?: boolean;\n\tresume?: true | string;\n\tcontinue?: boolean;\n\tfork?: string;\n}\n\nexport function shouldOpenAgentsViewForDaemonInteractive(options: AgentsViewStartupDecision): boolean {\n\tconst bareResume = options.resume === true;\n\tconst requestsAgentsView = bareResume || (options.explicitAgentsView && !options.needsOnboarding);\n\treturn (\n\t\toptions.useDaemonInteractive &&\n\t\t// A selector, continuation, or fork must open its target directly rather than the agents view.\n\t\t!!requestsAgentsView &&\n\t\ttypeof options.resume !== \"string\" &&\n\t\t!options.continue &&\n\t\t!options.fork\n\t);\n}\n\nexport interface DaemonInteractiveSessionManagerDecision {\n\tresume?: true | string;\n\tcontinue?: boolean;\n\tfork?: string;\n\thasActiveDaemonSession?: boolean;\n}\n\nexport function shouldUseEphemeralSessionManagerForDaemonInteractive(\n\toptions: DaemonInteractiveSessionManagerDecision,\n): boolean {\n\treturn (\n\t\t!options.hasActiveDaemonSession &&\n\t\t(options.resume === undefined || options.resume === true) &&\n\t\t!options.continue &&\n\t\t!options.fork\n\t);\n}\n\nexport interface DaemonActiveSessionLookupDecision {\n\tuseDaemonInteractive: boolean;\n\tresumeSelector?: string;\n\texplicitAttach?: boolean;\n}\n\nexport function shouldEnsureDaemonBeforeActiveSessionLookup(options: DaemonActiveSessionLookupDecision): boolean {\n\treturn (\n\t\toptions.useDaemonInteractive &&\n\t\toptions.resumeSelector !== undefined &&\n\t\t(options.explicitAttach || !looksLikeSessionPath(options.resumeSelector))\n\t);\n}\n\ntype ActiveDaemonSessionSummaryLookup = (socketPath: string, selector: string) => Promise<SessionSummary | undefined>;\n\ninterface ActiveDaemonSessionSummaryLookupOptions {\n\tfallbackOnError?: boolean;\n\tlookup?: ActiveDaemonSessionSummaryLookup;\n}\n\nexport async function findActiveDaemonSessionSummaryForInteractiveStartup(\n\tsocketPath: string,\n\tselector: string,\n\toptions: ActiveDaemonSessionSummaryLookupOptions = {},\n): Promise<SessionSummary | undefined> {\n\ttry {\n\t\treturn await (options.lookup ?? findActiveDaemonSessionSummary)(socketPath, selector);\n\t} catch (error) {\n\t\tif (options.fallbackOnError === false) {\n\t\t\tthrow error;\n\t\t}\n\t\treturn undefined;\n\t}\n}\n\nasync function prepareInitialMessage(\n\tparsed: Args,\n\tautoResizeImages: boolean,\n\tstdinContent?: string,\n): Promise<{\n\tinitialMessage?: string;\n\tinitialImages?: ImageContent[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn buildInitialMessage({ parsed, stdinContent });\n\t}\n\n\tconst { text, images } = await processFileArguments(parsed.fileArgs, { autoResizeImages });\n\treturn buildInitialMessage({\n\t\tparsed,\n\t\tfileText: text,\n\t\tfileImages: images,\n\t\tstdinContent,\n\t});\n}\n\n/** Prompt user for yes/no confirmation */\nasync function promptConfirm(message: string): Promise<boolean> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\trl.question(`${message} [y/N] `, (answer) => {\n\t\t\trl.close();\n\t\t\tresolve(answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\");\n\t\t});\n\t});\n}\n\n// Only busy sessions (streaming, compacting, or pending messages) lose work;\n// idle loaded sessions reload from disk on the fresh daemon.\nconst STARTUP_SESSION_LOSS_COPY: DaemonSessionLossCopy = {\n\tbusyDetail(count) {\n\t\tconst { noun, pronoun } = pluralizeSessions(count);\n\t\treturn `A background service from a different ASM Agent version is running with ${count} busy ${noun}. Stopping it will terminate ${pronoun}.`;\n\t},\n\tunlistableDetail:\n\t\t\"A background service from a different ASM Agent version is running and its sessions could not be listed. Stopping it may terminate active sessions.\",\n\tquestion: \"Stop it and continue?\",\n\tnonTtyHint: 'Run \"asm-agent shutdown\" to stop it, then retry.',\n};\n\n// The promise to keep after awaiting readiness. Wrapped in an object so it\n// survives `await` (which would otherwise flatten a returned Promise to void).\ntype DaemonReadyResult = { ready: Promise<void> | undefined };\n\n// A stale-version daemon couldn't be taken over automatically (busy or stuck).\n// Offer to stop it (default No) and start a fresh daemon, or exit. Returns the\n// fresh ready promise so callers stop re-handling the original rejection.\nasync function takeOverStaleDaemonOrExit(socketPath: string): Promise<DaemonReadyResult> {\n\tconst probe = await probeRunningDaemonSessions(socketPath);\n\tconst confirmed = await confirmDaemonSessionLoss(probe, { force: false, copy: STARTUP_SESSION_LOSS_COPY });\n\tif (!confirmed) {\n\t\t// Non-TTY already printed the reason; at a TTY the user declined.\n\t\tif (process.stdin.isTTY) {\n\t\t\tconsole.error(chalk.dim(\"Cancelled.\"));\n\t\t}\n\t\tprocess.exit(1);\n\t}\n\tif (!(await shutdownDaemonAndWait(socketPath))) {\n\t\tconsole.error(\n\t\t\tchalk.red(`Could not stop the background service on ${socketPath}. Run \"asm-agent shutdown\" and retry.`),\n\t\t);\n\t\tprocess.exit(1);\n\t}\n\tconst ready = ensureInteractiveDaemonRunning(socketPath);\n\ttry {\n\t\tawait ready;\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Could not start the background service: ${message}`));\n\t\tprocess.exit(1);\n\t}\n\treturn { ready };\n}\n\n// Resolves the daemon-ready promise, returning the promise to keep (the same\n// one on success, or the fresh one from a stale-daemon takeover) so repeat\n// calls don't re-handle the original rejection.\nasync function awaitDaemonReady(daemonReady: Promise<void> | undefined): Promise<DaemonReadyResult> {\n\tif (!daemonReady) {\n\t\treturn { ready: daemonReady };\n\t}\n\ttry {\n\t\tawait daemonReady;\n\t\treturn { ready: daemonReady };\n\t} catch (error) {\n\t\tif (error instanceof StaleDaemonError) {\n\t\t\treturn takeOverStaleDaemonOrExit(error.socketPath);\n\t\t}\n\t\tthrow error;\n\t}\n}\n\nfunction validateForkFlags(parsed: Args): void {\n\tif (!parsed.fork) return;\n\n\tconst conflictingFlags = [\n\t\tparsed.continue ? \"--continue\" : undefined,\n\t\tparsed.resume ? \"--resume\" : undefined,\n\t\tparsed.noSession ? \"--no-session\" : undefined,\n\t].filter((flag): flag is string => flag !== undefined);\n\n\tif (conflictingFlags.length > 0) {\n\t\tconsole.error(chalk.red(`Error: --fork cannot be combined with ${conflictingFlags.join(\", \")}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction forkSessionOrExit(sourcePath: string, cwd: string, sessionDir?: string): SessionManager {\n\ttry {\n\t\treturn SessionManager.forkFrom(sourcePath, cwd, sessionDir);\n\t} catch (error: unknown) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n\nfunction getResumeSelector(parsed: Pick<Args, \"resume\">): string | undefined {\n\treturn typeof parsed.resume === \"string\" ? parsed.resume : undefined;\n}\n\nexport async function createSessionManager(\n\tparsed: Args,\n\tcwd: string,\n\tsessionDir: string | undefined,\n): Promise<SessionManager> {\n\tconst explicitCwdOverride = parsed.cwd ? cwd : undefined;\n\n\tif (parsed.noSession) {\n\t\treturn SessionManager.inMemory();\n\t}\n\n\tif (parsed.fork) {\n\t\tconst resolved = await resolveSessionPath(parsed.fork, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\tcase \"global\":\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t}\n\t}\n\n\tconst resumeSelector = getResumeSelector(parsed);\n\tif (resumeSelector) {\n\t\tconst resolved = await resolveSessionPath(resumeSelector, cwd, sessionDir);\n\n\t\tswitch (resolved.type) {\n\t\t\tcase \"path\":\n\t\t\tcase \"local\":\n\t\t\t\treturn SessionManager.open(resolved.path, sessionDir, explicitCwdOverride);\n\n\t\t\tcase \"global\": {\n\t\t\t\tconsole.log(chalk.yellow(`Session found in different project: ${resolved.cwd}`));\n\t\t\t\tconst shouldFork = await promptConfirm(\"Fork this session into current directory?\");\n\t\t\t\tif (!shouldFork) {\n\t\t\t\t\tconsole.log(chalk.dim(\"Aborted.\"));\n\t\t\t\t\tprocess.exit(0);\n\t\t\t\t}\n\t\t\t\treturn forkSessionOrExit(resolved.path, cwd, sessionDir);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (parsed.continue) {\n\t\treturn SessionManager.continueRecent(cwd, sessionDir);\n\t}\n\n\treturn SessionManager.create(cwd, sessionDir);\n}\n\nfunction buildSessionOptions(\n\tconfig: AgentSessionRuntimeConfig,\n\tscopedModels: ScopedModel[],\n\thasExistingSession: boolean,\n\tmodelRegistry: ModelRegistry,\n\tsettingsManager: SettingsManager,\n): {\n\toptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n} {\n\tconst options: CreateAgentSessionOptions = {};\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [];\n\tlet cliThinkingFromModel = false;\n\n\t// Model from CLI\n\t// - supports --provider <name> --model <pattern>\n\t// - supports --model <provider>/<pattern>\n\tif (config.model) {\n\t\tconst resolved = resolveCliModel({\n\t\t\tcliProvider: config.provider,\n\t\t\tcliModel: config.model,\n\t\t\tmodelRegistry,\n\t\t});\n\t\tif (resolved.warning) {\n\t\t\tdiagnostics.push({ type: \"warning\", message: resolved.warning });\n\t\t}\n\t\tif (resolved.error) {\n\t\t\tdiagnostics.push({ type: \"error\", message: resolved.error });\n\t\t}\n\t\tif (resolved.model) {\n\t\t\toptions.model = resolved.model;\n\t\t\t// Allow \"--model <pattern>:<thinking>\" as a shorthand.\n\t\t\t// Explicit --thinking still takes precedence (applied later).\n\t\t\tif (!config.thinking && resolved.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = resolved.thinkingLevel;\n\t\t\t\tcliThinkingFromModel = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!options.model && scopedModels.length > 0 && !hasExistingSession) {\n\t\t// Check if saved default is in scoped models - use it if so, otherwise first scoped model\n\t\tconst savedProvider = settingsManager.getDefaultProvider();\n\t\tconst savedModelId = settingsManager.getDefaultModel();\n\t\tconst savedModel = savedProvider && savedModelId ? modelRegistry.find(savedProvider, savedModelId) : undefined;\n\t\tconst savedInScope = savedModel ? scopedModels.find((sm) => modelsAreEqual(sm.model, savedModel)) : undefined;\n\n\t\tif (savedInScope) {\n\t\t\toptions.model = savedInScope.model;\n\t\t\t// Use thinking level from scoped model config if explicitly set\n\t\t\tif (!config.thinking && savedInScope.thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = savedInScope.thinkingLevel;\n\t\t\t}\n\t\t} else {\n\t\t\toptions.model = scopedModels[0].model;\n\t\t\t// Use thinking level from first scoped model if explicitly set\n\t\t\tif (!config.thinking && scopedModels[0].thinkingLevel) {\n\t\t\t\toptions.thinkingLevel = scopedModels[0].thinkingLevel;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Thinking level from CLI (takes precedence over scoped model thinking levels set above)\n\tif (config.thinking) {\n\t\toptions.thinkingLevel = config.thinking;\n\t}\n\n\t// Scoped models for Ctrl+P cycling\n\t// Keep thinking level undefined when not explicitly set in the model pattern.\n\t// Undefined means \"inherit current session thinking level\" during cycling.\n\tif (scopedModels.length > 0) {\n\t\toptions.scopedModels = scopedModels.map((sm) => ({\n\t\t\tmodel: sm.model,\n\t\t\tthinkingLevel: sm.thinkingLevel,\n\t\t}));\n\t}\n\n\t// API key from CLI - set in authStorage\n\t// (handled by caller before createAgentSession)\n\n\t// Tools\n\tif (config.noTools) {\n\t\toptions.noTools = \"all\";\n\t} else if (config.noBuiltinTools) {\n\t\toptions.noTools = \"builtin\";\n\t}\n\tif (config.tools) {\n\t\toptions.tools = [...config.tools];\n\t}\n\tif (config.autonomous) {\n\t\toptions.autonomous = mergeAutonomousConfig(undefined, config.autonomous);\n\t}\n\n\treturn { options, cliThinkingFromModel, diagnostics };\n}\n\nfunction resolveCliPaths(cwd: string, paths: string[] | undefined): string[] | undefined {\n\treturn paths?.map((value) => (isLocalPath(value) ? resolve(cwd, value) : value));\n}\n\nfunction runtimeAutonomousConfigFromArgs(parsed: Args): AgentSessionRuntimeConfig[\"autonomous\"] {\n\tconst hasAutonomousOptions =\n\t\tparsed.autonomous === true ||\n\t\tparsed.autonomousGates !== undefined ||\n\t\tparsed.autonomousGateRetries !== undefined ||\n\t\tparsed.autonomousGateTimeoutMs !== undefined ||\n\t\tparsed.autonomousMaxContinuations !== undefined ||\n\t\tparsed.autonomousMaxTurns !== undefined ||\n\t\tparsed.autonomousMaxTokens !== undefined ||\n\t\tparsed.autonomousTimeoutMs !== undefined;\n\tif (!hasAutonomousOptions) {\n\t\treturn undefined;\n\t}\n\tconst hasGateOptions =\n\t\tparsed.autonomousGates !== undefined ||\n\t\tparsed.autonomousGateRetries !== undefined ||\n\t\tparsed.autonomousGateTimeoutMs !== undefined;\n\treturn {\n\t\tenabled: true,\n\t\tmaxContinuations: parsed.autonomousMaxContinuations,\n\t\tmaxTurns: parsed.autonomousMaxTurns,\n\t\tmaxTokens: parsed.autonomousMaxTokens,\n\t\ttimeoutMs: parsed.autonomousTimeoutMs,\n\t\tgates: hasGateOptions\n\t\t\t? {\n\t\t\t\t\tcommands: parsed.autonomousGates,\n\t\t\t\t\tmaxRetries: parsed.autonomousGateRetries,\n\t\t\t\t\ttimeoutMs: parsed.autonomousGateTimeoutMs,\n\t\t\t\t}\n\t\t\t: undefined,\n\t};\n}\n\nfunction runtimeConfigFromArgs(\n\tparsed: Args,\n\tcwd: string,\n\tagentDir: string,\n\tsessionDir: string | undefined,\n\tappMode: AppMode,\n): AgentSessionRuntimeConfig {\n\treturn {\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionDir,\n\t\tprovider: parsed.provider,\n\t\tmodel: parsed.model,\n\t\tapiKey: parsed.apiKey,\n\t\tsystemPrompt: parsed.systemPrompt,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tthinking: parsed.thinking,\n\t\tmodels: parsed.models,\n\t\ttools: parsed.tools,\n\t\tnoTools: parsed.noTools,\n\t\tnoBuiltinTools: parsed.noBuiltinTools,\n\t\textensions: resolveCliPaths(cwd, parsed.extensions),\n\t\tnoExtensions: parsed.noExtensions,\n\t\tskills: resolveCliPaths(cwd, parsed.skills),\n\t\tnoSkills: parsed.noSkills,\n\t\tpromptTemplates: resolveCliPaths(cwd, parsed.promptTemplates),\n\t\tnoPromptTemplates: parsed.noPromptTemplates,\n\t\tthemes: resolveCliPaths(cwd, parsed.themes),\n\t\tnoThemes: parsed.noThemes,\n\t\tnoContextFiles: parsed.noContextFiles,\n\t\tautonomous: runtimeAutonomousConfigFromArgs(parsed),\n\t\textensionFlagValues: parsed.unknownFlags.size > 0 ? Object.fromEntries(parsed.unknownFlags.entries()) : undefined,\n\t\texecutionMode: appMode === \"daemon\" ? undefined : appMode,\n\t\t// Serialized refine for print/json/rpc: the client's appMode is NOT\n\t\t// \"daemon\" here — it's \"print\", \"json\", or \"rpc\". The daemon worker\n\t\t// receives this flag via AgentSessionRuntimeConfig and uses it\n\t\t// instead of its own appMode=\"daemon\".\n\t\tserializedRefine: appMode !== \"interactive\" && appMode !== \"daemon\",\n\t\tinitialGoal: parsed.goal ? { objective: parsed.goal, tokenBudget: parsed.goalTokenBudget } : undefined,\n\t};\n}\n\ninterface PreparedRuntimeServices {\n\tservices: AgentSessionServices;\n\tscopedModels: ScopedModel[];\n\tsessionOptions: CreateAgentSessionOptions;\n\tcliThinkingFromModel: boolean;\n\tdiagnostics: AgentSessionRuntimeDiagnostic[];\n}\n\nexport function daemonServerDefaultSessionConfig(config: AgentSessionRuntimeConfig): AgentSessionRuntimeConfig {\n\treturn { ...config, initialGoal: undefined };\n}\n\nexport function resolveRuntimeSessionOptions(\n\tsessionOptions: CreateAgentSessionOptions,\n\truntimeSessionOptions?: CreateAgentSessionOptions,\n): CreateAgentSessionOptions {\n\treturn {\n\t\tmodel: runtimeSessionOptions?.model ?? sessionOptions.model,\n\t\tthinkingLevel: runtimeSessionOptions?.thinkingLevel ?? sessionOptions.thinkingLevel,\n\t\tserviceTier: runtimeSessionOptions?.serviceTier ?? sessionOptions.serviceTier,\n\t\tscopedModels: runtimeSessionOptions?.scopedModels ?? sessionOptions.scopedModels,\n\t\ttools: runtimeSessionOptions?.tools ?? sessionOptions.tools,\n\t\tnoTools: runtimeSessionOptions?.noTools ?? sessionOptions.noTools,\n\t\tcustomTools: runtimeSessionOptions?.customTools ?? sessionOptions.customTools,\n\t\tinitialActiveToolNames: runtimeSessionOptions?.initialActiveToolNames,\n\t\tallowedToolNames: runtimeSessionOptions?.allowedToolNames,\n\t\tincludeGoals: runtimeSessionOptions?.includeGoals,\n\t\tincludeCompactSkill: runtimeSessionOptions?.includeCompactSkill,\n\t\trlmHeartbeatController: runtimeSessionOptions?.rlmHeartbeatController,\n\t\tagentMessageController: runtimeSessionOptions?.agentMessageController,\n\t\tagentObserveController: runtimeSessionOptions?.agentObserveController,\n\t\tautonomous:\n\t\t\t(runtimeSessionOptions?.rlmDepth ?? 0) > 0\n\t\t\t\t? mergeAutonomousConfig(sessionOptions.autonomous, { ...runtimeSessionOptions?.autonomous, enabled: false })\n\t\t\t\t: mergeAutonomousConfig(sessionOptions.autonomous, runtimeSessionOptions?.autonomous),\n\t\trlmDepth: runtimeSessionOptions?.rlmDepth,\n\t\trlmMaxDepth: runtimeSessionOptions?.rlmMaxDepth,\n\t\trlmSessionDir: runtimeSessionOptions?.rlmSessionDir,\n\t\trlmParentNodeId: runtimeSessionOptions?.rlmParentNodeId,\n\t\trlmParentAgent: runtimeSessionOptions?.rlmParentAgent,\n\t\tsubagentRuntimeHost: runtimeSessionOptions?.subagentRuntimeHost,\n\t};\n}\n\nasync function prepareRuntimeServices(options: {\n\tconfig: AgentSessionRuntimeConfig;\n\tcwd: string;\n\tagentDir: string;\n\tsessionManager: SessionManager;\n\textensionFactories?: ExtensionFactory[];\n\tsessionOptionsOverride?: CreateAgentSessionOptions;\n}): Promise<PreparedRuntimeServices> {\n\tconst { config, sessionManager } = options;\n\tconst effectiveAgentDir = config.agentDir ?? options.agentDir;\n\tconst authStorage = AuthStorage.create(join(effectiveAgentDir, \"auth.json\"), {\n\t\tusePrimeCliConfig: false,\n\t});\n\tconst services = await createAgentSessionServices({\n\t\tcwd: options.cwd,\n\t\tagentDir: effectiveAgentDir,\n\t\tauthStorage,\n\t\textensionFlagValues: new Map(Object.entries(config.extensionFlagValues ?? {})),\n\t\t// Subagents share the parent's Herdr pane; their own reporter would race\n\t\t// the parent's and a subagent quit would release the still-active pane.\n\t\tnoBuiltinHerdrReporter: (options.sessionOptionsOverride?.rlmDepth ?? 0) > 0,\n\t\tresourceLoaderOptions: {\n\t\t\tadditionalExtensionPaths: config.extensions,\n\t\t\tadditionalSkillPaths: config.skills,\n\t\t\tadditionalPromptTemplatePaths: config.promptTemplates,\n\t\t\tadditionalThemePaths: config.themes,\n\t\t\tnoExtensions: config.noExtensions,\n\t\t\tnoSkills: config.noSkills,\n\t\t\tnoPromptTemplates: config.noPromptTemplates,\n\t\t\tnoThemes: config.noThemes,\n\t\t\tnoContextFiles: config.noContextFiles,\n\t\t\tsystemPrompt: config.systemPrompt,\n\t\t\tappendSystemPrompt: config.appendSystemPrompt,\n\t\t\textensionFactories: options.extensionFactories,\n\t\t},\n\t});\n\tconst { settingsManager, modelRegistry, resourceLoader } = services;\n\tconst diagnostics: AgentSessionRuntimeDiagnostic[] = [\n\t\t...services.diagnostics,\n\t\t...collectSettingsDiagnostics(settingsManager, \"runtime creation\"),\n\t\t...resourceLoader.getExtensions().errors.map(({ path, error }) => ({\n\t\t\ttype: \"error\" as const,\n\t\t\tmessage: `Failed to load extension \"${path}\": ${error}`,\n\t\t})),\n\t];\n\n\tconst modelPatterns = config.models ?? settingsManager.getEnabledModels();\n\tconst scopedModels =\n\t\tmodelPatterns && modelPatterns.length > 0 ? await resolveModelScope(modelPatterns, modelRegistry) : [];\n\tconst {\n\t\toptions: sessionOptions,\n\t\tcliThinkingFromModel,\n\t\tdiagnostics: sessionOptionDiagnostics,\n\t} = buildSessionOptions(\n\t\tconfig,\n\t\tscopedModels,\n\t\tsessionManager.buildSessionContext().messages.length > 0,\n\t\tmodelRegistry,\n\t\tsettingsManager,\n\t);\n\tdiagnostics.push(...sessionOptionDiagnostics);\n\n\tconst effectiveSessionModel = options.sessionOptionsOverride?.model ?? sessionOptions.model;\n\tif (config.apiKey) {\n\t\tif (!effectiveSessionModel) {\n\t\t\tdiagnostics.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: \"--api-key requires a model to be specified via --model, --provider/--model, or --models\",\n\t\t\t});\n\t\t} else {\n\t\t\tauthStorage.setRuntimeApiKey(effectiveSessionModel.provider, config.apiKey);\n\t\t}\n\t}\n\n\treturn {\n\t\tservices,\n\t\tscopedModels,\n\t\tsessionOptions,\n\t\tcliThinkingFromModel,\n\t\tdiagnostics,\n\t};\n}\n\nasync function resolvePreparedStartupModel(options: {\n\tprepared: PreparedRuntimeServices;\n\tsessionManager: SessionManager;\n}): Promise<{ model: Model<Api> | undefined; modelFallbackMessage: string | undefined }> {\n\tconst { prepared, sessionManager } = options;\n\tconst { modelRegistry, settingsManager } = prepared.services;\n\tconst existingSession = sessionManager.buildSessionContext();\n\tconst hasExistingSession = existingSession.messages.length > 0;\n\n\tlet model = prepared.sessionOptions.model;\n\tlet modelFallbackMessage: string | undefined;\n\n\tif (!model && hasExistingSession && existingSession.model) {\n\t\tconst restoredModel = modelRegistry.find(existingSession.model.provider, existingSession.model.modelId);\n\t\tif (restoredModel && modelRegistry.hasConfiguredAuth(restoredModel)) {\n\t\t\tmodel = restoredModel;\n\t\t}\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = `Could not restore model ${existingSession.model.provider}/${existingSession.model.modelId}`;\n\t\t}\n\t}\n\n\tif (!model) {\n\t\tconst result = await findInitialModel({\n\t\t\tscopedModels: prepared.scopedModels,\n\t\t\tisContinuing: hasExistingSession,\n\t\t\tdefaultProvider: settingsManager.getDefaultProvider(),\n\t\t\tdefaultModelId: settingsManager.getDefaultModel(),\n\t\t\tdefaultThinkingLevel: settingsManager.getDefaultThinkingLevel(),\n\t\t\tmodelRegistry,\n\t\t});\n\t\tmodel = result.model;\n\t\tif (!model) {\n\t\t\tmodelFallbackMessage = formatNoModelsAvailableMessage();\n\t\t} else if (modelFallbackMessage) {\n\t\t\tmodelFallbackMessage += `. Using ${model.provider}/${model.id}`;\n\t\t}\n\t}\n\n\treturn { model, modelFallbackMessage };\n}\n\nasync function promptForMissingSessionCwd(\n\tissue: SessionCwdIssue,\n\tsettingsManager: SettingsManager,\n): Promise<string | undefined> {\n\tinitTheme(settingsManager.getTheme());\n\tsetKeybindings(KeybindingsManager.create());\n\n\treturn new Promise((resolve) => {\n\t\tconst ui = new TUI(new ProcessTerminal(), settingsManager.getShowHardwareCursor());\n\t\tui.setClearOnShrink(settingsManager.getClearOnShrink());\n\n\t\tlet settled = false;\n\t\tconst finish = (result: string | undefined) => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tui.stop();\n\t\t\tresolve(result);\n\t\t};\n\n\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\tformatMissingSessionCwdPrompt(issue),\n\t\t\t[\"Continue\", \"Cancel\"],\n\t\t\t(option) => finish(option === \"Continue\" ? issue.fallbackCwd : undefined),\n\t\t\t() => finish(undefined),\n\t\t\t{ tui: ui },\n\t\t);\n\t\tui.addChild(selector);\n\t\tui.setFocus(selector);\n\t\tui.start();\n\t});\n}\n\nfunction getDaemonSummaryActiveSessionId(summary: SessionSummary): string {\n\treturn summary.activeSessionId ?? summary.id;\n}\n\nfunction isUnknownActiveSessionError(message: string): boolean {\n\treturn message.startsWith(\"Unknown active session:\");\n}\n\nasync function findActiveDaemonSessionSummary(\n\tsocketPath: string,\n\tselector: string,\n): Promise<SessionSummary | undefined> {\n\tconst client = new DaemonClient(socketPath);\n\tawait client.connect(250);\n\n\ttry {\n\t\tconst response = await client.request({ type: \"get_state\", activeSessionId: selector }, 3000);\n\t\tif (!response.success) {\n\t\t\tif (isUnknownActiveSessionError(response.error)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthrow new Error(response.error);\n\t\t}\n\t\tif (!isDaemonSessionSummary(response.data)) {\n\t\t\tthrow new Error(\"Daemon returned an invalid active session summary\");\n\t\t}\n\t\treturn response.data;\n\t} finally {\n\t\tclient.close();\n\t}\n}\n\nfunction createSessionManagerForActiveDaemonSummary(summary: SessionSummary, fallbackCwd: string): SessionManager {\n\tconst cwd = summary.cwd || fallbackCwd;\n\tif (summary.sessionFile) {\n\t\ttry {\n\t\t\treturn SessionManager.open(summary.sessionFile, undefined, cwd);\n\t\t} catch {\n\t\t\treturn SessionManager.inMemory(cwd);\n\t\t}\n\t}\n\treturn SessionManager.inMemory(cwd);\n}\n\nfunction getInteractiveDaemonSessionPath(parsed: Args, sessionManager: SessionManager): string | undefined {\n\tif (!parsed.resume && !parsed.continue && !parsed.fork) {\n\t\treturn undefined;\n\t}\n\treturn sessionManager.getSessionFile();\n}\n\nexport function findActiveDaemonSessionSummaryForSessionFile(\n\tsummaries: readonly SessionSummary[],\n\tsessionPath: string,\n): SessionSummary | undefined {\n\tconst resolvedSessionPath = canonicalSessionPath(sessionPath);\n\treturn summaries.find(\n\t\t(summary) =>\n\t\t\tsummary.activeSessionId !== undefined &&\n\t\t\tsummary.sessionFile !== undefined &&\n\t\t\tcanonicalSessionPath(summary.sessionFile) === resolvedSessionPath,\n\t);\n}\n\nasync function createDaemonClientConnection(options: {\n\tsocketPath: string;\n\tconfig: AgentSessionRuntimeConfig;\n\tsessionPath?: string;\n\tcontinueRecent?: boolean;\n\tactiveSessionId?: string;\n\tclientOwned?: boolean;\n\tnoSession?: boolean;\n\tsupportsExtensionUi?: boolean;\n}): Promise<{ connection: DaemonAgentConnection; summary: SessionSummary }> {\n\t// Caller must have awaited ensureInteractiveDaemonRunning for this socket.\n\tconst client = new DaemonClient(options.socketPath);\n\tawait client.connect();\n\n\ttry {\n\t\tconst attach = async (summary: SessionSummary) => {\n\t\t\tconst connection = await DaemonAgentConnection.attach(client, getDaemonSummaryActiveSessionId(summary), {\n\t\t\t\tcloseClientOnDispose: true,\n\t\t\t\tsendClientEnv: true,\n\t\t\t\townedSession: options.clientOwned,\n\t\t\t\townedSessionRecoveryConfig: options.clientOwned ? options.config : undefined,\n\t\t\t\tsupportsExtensionUi: options.supportsExtensionUi,\n\t\t\t\trecoverDaemon: () => ensureInteractiveDaemonRunning(options.socketPath),\n\t\t\t});\n\t\t\treturn { connection, summary };\n\t\t};\n\n\t\tif (options.activeSessionId) {\n\t\t\tconst summary = await findAttachedDaemonSessionSummary(client, options.activeSessionId);\n\t\t\treturn await attach(summary);\n\t\t}\n\n\t\tif (options.sessionPath && !options.clientOwned) {\n\t\t\tconst activeSummary = findActiveDaemonSessionSummaryForSessionFile(\n\t\t\t\tawait listActiveDaemonSessionSummaries(client),\n\t\t\t\toptions.sessionPath,\n\t\t\t);\n\t\t\tif (activeSummary && activeSummary.workerState !== \"failed\") {\n\t\t\t\treturn await attach(activeSummary);\n\t\t\t}\n\t\t}\n\t\tif (options.clientOwned) {\n\t\t\tawait client.waitForHello();\n\t\t\tif (!client.supportsServerCapability(\"client_owned_sessions\")) {\n\t\t\t\tthrow new DaemonCapabilityUnavailableError(\"create\", \"client_owned_sessions\");\n\t\t\t}\n\t\t}\n\n\t\tconst response = await client.request({\n\t\t\ttype: \"create\",\n\t\t\tconfig: options.config,\n\t\t\tsessionPath: options.sessionPath,\n\t\t\tcontinueRecent: options.continueRecent,\n\t\t\tnoSession: options.noSession,\n\t\t\tenv: collectDaemonClientEnv(),\n\t\t\tlifecycle: options.clientOwned ? \"client_owned\" : \"resident\",\n\t\t\tlaunchEnv: collectDaemonLaunchEnv(),\n\t\t});\n\t\tif (!response.success) {\n\t\t\tthrow deserializeDaemonError(response);\n\t\t}\n\t\tif (!isDaemonSessionSummary(response.data)) {\n\t\t\tthrow new Error(\"Daemon returned an invalid create response\");\n\t\t}\n\t\tconst summary = response.data;\n\t\treturn await attach(summary);\n\t} catch (error) {\n\t\tclient.close();\n\t\tthrow error;\n\t}\n}\n\nasync function findAttachedDaemonSessionSummary(\n\tclient: DaemonClient,\n\tactiveSessionId: string,\n): Promise<SessionSummary> {\n\tconst response = await client.request({ type: \"get_state\", activeSessionId });\n\tif (!response.success) {\n\t\tthrow new Error(response.error);\n\t}\n\tif (!isDaemonSessionSummary(response.data)) {\n\t\tthrow new Error(\"Daemon returned an invalid active session summary\");\n\t}\n\treturn response.data;\n}\n\nexport interface MainOptions {\n\textensionFactories?: ExtensionFactory[];\n}\n\nexport async function main(args: string[], options?: MainOptions) {\n\tresetTimings();\n\tif (isDaemonWorkerProcess()) {\n\t\twaitForDaemonWorkerStartupGate();\n\t}\n\tinstallFileLogSink();\n\tif (isDaemonCatalogProcess()) {\n\t\tawait runDaemonCatalogProcess();\n\t\treturn;\n\t}\n\t// Client and daemon are separate processes; both need these in their registry.\n\tregisterBuiltinMcpOAuthProviders();\n\tconst offlineMode = args.includes(\"--offline\") || isTruthyEnvFlag(process.env.PI_OFFLINE);\n\tif (offlineMode) {\n\t\tprocess.env.PI_OFFLINE = \"1\";\n\t\tprocess.env.PI_SKIP_VERSION_CHECK = \"1\";\n\t}\n\n\tconst publicCommand = await handlePublicCommand(args);\n\tif (publicCommand.handled) {\n\t\treturn;\n\t}\n\targs = publicCommand.args;\n\n\tif (await handleConfigCommand(args)) {\n\t\treturn;\n\t}\n\n\tconst explicitAgentsView = publicCommand.explicitAgentsView;\n\n\tconst parsed = parseArgs(args);\n\tif (parsed.diagnostics.length > 0) {\n\t\tfor (const d of parsed.diagnostics) {\n\t\t\tconst color = d.type === \"error\" ? chalk.red : chalk.yellow;\n\t\t\tconsole.error(color(`${d.type === \"error\" ? \"Error\" : \"Warning\"}: ${d.message}`));\n\t\t}\n\t\tif (parsed.diagnostics.some((d) => d.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"parseArgs\");\n\tconst appMode = resolveAppMode(parsed, process.stdin.isTTY);\n\n\tif (shouldRejectNonInteractiveAttach(publicCommand.attachAgent, appMode)) {\n\t\tconsole.error(chalk.red(\"Error: attach requires an interactive terminal\"));\n\t\tprocess.exit(1);\n\t}\n\tif (shouldRejectNonInteractiveBareResume(parsed.resume, appMode)) {\n\t\tconsole.error(chalk.red(\"Error: --resume without a session selector requires an interactive terminal\"));\n\t\tprocess.exit(1);\n\t}\n\tsetLogContext({ mode: appMode });\n\tconst shouldTakeOverStdout = appMode !== \"interactive\";\n\tif (shouldTakeOverStdout) {\n\t\ttakeOverStdout();\n\t}\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\tprocess.exit(0);\n\t}\n\tif (parsed.help) {\n\t\tconsole.log(formatTopLevelHelp());\n\t\tprocess.exit(0);\n\t}\n\n\tif (parsed.export) {\n\t\tlet result: string;\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tresult = await exportFromFile(parsed.export, outputPath);\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tconsole.log(`Exported to: ${result}`);\n\t\tprocess.exit(0);\n\t}\n\n\tif ((parsed.mode === \"rpc\" || parsed.mode === \"daemon\") && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC or daemon mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\tvalidateForkFlags(parsed);\n\n\tconst cwd = parsed.cwd ? resolve(expandTildePath(parsed.cwd)) : process.cwd();\n\tif (parsed.cwd) {\n\t\ttry {\n\t\t\tprocess.chdir(cwd);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(chalk.red(`Error: Cannot use cwd ${cwd}: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (parsed.daemonSocket) {\n\t\t// After --cwd so a relative socket path resolves against the requested directory.\n\t\tparsed.daemonSocket = normalizeSocketPath(parsed.daemonSocket);\n\t}\n\n\t// Run migrations (pass cwd for project-local migrations)\n\tconst { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(cwd);\n\ttime(\"runMigrations\");\n\n\tconst agentDir = getAgentDir();\n\tconst startupSettingsManager = SettingsManager.create(cwd, agentDir);\n\treportDiagnostics(collectSettingsDiagnostics(startupSettingsManager, \"startup session lookup\"));\n\tconst startupBenchmark = isTruthyEnvFlag(process.env.PI_STARTUP_BENCHMARK);\n\tif (startupBenchmark && appMode !== \"interactive\") {\n\t\tconsole.error(chalk.red(\"Error: PI_STARTUP_BENCHMARK only supports interactive mode\"));\n\t\tprocess.exit(1);\n\t}\n\t// Programmatic factories are process-local functions and cannot be serialized to a daemon worker.\n\tconst hasProcessLocalExtensionFactories = (options?.extensionFactories?.length ?? 0) > 0;\n\tconst useDaemonClient = shouldUseDaemonClientRuntime({\n\t\tappMode,\n\t\tstartupBenchmark,\n\t\tnoSession: parsed.noSession,\n\t\tlistModels: parsed.listModels,\n\t\townedSessionWorker: isOwnedSessionWorkerProcess(),\n\t\thasProcessLocalExtensionFactories,\n\t});\n\tconst useDaemonInteractive = useDaemonClient && appMode === \"interactive\";\n\n\t// Decide the final runtime cwd before creating cwd-bound runtime services.\n\t// --resume may select a session from another project, so project-local\n\t// settings, resources, provider registrations, and models must be resolved only after\n\t// the target session cwd is known. The startup-cwd settings manager is used only for\n\t// sessionDir lookup during session selection.\n\tconst sessionDir =\n\t\t(parsed.sessionDir ? expandTildePath(parsed.sessionDir) : undefined) ??\n\t\tgetSessionDirEnvOverride() ??\n\t\tstartupSettingsManager.getSessionDir();\n\tconst daemonSocketPath = parsed.daemonSocket ?? defaultDaemonSocketPath();\n\t// Kick off daemon spawn/readiness immediately so it overlaps session-manager\n\t// and runtime-services preparation; attach only connects to an existing daemon.\n\tlet daemonReady = shouldEnsureInteractiveDaemonForStartup(useDaemonClient, publicCommand.attachAgent)\n\t\t? ensureInteractiveDaemonRunning(daemonSocketPath)\n\t\t: undefined;\n\t// Errors are rethrown at the await sites below; this only avoids an unhandled\n\t// rejection if startup exits before reaching them.\n\tdaemonReady?.catch(() => {});\n\tconst resumeSelector = getResumeSelector(parsed);\n\tconst shouldLookupDaemonActiveSession = shouldEnsureDaemonBeforeActiveSessionLookup({\n\t\tuseDaemonInteractive,\n\t\tresumeSelector,\n\t\texplicitAttach: publicCommand.attachAgent !== undefined,\n\t});\n\tif (shouldLookupDaemonActiveSession && daemonReady) {\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t}\n\tlet activeDaemonSessionSummary: SessionSummary | undefined;\n\tif (shouldLookupDaemonActiveSession && resumeSelector) {\n\t\ttry {\n\t\t\tactiveDaemonSessionSummary = await findActiveDaemonSessionSummaryForInteractiveStartup(\n\t\t\t\tdaemonSocketPath,\n\t\t\t\tresumeSelector,\n\t\t\t\t{ fallbackOnError: !publicCommand.attachAgent },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(chalk.red(`Error: Could not look up active agent '${resumeSelector}': ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tif (publicCommand.attachAgent && !activeDaemonSessionSummary) {\n\t\tconsole.error(chalk.red(`Error: No active agent found matching '${publicCommand.attachAgent}'`));\n\t\tprocess.exit(1);\n\t}\n\tlet sessionManager: SessionManager;\n\tif (activeDaemonSessionSummary) {\n\t\tsessionManager = createSessionManagerForActiveDaemonSummary(activeDaemonSessionSummary, cwd);\n\t} else if (\n\t\tuseDaemonInteractive &&\n\t\tshouldUseEphemeralSessionManagerForDaemonInteractive({\n\t\t\tresume: parsed.resume,\n\t\t\tcontinue: parsed.continue,\n\t\t\tfork: parsed.fork,\n\t\t})\n\t) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else {\n\t\ttry {\n\t\t\tsessionManager = await createSessionManager(parsed, cwd, sessionDir);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof SessionSelectorError)) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst suggestion =\n\t\t\t\terror instanceof SessionSelectorNotFoundError && error.suggestion\n\t\t\t\t\t? ` Did you mean '${error.suggestion}'?`\n\t\t\t\t\t: \"\";\n\t\t\tconsole.error(chalk.red(`Error: ${error.message}.${suggestion}`));\n\t\t\tconsole.error(chalk.dim(`Open ${APP_NAME} and press left-arrow to browse sessions.`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\tconst missingSessionCwdIssue = getMissingSessionCwdIssue(sessionManager, cwd);\n\tif (missingSessionCwdIssue) {\n\t\tif (appMode === \"interactive\") {\n\t\t\tconst selectedCwd = await promptForMissingSessionCwd(missingSessionCwdIssue, startupSettingsManager);\n\t\t\tif (!selectedCwd) {\n\t\t\t\tprocess.exit(0);\n\t\t\t}\n\t\t\tsessionManager = SessionManager.open(missingSessionCwdIssue.sessionFile!, sessionDir, selectedCwd);\n\t\t} else {\n\t\t\tconsole.error(chalk.red(new MissingSessionCwdError(missingSessionCwdIssue).message));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\ttime(\"createSessionManager\");\n\n\tconst defaultSessionConfig = runtimeConfigFromArgs(parsed, sessionManager.getCwd(), agentDir, sessionDir, appMode);\n\t// Verifier/headless clients pass initialGoal in each create request. The long-lived\n\t// daemon fallback must not seed that goal into unrelated future sessions.\n\tconst daemonDefaultSessionConfig = daemonServerDefaultSessionConfig(defaultSessionConfig);\n\tconst runtimeDefaultSessionConfig = appMode === \"daemon\" ? daemonDefaultSessionConfig : defaultSessionConfig;\n\tconst createRuntime: CreateAgentSessionRuntimeFactory = async ({\n\t\tcwd,\n\t\tagentDir,\n\t\tsessionManager,\n\t\tsessionStartEvent,\n\t\tsessionConfig,\n\t\tsessionOptions: runtimeSessionOptions,\n\t}) => {\n\t\tconst config = mergeAgentSessionRuntimeConfig(runtimeDefaultSessionConfig, sessionConfig);\n\t\tconst prepared = await prepareRuntimeServices({\n\t\t\tconfig,\n\t\t\tcwd,\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\textensionFactories: options?.extensionFactories,\n\t\t\tsessionOptionsOverride: runtimeSessionOptions,\n\t\t});\n\t\tconst { services, sessionOptions, diagnostics } = prepared;\n\t\tconst resolvedSessionOptions = resolveRuntimeSessionOptions(sessionOptions, runtimeSessionOptions);\n\n\t\tconst created = await createAgentSessionFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t\tsessionStartEvent,\n\t\t\t...resolvedSessionOptions,\n\t\t\t// Main agents boot their kernel in the background at session creation;\n\t\t\t// subagent sessions (rlmDepth > 0) keep the lazy first-call start.\n\t\t\tprewarmIpythonKernel: true,\n\t\t\t// Read serializedRefine from the merged runtime config (passed\n\t\t\t// from the JSON/print client through AgentSessionRuntimeConfig)\n\t\t\t// so it survives the daemon worker's appMode=\"daemon\" context.\n\t\t\tserializedRefine: config.serializedRefine ?? false,\n\t\t\texecutionMode: config.executionMode,\n\t\t\t// Only seed initial goal for top-level sessions (rlmDepth 0).\n\t\t\tinitialGoal: (runtimeSessionOptions?.rlmDepth ?? 0) === 0 ? config.initialGoal : undefined,\n\t\t});\n\t\tconst cliThinkingOverride = config.thinking !== undefined || prepared.cliThinkingFromModel;\n\t\tif (created.session.model && cliThinkingOverride) {\n\t\t\tcreated.session.setThinkingLevel(created.session.thinkingLevel);\n\t\t}\n\n\t\treturn {\n\t\t\t...created,\n\t\t\tservices,\n\t\t\tdiagnostics,\n\t\t};\n\t};\n\ttime(\"createRuntime\");\n\t// Daemon mode never uses the bootstrap runtime, so skip the heavy\n\t// createAgentSessionRuntime below and start listening immediately; sessions\n\t// are created on demand through the daemon protocol via createRuntime.\n\t// --list-models still takes the full path to print and exit.\n\tif (appMode === \"daemon\" && parsed.listModels === undefined) {\n\t\tprintTimings();\n\t\tif (isDaemonWorkerProcess()) {\n\t\t\tawait runDaemonMode({\n\t\t\t\tsocketPath: parsed.daemonSocket,\n\t\t\t\tdefaultSessionConfig: daemonDefaultSessionConfig,\n\t\t\t\tcreateRuntime,\n\t\t\t\tworker: {\n\t\t\t\t\tauthenticationToken: requireDaemonWorkerAuthenticationToken(),\n\t\t\t\t\trestoreActiveSessionId: process.env[DAEMON_WORKER_ACTIVE_SESSION_ID_ENV],\n\t\t\t\t},\n\t\t\t});\n\t\t} else {\n\t\t\tawait runDaemonSupervisorMode({\n\t\t\t\tsocketPath: parsed.daemonSocket,\n\t\t\t\tdefaultSessionConfig: daemonDefaultSessionConfig,\n\t\t\t});\n\t\t}\n\t\treturn;\n\t}\n\tif (useDaemonInteractive) {\n\t\tconst prepared = await prepareRuntimeServices({\n\t\t\tconfig: defaultSessionConfig,\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\textensionFactories: options?.extensionFactories,\n\t\t});\n\t\tconst { services, scopedModels } = prepared;\n\t\tconst { settingsManager } = services;\n\n\t\tconst startupModel = await resolvePreparedStartupModel({ prepared, sessionManager });\n\n\t\tconst stdinContent = await readPipedStdin();\n\t\ttime(\"readPipedStdin\");\n\n\t\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\t\tparsed,\n\t\t\tsettingsManager.getImageAutoResize(),\n\t\t\tstdinContent,\n\t\t);\n\t\ttime(\"prepareInitialMessage\");\n\t\tinitTheme(settingsManager.getTheme(), true);\n\t\ttime(\"initTheme\");\n\n\t\tif (deprecationWarnings.length > 0) {\n\t\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t\t}\n\n\t\treportDiagnostics(prepared.diagnostics);\n\t\tif (prepared.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\t\tprocess.exit(1);\n\t\t}\n\t\ttime(\"prepareInteractiveServices\");\n\n\t\tif (scopedModels.length > 0 && (parsed.verbose || !settingsManager.getQuietStartup())) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst promptStashStore = new ClientPromptStashStore();\n\t\tconst daemonUiServices = createInteractiveModeUiServicesFromServices({\n\t\t\tservices,\n\t\t\tsessionManager,\n\t\t});\n\t\tconst launchAgentsView = async (initialSession?: SessionSummary, initialScopeKey?: AgentsViewScopeKey) => {\n\t\t\tawait runAgentsViewMode({\n\t\t\t\tsocketPath: daemonSocketPath,\n\t\t\t\tconfig: defaultSessionConfig,\n\t\t\t\tuiServices: daemonUiServices,\n\t\t\t\trecoverDaemon: () => ensureInteractiveDaemonRunning(daemonSocketPath),\n\t\t\t\tcreateUiServicesForSession: async (summary) => {\n\t\t\t\t\tconst attachedSessionManager = createSessionManagerForActiveDaemonSummary(\n\t\t\t\t\t\tsummary,\n\t\t\t\t\t\tsessionManager.getCwd(),\n\t\t\t\t\t);\n\t\t\t\t\tconst attachedPrepared = await prepareRuntimeServices({\n\t\t\t\t\t\tconfig: mergeAgentSessionRuntimeConfig(defaultSessionConfig, {\n\t\t\t\t\t\t\tcwd: attachedSessionManager.getCwd(),\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tcwd: attachedSessionManager.getCwd(),\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t\tsessionManager: attachedSessionManager,\n\t\t\t\t\t\textensionFactories: options?.extensionFactories,\n\t\t\t\t\t});\n\t\t\t\t\treturn createInteractiveModeUiServicesFromServices({\n\t\t\t\t\t\tservices: attachedPrepared.services,\n\t\t\t\t\t\tsessionManager: attachedSessionManager,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\tmigratedProviders,\n\t\t\t\tmodelFallbackMessage: startupModel.modelFallbackMessage,\n\t\t\t\tpromptStashStore,\n\t\t\t\tstartupModelId: startupModel.model?.id,\n\t\t\t\tinitialSession,\n\t\t\t\tinitialScopeKey,\n\t\t\t\tverbose: parsed.verbose,\n\t\t\t});\n\t\t};\n\t\tif (\n\t\t\tshouldOpenAgentsViewForDaemonInteractive({\n\t\t\t\tuseDaemonInteractive: useDaemonInteractive && !parsed.noSession,\n\t\t\t\texplicitAgentsView,\n\t\t\t\tneedsOnboarding: shouldRunOnboarding({\n\t\t\t\t\tsettingsManager,\n\t\t\t\t\tmodelRegistry: services.modelRegistry,\n\t\t\t\t\tmodel: startupModel.model,\n\t\t\t\t}),\n\t\t\t\tresume: parsed.resume,\n\t\t\t\tcontinue: parsed.continue,\n\t\t\t\tfork: parsed.fork,\n\t\t\t})\n\t\t) {\n\t\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\t\tawait preloadCodeHighlighter();\n\t\t\tprintTimings();\n\t\t\tawait launchAgentsView();\n\t\t\treturn;\n\t\t}\n\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\t// A fresh default chat opens a real but message-less session; the lifecycle\n\t\t// axis treats it as a draft (hidden, discarded on detach if never used), so\n\t\t// no DeferredAgentConnection is needed to avoid creating it up front.\n\t\tconst isFreshDefaultSession =\n\t\t\t!activeDaemonSessionSummary && !getInteractiveDaemonSessionPath(parsed, sessionManager);\n\t\tconst { connection, summary } = await createDaemonClientConnection({\n\t\t\tsocketPath: daemonSocketPath,\n\t\t\tconfig: defaultSessionConfig,\n\t\t\tactiveSessionId: activeDaemonSessionSummary\n\t\t\t\t? getDaemonSummaryActiveSessionId(activeDaemonSessionSummary)\n\t\t\t\t: undefined,\n\t\t\tsessionPath: getInteractiveDaemonSessionPath(parsed, sessionManager),\n\t\t\tclientOwned: parsed.noSession,\n\t\t\tnoSession: parsed.noSession,\n\t\t\tsupportsExtensionUi: true,\n\t\t});\n\t\tconst agentConnection: AgentConnection = connection;\n\t\tconst attachModelFallbackMessage = isFreshDefaultSession\n\t\t\t? startupModel.modelFallbackMessage\n\t\t\t: resolveAttachModelFallbackMessage(summary, startupModel.modelFallbackMessage);\n\n\t\tconst interactiveMode = new InteractiveMode({\n\t\t\tagentConnection,\n\t\t\tdaemonSocketPath,\n\t\t\tuiServices: daemonUiServices,\n\t\t\tpromptStashStore,\n\t\t\tpromptStashSessionId: summary.sessionId,\n\t\t\tbindLocalSessionExtensions: false,\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage: attachModelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t\t// Resumed/attached daemon sessions are part of the same fleet; left\n\t\t\t// arrow takes them to the agents view like any other session. The agents\n\t\t\t// view was not rendered here, so we intentionally leave\n\t\t\t// agentsViewOwnsStartupNotices unset and let the in-session fallback run.\n\t\t\treturnToAgentsView: !parsed.noSession,\n\t\t\tsessionDepth: summary.rlmDepth,\n\t\t\t// Direct launch has only the attached summary; the live+passive+saved\n\t\t\t// unified catalog index is not built until the agents view opens. Retained\n\t\t\t// child snapshots augment this running-child fallback inside chat.\n\t\t\tsessionHasChildren: summary.hasRunningRlmChildren === true,\n\t\t});\n\n\t\tawait preloadCodeHighlighter();\n\t\tprintTimings();\n\t\tconst interactiveResult = await interactiveMode.run();\n\t\tif (parsed.noSession) {\n\t\t\treturn;\n\t\t}\n\t\tconst returnedSummary = {\n\t\t\t...summary,\n\t\t\t...interactiveResult.source,\n\t\t\tid: interactiveResult.source.activeSessionId ?? summary.id,\n\t\t};\n\t\tconst initialScopeKey =\n\t\t\tinteractiveResult.type === \"scoped_agents_view\"\n\t\t\t\t? {\n\t\t\t\t\t\tsessionId: interactiveResult.source.sessionId,\n\t\t\t\t\t\tactiveSessionId: interactiveResult.source.activeSessionId,\n\t\t\t\t\t}\n\t\t\t\t: undefined;\n\t\tawait launchAgentsView(returnedSummary, initialScopeKey);\n\t\treturn;\n\t}\n\tif (useDaemonClient) {\n\t\tconst settingsManager = SettingsManager.create(sessionManager.getCwd(), agentDir);\n\t\tlet stdinContent: string | undefined;\n\t\tif (appMode !== \"rpc\" && appMode !== \"acp\") {\n\t\t\tstdinContent = await readPipedStdin();\n\t\t}\n\t\ttime(\"readPipedStdin\");\n\t\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\t\tparsed,\n\t\t\tsettingsManager.getImageAutoResize(),\n\t\t\tstdinContent,\n\t\t);\n\t\ttime(\"prepareInitialMessage\");\n\t\tinitTheme(settingsManager.getTheme(), false);\n\t\ttime(\"initTheme\");\n\n\t\tdaemonReady = (await awaitDaemonReady(daemonReady)).ready;\n\t\tlet connection: DaemonAgentConnection;\n\t\tlet summary: SessionSummary;\n\t\ttry {\n\t\t\t({ connection, summary } = await createDaemonClientConnection({\n\t\t\t\tsocketPath: daemonSocketPath,\n\t\t\t\tconfig: defaultSessionConfig,\n\t\t\t\tsessionPath: parsed.noSession ? undefined : sessionManager.getSessionFile(),\n\t\t\t\tcontinueRecent: parsed.continue,\n\t\t\t\tclientOwned: isClientOwnedDaemonSession(appMode, parsed.noSession),\n\t\t\t\tnoSession: parsed.noSession,\n\t\t\t\tsupportsExtensionUi: appMode === \"rpc\",\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tif (error instanceof SessionAlreadyActiveError) {\n\t\t\t\tconsole.error(chalk.red(`Error: ${error.message}`));\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tconst diagnostics = summary.diagnostics ?? [];\n\t\treportDiagnostics(diagnostics);\n\t\tif (diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\t\tawait connection.dispose();\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!summary.model) {\n\t\t\tconsole.error(chalk.red(summary.modelFallbackMessage ?? formatNoModelsAvailableMessage()));\n\t\t\tawait connection.dispose();\n\t\t\tprocess.exit(1);\n\t\t}\n\n\t\tprintTimings();\n\t\tif (appMode === \"rpc\") {\n\t\t\treturn await runRpcModeWithConnection(connection);\n\t\t}\n\t\tif (appMode === \"acp\") {\n\t\t\treturn await runAcpModeWithConnection(connection);\n\t\t}\n\t\tconst exitCode = await runPrintModeWithConnection(connection, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n\n\tlet runtime: AgentSessionRuntime;\n\ttry {\n\t\truntime = await createAgentSessionRuntime(createRuntime, {\n\t\t\tcwd: sessionManager.getCwd(),\n\t\t\tagentDir,\n\t\t\tsessionManager,\n\t\t\tsessionConfig: defaultSessionConfig,\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof SessionAlreadyActiveError) {\n\t\t\tconsole.error(chalk.red(`Error: ${error.message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tthrow error;\n\t}\n\tconst { services, session, modelFallbackMessage } = runtime;\n\tinstallOwnedSessionRecoveryTracking(runtime);\n\tconst { settingsManager, modelRegistry } = services;\n\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(modelRegistry, searchPattern);\n\t\tprocess.exit(0);\n\t}\n\n\t// Read piped stdin content (if any) - skip for RPC/daemon modes which use other transports\n\tlet stdinContent: string | undefined;\n\tif (appMode !== \"rpc\" && appMode !== \"acp\" && appMode !== \"daemon\") {\n\t\tstdinContent = await readPipedStdin();\n\t}\n\ttime(\"readPipedStdin\");\n\n\tconst { initialMessage, initialImages } = await prepareInitialMessage(\n\t\tparsed,\n\t\tsettingsManager.getImageAutoResize(),\n\t\tstdinContent,\n\t);\n\ttime(\"prepareInitialMessage\");\n\tinitTheme(settingsManager.getTheme(), appMode === \"interactive\");\n\ttime(\"initTheme\");\n\n\t// Show deprecation warnings in interactive mode\n\tif (appMode === \"interactive\" && deprecationWarnings.length > 0) {\n\t\tawait showDeprecationWarnings(deprecationWarnings);\n\t}\n\n\tconst scopedModels = [...session.scopedModels];\n\ttime(\"resolveModelScope\");\n\treportDiagnostics(runtime.diagnostics);\n\tif (runtime.diagnostics.some((diagnostic) => diagnostic.type === \"error\")) {\n\t\tprocess.exit(1);\n\t}\n\ttime(\"createAgentSession\");\n\n\tif (appMode !== \"interactive\" && appMode !== \"daemon\" && !session.model) {\n\t\tconsole.error(chalk.red(formatNoModelsAvailableMessage()));\n\t\tprocess.exit(1);\n\t}\n\n\tif (appMode === \"rpc\") {\n\t\tprintTimings();\n\t\tawait runRpcMode(runtime);\n\t} else if (appMode === \"acp\") {\n\t\tprintTimings();\n\t\tawait runAcpMode(runtime);\n\t} else if (appMode === \"interactive\") {\n\t\tif (explicitAgentsView || parsed.resume === true) {\n\t\t\tconsole.error(chalk.yellow(\"Warning: the agents view needs the daemon; opening a normal chat instead\"));\n\t\t}\n\t\tif (scopedModels.length > 0 && (parsed.verbose || !settingsManager.getQuietStartup())) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\tconst interactiveMode = new InteractiveMode({\n\t\t\tagentConnection: new InProcessAgentConnection(runtime),\n\t\t\tlocalSessionHost: createInteractiveModeLocalSessionHost(runtime),\n\t\t\tpromptStashStore: new ClientPromptStashStore(),\n\t\t\tpromptStashSessionId: session.sessionId,\n\t\t\tbindLocalSessionExtensions: true,\n\t\t\tmigratedProviders,\n\t\t\tmodelFallbackMessage,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tverbose: parsed.verbose,\n\t\t});\n\t\tif (startupBenchmark) {\n\t\t\tawait interactiveMode.init();\n\t\t\ttime(\"interactiveMode.init\");\n\t\t\tprintTimings();\n\t\t\tinteractiveMode.stop();\n\t\t\tstopThemeWatcher();\n\t\t\tif (process.stdout.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t\t}\n\t\t\tif (process.stderr.writableLength > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => process.stderr.once(\"drain\", resolve));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tawait preloadCodeHighlighter();\n\t\tprintTimings();\n\t\tawait interactiveMode.run();\n\t} else {\n\t\tprintTimings();\n\t\tconst exitCode = await runPrintMode(runtime, {\n\t\t\tmode: toPrintOutputMode(appMode),\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tinitialImages,\n\t\t});\n\t\tstopThemeWatcher();\n\t\trestoreStdout();\n\t\tif (exitCode !== 0) {\n\t\t\tprocess.exitCode = exitCode;\n\t\t}\n\t\treturn;\n\t}\n}\n"]}
package/dist/main.js CHANGED
@@ -35,7 +35,6 @@ import { formatMissingSessionCwdPrompt, getMissingSessionCwdIssue, MissingSessio
35
35
  import { canonicalSessionPath, SessionAlreadyActiveError } from "./core/session-lease.js";
36
36
  import { SessionManager } from "./core/session-manager.js";
37
37
  import { SettingsManager } from "./core/settings-manager.js";
38
- import { isTelemetryEnabled } from "./core/telemetry.js";
39
38
  import { printTimings, resetTimings, time } from "./core/timings.js";
40
39
  import { runMigrations, showDeprecationWarnings } from "./migrations.js";
41
40
  import { isDaemonCatalogProcess, runDaemonCatalogProcess } from "./modes/daemon/daemon-catalog-process.js";
@@ -430,7 +429,7 @@ function runtimeAutonomousConfigFromArgs(parsed) {
430
429
  : undefined,
431
430
  };
432
431
  }
433
- function runtimeConfigFromArgs(parsed, cwd, agentDir, sessionDir, appMode, telemetryDisabled) {
432
+ function runtimeConfigFromArgs(parsed, cwd, agentDir, sessionDir, appMode) {
434
433
  return {
435
434
  cwd,
436
435
  agentDir,
@@ -457,7 +456,6 @@ function runtimeConfigFromArgs(parsed, cwd, agentDir, sessionDir, appMode, telem
457
456
  autonomous: runtimeAutonomousConfigFromArgs(parsed),
458
457
  extensionFlagValues: parsed.unknownFlags.size > 0 ? Object.fromEntries(parsed.unknownFlags.entries()) : undefined,
459
458
  executionMode: appMode === "daemon" ? undefined : appMode,
460
- telemetryDisabled,
461
459
  // Serialized refine for print/json/rpc: the client's appMode is NOT
462
460
  // "daemon" here — it's "print", "json", or "rpc". The daemon worker
463
461
  // receives this flag via AgentSessionRuntimeConfig and uses it
@@ -510,7 +508,6 @@ async function prepareRuntimeServices(options) {
510
508
  // Subagents share the parent's Herdr pane; their own reporter would race
511
509
  // the parent's and a subagent quit would release the still-active pane.
512
510
  noBuiltinHerdrReporter: (options.sessionOptionsOverride?.rlmDepth ?? 0) > 0,
513
- telemetryDisabled: config.telemetryDisabled,
514
511
  resourceLoaderOptions: {
515
512
  additionalExtensionPaths: config.extensions,
516
513
  additionalSkillPaths: config.skills,
@@ -678,7 +675,6 @@ async function createDaemonClientConnection(options) {
678
675
  ownedSessionRecoveryConfig: options.clientOwned ? options.config : undefined,
679
676
  supportsExtensionUi: options.supportsExtensionUi,
680
677
  recoverDaemon: () => ensureInteractiveDaemonRunning(options.socketPath),
681
- telemetryDisabled: options.config.telemetryDisabled,
682
678
  });
683
679
  return { connection, summary };
684
680
  };
@@ -931,11 +927,7 @@ export async function main(args, options) {
931
927
  }
932
928
  }
933
929
  time("createSessionManager");
934
- const telemetrySettingsManager = sessionManager.getCwd() === cwd
935
- ? startupSettingsManager
936
- : SettingsManager.create(sessionManager.getCwd(), agentDir);
937
- const telemetryDisabled = isTelemetryEnabled(telemetrySettingsManager) ? undefined : true;
938
- const defaultSessionConfig = runtimeConfigFromArgs(parsed, sessionManager.getCwd(), agentDir, sessionDir, appMode, telemetryDisabled);
930
+ const defaultSessionConfig = runtimeConfigFromArgs(parsed, sessionManager.getCwd(), agentDir, sessionDir, appMode);
939
931
  // Verifier/headless clients pass initialGoal in each create request. The long-lived
940
932
  // daemon fallback must not seed that goal into unrelated future sessions.
941
933
  const daemonDefaultSessionConfig = daemonServerDefaultSessionConfig(defaultSessionConfig);
@@ -965,7 +957,6 @@ export async function main(args, options) {
965
957
  // so it survives the daemon worker's appMode="daemon" context.
966
958
  serializedRefine: config.serializedRefine ?? false,
967
959
  executionMode: config.executionMode,
968
- telemetryDisabled: config.telemetryDisabled,
969
960
  // Only seed initial goal for top-level sessions (rlmDepth 0).
970
961
  initialGoal: (runtimeSessionOptions?.rlmDepth ?? 0) === 0 ? config.initialGoal : undefined,
971
962
  });