@asm-agent/coding-agent 0.8.8 → 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 (178) hide show
  1. package/CHANGELOG.md +15 -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__/bash.cpython-312.pyc +0 -0
  9. package/dist/asm-agent-runtime/src/rlm/__pycache__/bash.cpython-313.pyc +0 -0
  10. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp.cpython-312.pyc +0 -0
  11. package/dist/asm-agent-runtime/src/rlm/__pycache__/mcp_base.cpython-312.pyc +0 -0
  12. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-312.pyc +0 -0
  13. package/dist/asm-agent-runtime/src/rlm/__pycache__/repl.cpython-313.pyc +0 -0
  14. package/dist/asm-agent-runtime/src/rlm/__pycache__/skill.cpython-313.pyc +0 -0
  15. package/dist/asm-agent-runtime/src/rlm/bash.py +94 -31
  16. package/dist/asm-agent-runtime/src/rlm/repl.md +3 -3
  17. package/dist/asm-agent-runtime/src/rlm/repl.py +46 -45
  18. package/dist/asm-agent-runtime/test/__pycache__/test_bash.cpython-313.pyc +0 -0
  19. package/dist/asm-agent-runtime/test/__pycache__/test_repl.cpython-313.pyc +0 -0
  20. package/dist/asm-agent-runtime/test/test_bash.py +74 -19
  21. package/dist/asm-agent-runtime/test/test_repl.py +67 -3
  22. package/dist/asm-agent-runtime/uv.lock +446 -155
  23. package/dist/bundle/{anthropic-OFECORE7.js → anthropic-ZOZNPFXS.js} +2 -2
  24. package/dist/bundle/{azure-openai-responses-AOVTLR6N.js → azure-openai-responses-TDHCU647.js} +2 -2
  25. package/dist/bundle/{bundled-modules-JQCJDEIY.js → bundled-modules-Q2YODC4B.js} +3 -3
  26. package/dist/bundle/{chunk-M67RGWDQ.js → chunk-AMKLNEAK.js} +25 -44
  27. package/dist/bundle/{chunk-7TB7CGPV.js → chunk-FPY7FFJS.js} +978 -1821
  28. package/dist/bundle/{chunk-JC2TT4MZ.js → chunk-G6SC4YYW.js} +4942 -1916
  29. package/dist/bundle/{chunk-A65OM3TZ.js → chunk-QLHAP3FT.js} +1 -1
  30. package/dist/bundle/{cli-main-FNHDQGQD.js → cli-main-277AG2BR.js} +3 -3
  31. package/dist/bundle/cli.js +1 -1
  32. package/dist/bundle/{google-RVJL6RIQ.js → google-Q3AJWUOY.js} +1 -1
  33. package/dist/bundle/{google-vertex-EZTRRRH6.js → google-vertex-2CSNOUBS.js} +1 -1
  34. package/dist/bundle/{main-IYXIIAJN.js → main-TNXWJXIU.js} +3 -3
  35. package/dist/bundle/{mistral-FSGKSMA7.js → mistral-XRYJGLLG.js} +1 -1
  36. package/dist/bundle/{openai-codex-responses-VM5Y564D.js → openai-codex-responses-C6BRO2BN.js} +2 -2
  37. package/dist/bundle/{openai-completions-X46FOQDN.js → openai-completions-DNXCK2JW.js} +3 -3
  38. package/dist/bundle/{openai-responses-SOQNEPBE.js → openai-responses-BEMWVIXE.js} +2 -2
  39. package/dist/config.d.ts +0 -1
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +0 -3
  42. package/dist/config.js.map +1 -1
  43. package/dist/core/agent-session-config.d.ts +1 -2
  44. package/dist/core/agent-session-config.d.ts.map +1 -1
  45. package/dist/core/agent-session-config.js +0 -3
  46. package/dist/core/agent-session-config.js.map +1 -1
  47. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  48. package/dist/core/agent-session-runtime.js +0 -8
  49. package/dist/core/agent-session-runtime.js.map +1 -1
  50. package/dist/core/agent-session-services.d.ts +0 -2
  51. package/dist/core/agent-session-services.d.ts.map +1 -1
  52. package/dist/core/agent-session-services.js +0 -22
  53. package/dist/core/agent-session-services.js.map +1 -1
  54. package/dist/core/agent-session.d.ts +8 -1
  55. package/dist/core/agent-session.d.ts.map +1 -1
  56. package/dist/core/agent-session.js +100 -22
  57. package/dist/core/agent-session.js.map +1 -1
  58. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  59. package/dist/core/kernel/bootstrap.js +1 -1
  60. package/dist/core/kernel/bootstrap.js.map +1 -1
  61. package/dist/core/kernel/repl-manager.d.ts +32 -8
  62. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  63. package/dist/core/kernel/repl-manager.js +401 -102
  64. package/dist/core/kernel/repl-manager.js.map +1 -1
  65. package/dist/core/kernel/shared.d.ts +11 -7
  66. package/dist/core/kernel/shared.d.ts.map +1 -1
  67. package/dist/core/kernel/shared.js +2 -5
  68. package/dist/core/kernel/shared.js.map +1 -1
  69. package/dist/core/mcp/mcp-manager.d.ts +4 -2
  70. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  71. package/dist/core/mcp/mcp-manager.js +9 -7
  72. package/dist/core/mcp/mcp-manager.js.map +1 -1
  73. package/dist/core/prime-inference-auth.d.ts +0 -9
  74. package/dist/core/prime-inference-auth.d.ts.map +1 -1
  75. package/dist/core/prime-inference-auth.js +0 -50
  76. package/dist/core/prime-inference-auth.js.map +1 -1
  77. package/dist/core/provider-display-names.d.ts.map +1 -1
  78. package/dist/core/provider-display-names.js +0 -1
  79. package/dist/core/provider-display-names.js.map +1 -1
  80. package/dist/core/session-manager.d.ts +0 -4
  81. package/dist/core/session-manager.d.ts.map +1 -1
  82. package/dist/core/session-manager.js +0 -22
  83. package/dist/core/session-manager.js.map +1 -1
  84. package/dist/core/settings-manager.d.ts +4 -16
  85. package/dist/core/settings-manager.d.ts.map +1 -1
  86. package/dist/core/settings-manager.js +13 -43
  87. package/dist/core/settings-manager.js.map +1 -1
  88. package/dist/core/slash-commands.d.ts.map +1 -1
  89. package/dist/core/slash-commands.js +0 -5
  90. package/dist/core/slash-commands.js.map +1 -1
  91. package/dist/core/tools/acp-mcp.d.ts +6 -0
  92. package/dist/core/tools/acp-mcp.d.ts.map +1 -0
  93. package/dist/core/tools/acp-mcp.js +86 -0
  94. package/dist/core/tools/acp-mcp.js.map +1 -0
  95. package/dist/core/tools/index.d.ts +1 -0
  96. package/dist/core/tools/index.d.ts.map +1 -1
  97. package/dist/core/tools/index.js +1 -0
  98. package/dist/core/tools/index.js.map +1 -1
  99. package/dist/core/tools/ipython.d.ts.map +1 -1
  100. package/dist/core/tools/ipython.js +9 -4
  101. package/dist/core/tools/ipython.js.map +1 -1
  102. package/dist/main.d.ts.map +1 -1
  103. package/dist/main.js +2 -11
  104. package/dist/main.js.map +1 -1
  105. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  106. package/dist/modes/acp/acp-mode.js +1 -1
  107. package/dist/modes/acp/acp-mode.js.map +1 -1
  108. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +0 -2
  109. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  110. package/dist/modes/agent-connection/daemon-agent-connection.js +0 -2
  111. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  112. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  113. package/dist/modes/agents-view/agents-view-mode.js +0 -19
  114. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  115. package/dist/modes/daemon/daemon-mode.d.ts +1 -10
  116. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  117. package/dist/modes/daemon/daemon-mode.js +134 -274
  118. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  119. package/dist/modes/daemon/daemon-protocol.d.ts +11 -5
  120. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  121. package/dist/modes/daemon/daemon-protocol.js +6 -8
  122. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  123. package/dist/modes/daemon/daemon-supervisor.d.ts +0 -3
  124. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  125. package/dist/modes/daemon/daemon-supervisor.js +18 -52
  126. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  127. package/dist/modes/daemon/daemon-worker-protocol.d.ts +1 -6
  128. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  129. package/dist/modes/daemon/daemon-worker-protocol.js +0 -2
  130. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  131. package/dist/modes/interactive/auth-flows.d.ts +0 -2
  132. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  133. package/dist/modes/interactive/auth-flows.js +1 -101
  134. package/dist/modes/interactive/auth-flows.js.map +1 -1
  135. package/dist/modes/interactive/components/assistant-message.d.ts +6 -1
  136. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  137. package/dist/modes/interactive/components/assistant-message.js +13 -3
  138. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  139. package/dist/modes/interactive/components/footer.d.ts +1 -1
  140. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/footer.js +2 -2
  142. package/dist/modes/interactive/components/footer.js.map +1 -1
  143. package/dist/modes/interactive/components/mermaid.d.ts +12 -0
  144. package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
  145. package/dist/modes/interactive/components/mermaid.js +63 -0
  146. package/dist/modes/interactive/components/mermaid.js.map +1 -0
  147. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  148. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/settings-selector.js +10 -0
  150. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  151. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  152. package/dist/modes/interactive/feature-hints.js +0 -4
  153. package/dist/modes/interactive/feature-hints.js.map +1 -1
  154. package/dist/modes/interactive/interactive-mode.d.ts +1 -7
  155. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  156. package/dist/modes/interactive/interactive-mode.js +23 -279
  157. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  158. package/dist/utils/version-check.d.ts.map +1 -1
  159. package/dist/utils/version-check.js +1 -1
  160. package/dist/utils/version-check.js.map +1 -1
  161. package/docs/settings.md +1 -31
  162. package/docs/usage.md +1 -4
  163. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  164. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  165. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  166. package/examples/extensions/sandbox/package-lock.json +2 -2
  167. package/examples/extensions/sandbox/package.json +1 -1
  168. package/examples/extensions/with-deps/package-lock.json +2 -2
  169. package/examples/extensions/with-deps/package.json +1 -1
  170. package/package.json +6 -5
  171. package/dist/core/agent-traces.d.ts +0 -130
  172. package/dist/core/agent-traces.d.ts.map +0 -1
  173. package/dist/core/agent-traces.js +0 -746
  174. package/dist/core/agent-traces.js.map +0 -1
  175. package/dist/core/telemetry.d.ts +0 -92
  176. package/dist/core/telemetry.d.ts.map +0 -1
  177. package/dist/core/telemetry.js +0 -633
  178. package/dist/core/telemetry.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import type { AgentSessionMessageAgentSummary, AgentSessionMessageDeliveryMode, AgentSessionMessageSender } from "../../core/agent-messages.js";
1
+ import type { AgentSessionMessageDeliveryMode, AgentSessionMessageSender } from "../../core/agent-messages.js";
2
2
  import type { IdleEvictionMinutes } from "../../core/session-action-store.js";
3
3
  export { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from "../../core/session-lease.js";
4
4
  import type { DaemonClientCapability, DaemonCommand, DaemonOutbound } from "./daemon-protocol.js";
@@ -51,10 +51,6 @@ export type DaemonWorkerCommand = {
51
51
  id?: string;
52
52
  type: "worker_unsubscribe";
53
53
  activeSessionId: string;
54
- } | {
55
- id?: string;
56
- type: "worker_sync_agent_peers";
57
- peers: AgentSessionMessageAgentSummary[];
58
54
  } | {
59
55
  id?: string;
60
56
  type: "worker_archive_and_shutdown";
@@ -100,7 +96,6 @@ export interface DaemonWorkerDescriptor {
100
96
  rootSessionId?: string;
101
97
  sessionFile?: string;
102
98
  sessionDir?: string;
103
- telemetryDisabled?: true;
104
99
  createdAt: string;
105
100
  updatedAt: string;
106
101
  lifecycle: DaemonWorkerLifecycle;
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-worker-protocol.d.ts","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-worker-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { closeSync, readFileSync } from \"node:fs\";\nimport type {\n\tAgentSessionMessageAgentSummary,\n\tAgentSessionMessageDeliveryMode,\n\tAgentSessionMessageSender,\n} from \"../../core/agent-messages.js\";\nimport type { IdleEvictionMinutes } from \"../../core/session-action-store.js\";\n\nexport { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from \"../../core/session-lease.js\";\n\nimport type { DaemonClientCapability, DaemonCommand, DaemonOutbound } from \"./daemon-protocol.js\";\n\nexport const DAEMON_WORKER_ROLE_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER\";\nexport const DAEMON_WORKER_TOKEN_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_TOKEN\";\nexport const DAEMON_WORKER_ACTIVE_SESSION_ID_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_ACTIVE_SESSION_ID\";\nexport const DAEMON_WORKER_SUPERVISOR_SOCKET_ENV = \"ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_SOCKET\";\nexport const DAEMON_WORKER_RECOVERY_JOURNAL_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_RECOVERY_JOURNAL\";\nexport const DAEMON_WORKER_STARTUP_GATE_FD_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_STARTUP_GATE_FD\";\nexport const DAEMON_WORKER_STARTUP_GATE_COMMIT = \"start\\n\";\nexport type DaemonWorkerLifecycle = \"starting\" | \"ready\" | \"recovering\" | \"stopping\" | \"failed\";\n\nexport type DaemonWorkerFrameHeader =\n\t| {\n\t\t\tkind: \"command\";\n\t\t\trequestId: string;\n\t\t\tcommandType: string;\n\t }\n\t| {\n\t\t\tkind: \"outbound\";\n\t\t\trequestId?: string;\n\t\t\toutboundType: DaemonOutbound[\"type\"];\n\t\t\tactiveSessionId?: string;\n\t\t\tsnapshotId?: string;\n\t\t\tsessionEventType?: string;\n\t\t\tpayloadEncoding?: \"jsonl\" | \"assistant-delta\";\n\t\t\tsnapshotPurpose?: \"attach\" | \"replacement\" | \"catchup\";\n\t };\n\nexport type DaemonCreateCommand = Extract<DaemonCommand, { type: \"create\" }>;\n\nexport interface DurableDaemonCreateCommand {\n\ttype: \"create\";\n\tsessionPath?: string;\n\tnoSession?: boolean;\n}\n\nexport function durableDaemonCreateCommand(command: DaemonCreateCommand): DurableDaemonCreateCommand {\n\treturn {\n\t\ttype: \"create\",\n\t\t...(command.sessionPath !== undefined ? { sessionPath: command.sessionPath } : {}),\n\t\t...(command.noSession !== undefined ? { noSession: command.noSession } : {}),\n\t};\n}\n\nexport type DaemonWorkerCommand =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_auth\";\n\t\t\ttoken: string;\n\t\t\tsupervisorGeneration: string;\n\t\t\tsupervisorPid: number;\n\t\t\tsupervisorProcessStartId?: string;\n\t\t\tsupervisorSocketPath: string;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_subscribe\";\n\t\t\tactiveSessionId: string;\n\t\t\tcapabilities?: readonly DaemonClientCapability[];\n\t\t\tsupportsExtensionUi?: boolean;\n\t }\n\t| { id?: string; type: \"worker_unsubscribe\"; activeSessionId: string }\n\t| { id?: string; type: \"worker_sync_agent_peers\"; peers: AgentSessionMessageAgentSummary[] }\n\t| { id?: string; type: \"worker_archive_and_shutdown\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_passivate_idle_children\";\n\t\t\tidleEvictionMinutes: IdleEvictionMinutes;\n\t\t\tnow: number;\n\t\t\tlimit: number;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_deliver_message\";\n\t\t\ttargetActiveSessionId: string;\n\t\t\tmessage: string;\n\t\t\tsender: AgentSessionMessageSender;\n\t\t\tdeliveryMode?: AgentSessionMessageDeliveryMode;\n\t }\n\t| { id?: string; type: \"worker_prepare_update\" }\n\t| { id?: string; type: \"worker_commit_update\" }\n\t| { id?: string; type: \"worker_cancel_update\" };\n\nexport type DaemonWorkerCommandBody = DaemonWorkerCommand extends infer TCommand\n\t? TCommand extends { id?: string }\n\t\t? Omit<TCommand, \"id\">\n\t\t: never\n\t: never;\n\nexport interface DaemonWorkerDescriptor {\n\tversion: 1 | 2;\n\tworkerId: string;\n\tpid: number;\n\tprocessStartId?: string;\n\tsocketPath: string;\n\trecoveryJournalPath: string;\n\torphanProcessJournalPath?: string;\n\tsupervisorSocketPath: string;\n\tauthenticationToken: string;\n\trootActiveSessionId: string;\n\t/** Stable protocol client that owns this worker. Omitted for resident sessions. */\n\townerClientId?: string;\n\trootSessionId?: string;\n\tsessionFile?: string;\n\tsessionDir?: string;\n\ttelemetryDisabled?: true;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlifecycle: DaemonWorkerLifecycle;\n\tcreateCommand: DurableDaemonCreateCommand;\n\tconsecutiveFailures: number;\n\t/** Durable intent written before root termination so replacement supervisors never recover it. */\n\tstopRequestedAt?: string;\n\t/** Complete the root's archived lifecycle state after its process has stopped. */\n\tarchiveOnStop?: boolean;\n\tlastFailureAt?: string;\n\tlastError?: string;\n}\n\nexport function durableDaemonWorkerDescriptor(descriptor: DaemonWorkerDescriptor): DaemonWorkerDescriptor {\n\tconst versionOneCreateCommand = descriptor.createCommand as unknown as { config?: unknown };\n\tconst versionOneConfig =\n\t\tdescriptor.version === 1 &&\n\t\ttypeof versionOneCreateCommand.config === \"object\" &&\n\t\tversionOneCreateCommand.config !== null\n\t\t\t? (versionOneCreateCommand.config as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst sessionDir =\n\t\tdescriptor.sessionDir ??\n\t\t(typeof versionOneConfig?.sessionDir === \"string\" ? versionOneConfig.sessionDir : undefined);\n\tconst telemetryDisabled = descriptor.telemetryDisabled === true || versionOneConfig?.telemetryDisabled === true;\n\treturn {\n\t\tversion: 2,\n\t\tworkerId: descriptor.workerId,\n\t\tpid: descriptor.pid,\n\t\t...(descriptor.processStartId !== undefined ? { processStartId: descriptor.processStartId } : {}),\n\t\tsocketPath: descriptor.socketPath,\n\t\trecoveryJournalPath: descriptor.recoveryJournalPath,\n\t\t...(descriptor.orphanProcessJournalPath !== undefined\n\t\t\t? { orphanProcessJournalPath: descriptor.orphanProcessJournalPath }\n\t\t\t: {}),\n\t\tsupervisorSocketPath: descriptor.supervisorSocketPath,\n\t\tauthenticationToken: descriptor.authenticationToken,\n\t\trootActiveSessionId: descriptor.rootActiveSessionId,\n\t\t...(descriptor.ownerClientId !== undefined ? { ownerClientId: descriptor.ownerClientId } : {}),\n\t\t...(descriptor.rootSessionId !== undefined ? { rootSessionId: descriptor.rootSessionId } : {}),\n\t\t...(descriptor.sessionFile !== undefined ? { sessionFile: descriptor.sessionFile } : {}),\n\t\t...(sessionDir !== undefined ? { sessionDir } : {}),\n\t\t...(telemetryDisabled ? { telemetryDisabled: true as const } : {}),\n\t\tcreatedAt: descriptor.createdAt,\n\t\tupdatedAt: descriptor.updatedAt,\n\t\tlifecycle: descriptor.lifecycle,\n\t\tcreateCommand: durableDaemonCreateCommand(descriptor.createCommand),\n\t\tconsecutiveFailures: descriptor.consecutiveFailures,\n\t\t...(descriptor.stopRequestedAt !== undefined ? { stopRequestedAt: descriptor.stopRequestedAt } : {}),\n\t\t...(descriptor.archiveOnStop !== undefined ? { archiveOnStop: descriptor.archiveOnStop } : {}),\n\t\t...(descriptor.lastFailureAt !== undefined ? { lastFailureAt: descriptor.lastFailureAt } : {}),\n\t\t...(descriptor.lifecycle === \"failed\" ? { lastError: \"Waiting for a client with fresh runtime context\" } : {}),\n\t};\n}\n\nexport function isDaemonWorkerProcess(environment: NodeJS.ProcessEnv = process.env): boolean {\n\treturn environment[DAEMON_WORKER_ROLE_ENV] === \"1\";\n}\n\nexport function waitForDaemonWorkerStartupGate(environment: NodeJS.ProcessEnv = process.env): void {\n\tconst rawFd = environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tif (rawFd === undefined) {\n\t\treturn;\n\t}\n\tdelete environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tconst fd = Number(rawFd);\n\tif (!Number.isInteger(fd) || fd < 3) {\n\t\tthrow new Error(\"Daemon session worker has an invalid startup gate\");\n\t}\n\tlet marker: string;\n\ttry {\n\t\tmarker = readFileSync(fd, \"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\tif (marker !== DAEMON_WORKER_STARTUP_GATE_COMMIT) {\n\t\tthrow new Error(\"Daemon session worker startup was cancelled\");\n\t}\n}\n\nexport function requireDaemonWorkerAuthenticationToken(environment: NodeJS.ProcessEnv = process.env): string {\n\tconst token = environment[DAEMON_WORKER_TOKEN_ENV];\n\tif (!token) {\n\t\tthrow new Error(\"Daemon session worker is missing its authentication token\");\n\t}\n\treturn token;\n}\n\nexport function isDaemonWorkerFrameHeader(value: unknown): value is DaemonWorkerFrameHeader {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tif (candidate.kind === \"command\") {\n\t\treturn typeof candidate.requestId === \"string\" && typeof candidate.commandType === \"string\";\n\t}\n\treturn (\n\t\tcandidate.kind === \"outbound\" &&\n\t\ttypeof candidate.outboundType === \"string\" &&\n\t\t(candidate.requestId === undefined || typeof candidate.requestId === \"string\") &&\n\t\t(candidate.activeSessionId === undefined || typeof candidate.activeSessionId === \"string\") &&\n\t\t(candidate.snapshotId === undefined || typeof candidate.snapshotId === \"string\") &&\n\t\t(candidate.sessionEventType === undefined || typeof candidate.sessionEventType === \"string\") &&\n\t\t(candidate.snapshotPurpose === undefined ||\n\t\t\tcandidate.snapshotPurpose === \"attach\" ||\n\t\t\tcandidate.snapshotPurpose === \"replacement\" ||\n\t\t\tcandidate.snapshotPurpose === \"catchup\") &&\n\t\t(candidate.payloadEncoding === undefined ||\n\t\t\tcandidate.payloadEncoding === \"jsonl\" ||\n\t\t\tcandidate.payloadEncoding === \"assistant-delta\")\n\t);\n}\n"]}
1
+ {"version":3,"file":"daemon-worker-protocol.d.ts","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-worker-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { closeSync, readFileSync } from \"node:fs\";\nimport type { AgentSessionMessageDeliveryMode, AgentSessionMessageSender } from \"../../core/agent-messages.js\";\nimport type { IdleEvictionMinutes } from \"../../core/session-action-store.js\";\n\nexport { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from \"../../core/session-lease.js\";\n\nimport type { DaemonClientCapability, DaemonCommand, DaemonOutbound } from \"./daemon-protocol.js\";\n\nexport const DAEMON_WORKER_ROLE_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER\";\nexport const DAEMON_WORKER_TOKEN_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_TOKEN\";\nexport const DAEMON_WORKER_ACTIVE_SESSION_ID_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_ACTIVE_SESSION_ID\";\nexport const DAEMON_WORKER_SUPERVISOR_SOCKET_ENV = \"ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_SOCKET\";\nexport const DAEMON_WORKER_RECOVERY_JOURNAL_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_RECOVERY_JOURNAL\";\nexport const DAEMON_WORKER_STARTUP_GATE_FD_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_STARTUP_GATE_FD\";\nexport const DAEMON_WORKER_STARTUP_GATE_COMMIT = \"start\\n\";\nexport type DaemonWorkerLifecycle = \"starting\" | \"ready\" | \"recovering\" | \"stopping\" | \"failed\";\n\nexport type DaemonWorkerFrameHeader =\n\t| {\n\t\t\tkind: \"command\";\n\t\t\trequestId: string;\n\t\t\tcommandType: string;\n\t }\n\t| {\n\t\t\tkind: \"outbound\";\n\t\t\trequestId?: string;\n\t\t\toutboundType: DaemonOutbound[\"type\"];\n\t\t\tactiveSessionId?: string;\n\t\t\tsnapshotId?: string;\n\t\t\tsessionEventType?: string;\n\t\t\tpayloadEncoding?: \"jsonl\" | \"assistant-delta\";\n\t\t\tsnapshotPurpose?: \"attach\" | \"replacement\" | \"catchup\";\n\t };\n\nexport type DaemonCreateCommand = Extract<DaemonCommand, { type: \"create\" }>;\n\nexport interface DurableDaemonCreateCommand {\n\ttype: \"create\";\n\tsessionPath?: string;\n\tnoSession?: boolean;\n}\n\nexport function durableDaemonCreateCommand(command: DaemonCreateCommand): DurableDaemonCreateCommand {\n\treturn {\n\t\ttype: \"create\",\n\t\t...(command.sessionPath !== undefined ? { sessionPath: command.sessionPath } : {}),\n\t\t...(command.noSession !== undefined ? { noSession: command.noSession } : {}),\n\t};\n}\n\nexport type DaemonWorkerCommand =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_auth\";\n\t\t\ttoken: string;\n\t\t\tsupervisorGeneration: string;\n\t\t\tsupervisorPid: number;\n\t\t\tsupervisorProcessStartId?: string;\n\t\t\tsupervisorSocketPath: string;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_subscribe\";\n\t\t\tactiveSessionId: string;\n\t\t\tcapabilities?: readonly DaemonClientCapability[];\n\t\t\tsupportsExtensionUi?: boolean;\n\t }\n\t| { id?: string; type: \"worker_unsubscribe\"; activeSessionId: string }\n\t| { id?: string; type: \"worker_archive_and_shutdown\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_passivate_idle_children\";\n\t\t\tidleEvictionMinutes: IdleEvictionMinutes;\n\t\t\tnow: number;\n\t\t\tlimit: number;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_deliver_message\";\n\t\t\ttargetActiveSessionId: string;\n\t\t\tmessage: string;\n\t\t\tsender: AgentSessionMessageSender;\n\t\t\tdeliveryMode?: AgentSessionMessageDeliveryMode;\n\t }\n\t| { id?: string; type: \"worker_prepare_update\" }\n\t| { id?: string; type: \"worker_commit_update\" }\n\t| { id?: string; type: \"worker_cancel_update\" };\n\nexport type DaemonWorkerCommandBody = DaemonWorkerCommand extends infer TCommand\n\t? TCommand extends { id?: string }\n\t\t? Omit<TCommand, \"id\">\n\t\t: never\n\t: never;\n\nexport interface DaemonWorkerDescriptor {\n\tversion: 1 | 2;\n\tworkerId: string;\n\tpid: number;\n\tprocessStartId?: string;\n\tsocketPath: string;\n\trecoveryJournalPath: string;\n\torphanProcessJournalPath?: string;\n\tsupervisorSocketPath: string;\n\tauthenticationToken: string;\n\trootActiveSessionId: string;\n\t/** Stable protocol client that owns this worker. Omitted for resident sessions. */\n\townerClientId?: string;\n\trootSessionId?: string;\n\tsessionFile?: string;\n\tsessionDir?: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlifecycle: DaemonWorkerLifecycle;\n\tcreateCommand: DurableDaemonCreateCommand;\n\tconsecutiveFailures: number;\n\t/** Durable intent written before root termination so replacement supervisors never recover it. */\n\tstopRequestedAt?: string;\n\t/** Complete the root's archived lifecycle state after its process has stopped. */\n\tarchiveOnStop?: boolean;\n\tlastFailureAt?: string;\n\tlastError?: string;\n}\n\nexport function durableDaemonWorkerDescriptor(descriptor: DaemonWorkerDescriptor): DaemonWorkerDescriptor {\n\tconst versionOneCreateCommand = descriptor.createCommand as unknown as { config?: unknown };\n\tconst versionOneConfig =\n\t\tdescriptor.version === 1 &&\n\t\ttypeof versionOneCreateCommand.config === \"object\" &&\n\t\tversionOneCreateCommand.config !== null\n\t\t\t? (versionOneCreateCommand.config as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst sessionDir =\n\t\tdescriptor.sessionDir ??\n\t\t(typeof versionOneConfig?.sessionDir === \"string\" ? versionOneConfig.sessionDir : undefined);\n\treturn {\n\t\tversion: 2,\n\t\tworkerId: descriptor.workerId,\n\t\tpid: descriptor.pid,\n\t\t...(descriptor.processStartId !== undefined ? { processStartId: descriptor.processStartId } : {}),\n\t\tsocketPath: descriptor.socketPath,\n\t\trecoveryJournalPath: descriptor.recoveryJournalPath,\n\t\t...(descriptor.orphanProcessJournalPath !== undefined\n\t\t\t? { orphanProcessJournalPath: descriptor.orphanProcessJournalPath }\n\t\t\t: {}),\n\t\tsupervisorSocketPath: descriptor.supervisorSocketPath,\n\t\tauthenticationToken: descriptor.authenticationToken,\n\t\trootActiveSessionId: descriptor.rootActiveSessionId,\n\t\t...(descriptor.ownerClientId !== undefined ? { ownerClientId: descriptor.ownerClientId } : {}),\n\t\t...(descriptor.rootSessionId !== undefined ? { rootSessionId: descriptor.rootSessionId } : {}),\n\t\t...(descriptor.sessionFile !== undefined ? { sessionFile: descriptor.sessionFile } : {}),\n\t\t...(sessionDir !== undefined ? { sessionDir } : {}),\n\t\tcreatedAt: descriptor.createdAt,\n\t\tupdatedAt: descriptor.updatedAt,\n\t\tlifecycle: descriptor.lifecycle,\n\t\tcreateCommand: durableDaemonCreateCommand(descriptor.createCommand),\n\t\tconsecutiveFailures: descriptor.consecutiveFailures,\n\t\t...(descriptor.stopRequestedAt !== undefined ? { stopRequestedAt: descriptor.stopRequestedAt } : {}),\n\t\t...(descriptor.archiveOnStop !== undefined ? { archiveOnStop: descriptor.archiveOnStop } : {}),\n\t\t...(descriptor.lastFailureAt !== undefined ? { lastFailureAt: descriptor.lastFailureAt } : {}),\n\t\t...(descriptor.lifecycle === \"failed\" ? { lastError: \"Waiting for a client with fresh runtime context\" } : {}),\n\t};\n}\n\nexport function isDaemonWorkerProcess(environment: NodeJS.ProcessEnv = process.env): boolean {\n\treturn environment[DAEMON_WORKER_ROLE_ENV] === \"1\";\n}\n\nexport function waitForDaemonWorkerStartupGate(environment: NodeJS.ProcessEnv = process.env): void {\n\tconst rawFd = environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tif (rawFd === undefined) {\n\t\treturn;\n\t}\n\tdelete environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tconst fd = Number(rawFd);\n\tif (!Number.isInteger(fd) || fd < 3) {\n\t\tthrow new Error(\"Daemon session worker has an invalid startup gate\");\n\t}\n\tlet marker: string;\n\ttry {\n\t\tmarker = readFileSync(fd, \"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\tif (marker !== DAEMON_WORKER_STARTUP_GATE_COMMIT) {\n\t\tthrow new Error(\"Daemon session worker startup was cancelled\");\n\t}\n}\n\nexport function requireDaemonWorkerAuthenticationToken(environment: NodeJS.ProcessEnv = process.env): string {\n\tconst token = environment[DAEMON_WORKER_TOKEN_ENV];\n\tif (!token) {\n\t\tthrow new Error(\"Daemon session worker is missing its authentication token\");\n\t}\n\treturn token;\n}\n\nexport function isDaemonWorkerFrameHeader(value: unknown): value is DaemonWorkerFrameHeader {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tif (candidate.kind === \"command\") {\n\t\treturn typeof candidate.requestId === \"string\" && typeof candidate.commandType === \"string\";\n\t}\n\treturn (\n\t\tcandidate.kind === \"outbound\" &&\n\t\ttypeof candidate.outboundType === \"string\" &&\n\t\t(candidate.requestId === undefined || typeof candidate.requestId === \"string\") &&\n\t\t(candidate.activeSessionId === undefined || typeof candidate.activeSessionId === \"string\") &&\n\t\t(candidate.snapshotId === undefined || typeof candidate.snapshotId === \"string\") &&\n\t\t(candidate.sessionEventType === undefined || typeof candidate.sessionEventType === \"string\") &&\n\t\t(candidate.snapshotPurpose === undefined ||\n\t\t\tcandidate.snapshotPurpose === \"attach\" ||\n\t\t\tcandidate.snapshotPurpose === \"replacement\" ||\n\t\t\tcandidate.snapshotPurpose === \"catchup\") &&\n\t\t(candidate.payloadEncoding === undefined ||\n\t\t\tcandidate.payloadEncoding === \"jsonl\" ||\n\t\t\tcandidate.payloadEncoding === \"assistant-delta\")\n\t);\n}\n"]}
@@ -23,7 +23,6 @@ export function durableDaemonWorkerDescriptor(descriptor) {
23
23
  : undefined;
24
24
  const sessionDir = descriptor.sessionDir ??
25
25
  (typeof versionOneConfig?.sessionDir === "string" ? versionOneConfig.sessionDir : undefined);
26
- const telemetryDisabled = descriptor.telemetryDisabled === true || versionOneConfig?.telemetryDisabled === true;
27
26
  return {
28
27
  version: 2,
29
28
  workerId: descriptor.workerId,
@@ -41,7 +40,6 @@ export function durableDaemonWorkerDescriptor(descriptor) {
41
40
  ...(descriptor.rootSessionId !== undefined ? { rootSessionId: descriptor.rootSessionId } : {}),
42
41
  ...(descriptor.sessionFile !== undefined ? { sessionFile: descriptor.sessionFile } : {}),
43
42
  ...(sessionDir !== undefined ? { sessionDir } : {}),
44
- ...(telemetryDisabled ? { telemetryDisabled: true } : {}),
45
43
  createdAt: descriptor.createdAt,
46
44
  updatedAt: descriptor.updatedAt,
47
45
  lifecycle: descriptor.lifecycle,
@@ -1 +1 @@
1
- {"version":3,"file":"daemon-worker-protocol.js","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-worker-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { closeSync, readFileSync } from \"node:fs\";\nimport type {\n\tAgentSessionMessageAgentSummary,\n\tAgentSessionMessageDeliveryMode,\n\tAgentSessionMessageSender,\n} from \"../../core/agent-messages.js\";\nimport type { IdleEvictionMinutes } from \"../../core/session-action-store.js\";\n\nexport { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from \"../../core/session-lease.js\";\n\nimport type { DaemonClientCapability, DaemonCommand, DaemonOutbound } from \"./daemon-protocol.js\";\n\nexport const DAEMON_WORKER_ROLE_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER\";\nexport const DAEMON_WORKER_TOKEN_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_TOKEN\";\nexport const DAEMON_WORKER_ACTIVE_SESSION_ID_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_ACTIVE_SESSION_ID\";\nexport const DAEMON_WORKER_SUPERVISOR_SOCKET_ENV = \"ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_SOCKET\";\nexport const DAEMON_WORKER_RECOVERY_JOURNAL_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_RECOVERY_JOURNAL\";\nexport const DAEMON_WORKER_STARTUP_GATE_FD_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_STARTUP_GATE_FD\";\nexport const DAEMON_WORKER_STARTUP_GATE_COMMIT = \"start\\n\";\nexport type DaemonWorkerLifecycle = \"starting\" | \"ready\" | \"recovering\" | \"stopping\" | \"failed\";\n\nexport type DaemonWorkerFrameHeader =\n\t| {\n\t\t\tkind: \"command\";\n\t\t\trequestId: string;\n\t\t\tcommandType: string;\n\t }\n\t| {\n\t\t\tkind: \"outbound\";\n\t\t\trequestId?: string;\n\t\t\toutboundType: DaemonOutbound[\"type\"];\n\t\t\tactiveSessionId?: string;\n\t\t\tsnapshotId?: string;\n\t\t\tsessionEventType?: string;\n\t\t\tpayloadEncoding?: \"jsonl\" | \"assistant-delta\";\n\t\t\tsnapshotPurpose?: \"attach\" | \"replacement\" | \"catchup\";\n\t };\n\nexport type DaemonCreateCommand = Extract<DaemonCommand, { type: \"create\" }>;\n\nexport interface DurableDaemonCreateCommand {\n\ttype: \"create\";\n\tsessionPath?: string;\n\tnoSession?: boolean;\n}\n\nexport function durableDaemonCreateCommand(command: DaemonCreateCommand): DurableDaemonCreateCommand {\n\treturn {\n\t\ttype: \"create\",\n\t\t...(command.sessionPath !== undefined ? { sessionPath: command.sessionPath } : {}),\n\t\t...(command.noSession !== undefined ? { noSession: command.noSession } : {}),\n\t};\n}\n\nexport type DaemonWorkerCommand =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_auth\";\n\t\t\ttoken: string;\n\t\t\tsupervisorGeneration: string;\n\t\t\tsupervisorPid: number;\n\t\t\tsupervisorProcessStartId?: string;\n\t\t\tsupervisorSocketPath: string;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_subscribe\";\n\t\t\tactiveSessionId: string;\n\t\t\tcapabilities?: readonly DaemonClientCapability[];\n\t\t\tsupportsExtensionUi?: boolean;\n\t }\n\t| { id?: string; type: \"worker_unsubscribe\"; activeSessionId: string }\n\t| { id?: string; type: \"worker_sync_agent_peers\"; peers: AgentSessionMessageAgentSummary[] }\n\t| { id?: string; type: \"worker_archive_and_shutdown\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_passivate_idle_children\";\n\t\t\tidleEvictionMinutes: IdleEvictionMinutes;\n\t\t\tnow: number;\n\t\t\tlimit: number;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_deliver_message\";\n\t\t\ttargetActiveSessionId: string;\n\t\t\tmessage: string;\n\t\t\tsender: AgentSessionMessageSender;\n\t\t\tdeliveryMode?: AgentSessionMessageDeliveryMode;\n\t }\n\t| { id?: string; type: \"worker_prepare_update\" }\n\t| { id?: string; type: \"worker_commit_update\" }\n\t| { id?: string; type: \"worker_cancel_update\" };\n\nexport type DaemonWorkerCommandBody = DaemonWorkerCommand extends infer TCommand\n\t? TCommand extends { id?: string }\n\t\t? Omit<TCommand, \"id\">\n\t\t: never\n\t: never;\n\nexport interface DaemonWorkerDescriptor {\n\tversion: 1 | 2;\n\tworkerId: string;\n\tpid: number;\n\tprocessStartId?: string;\n\tsocketPath: string;\n\trecoveryJournalPath: string;\n\torphanProcessJournalPath?: string;\n\tsupervisorSocketPath: string;\n\tauthenticationToken: string;\n\trootActiveSessionId: string;\n\t/** Stable protocol client that owns this worker. Omitted for resident sessions. */\n\townerClientId?: string;\n\trootSessionId?: string;\n\tsessionFile?: string;\n\tsessionDir?: string;\n\ttelemetryDisabled?: true;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlifecycle: DaemonWorkerLifecycle;\n\tcreateCommand: DurableDaemonCreateCommand;\n\tconsecutiveFailures: number;\n\t/** Durable intent written before root termination so replacement supervisors never recover it. */\n\tstopRequestedAt?: string;\n\t/** Complete the root's archived lifecycle state after its process has stopped. */\n\tarchiveOnStop?: boolean;\n\tlastFailureAt?: string;\n\tlastError?: string;\n}\n\nexport function durableDaemonWorkerDescriptor(descriptor: DaemonWorkerDescriptor): DaemonWorkerDescriptor {\n\tconst versionOneCreateCommand = descriptor.createCommand as unknown as { config?: unknown };\n\tconst versionOneConfig =\n\t\tdescriptor.version === 1 &&\n\t\ttypeof versionOneCreateCommand.config === \"object\" &&\n\t\tversionOneCreateCommand.config !== null\n\t\t\t? (versionOneCreateCommand.config as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst sessionDir =\n\t\tdescriptor.sessionDir ??\n\t\t(typeof versionOneConfig?.sessionDir === \"string\" ? versionOneConfig.sessionDir : undefined);\n\tconst telemetryDisabled = descriptor.telemetryDisabled === true || versionOneConfig?.telemetryDisabled === true;\n\treturn {\n\t\tversion: 2,\n\t\tworkerId: descriptor.workerId,\n\t\tpid: descriptor.pid,\n\t\t...(descriptor.processStartId !== undefined ? { processStartId: descriptor.processStartId } : {}),\n\t\tsocketPath: descriptor.socketPath,\n\t\trecoveryJournalPath: descriptor.recoveryJournalPath,\n\t\t...(descriptor.orphanProcessJournalPath !== undefined\n\t\t\t? { orphanProcessJournalPath: descriptor.orphanProcessJournalPath }\n\t\t\t: {}),\n\t\tsupervisorSocketPath: descriptor.supervisorSocketPath,\n\t\tauthenticationToken: descriptor.authenticationToken,\n\t\trootActiveSessionId: descriptor.rootActiveSessionId,\n\t\t...(descriptor.ownerClientId !== undefined ? { ownerClientId: descriptor.ownerClientId } : {}),\n\t\t...(descriptor.rootSessionId !== undefined ? { rootSessionId: descriptor.rootSessionId } : {}),\n\t\t...(descriptor.sessionFile !== undefined ? { sessionFile: descriptor.sessionFile } : {}),\n\t\t...(sessionDir !== undefined ? { sessionDir } : {}),\n\t\t...(telemetryDisabled ? { telemetryDisabled: true as const } : {}),\n\t\tcreatedAt: descriptor.createdAt,\n\t\tupdatedAt: descriptor.updatedAt,\n\t\tlifecycle: descriptor.lifecycle,\n\t\tcreateCommand: durableDaemonCreateCommand(descriptor.createCommand),\n\t\tconsecutiveFailures: descriptor.consecutiveFailures,\n\t\t...(descriptor.stopRequestedAt !== undefined ? { stopRequestedAt: descriptor.stopRequestedAt } : {}),\n\t\t...(descriptor.archiveOnStop !== undefined ? { archiveOnStop: descriptor.archiveOnStop } : {}),\n\t\t...(descriptor.lastFailureAt !== undefined ? { lastFailureAt: descriptor.lastFailureAt } : {}),\n\t\t...(descriptor.lifecycle === \"failed\" ? { lastError: \"Waiting for a client with fresh runtime context\" } : {}),\n\t};\n}\n\nexport function isDaemonWorkerProcess(environment: NodeJS.ProcessEnv = process.env): boolean {\n\treturn environment[DAEMON_WORKER_ROLE_ENV] === \"1\";\n}\n\nexport function waitForDaemonWorkerStartupGate(environment: NodeJS.ProcessEnv = process.env): void {\n\tconst rawFd = environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tif (rawFd === undefined) {\n\t\treturn;\n\t}\n\tdelete environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tconst fd = Number(rawFd);\n\tif (!Number.isInteger(fd) || fd < 3) {\n\t\tthrow new Error(\"Daemon session worker has an invalid startup gate\");\n\t}\n\tlet marker: string;\n\ttry {\n\t\tmarker = readFileSync(fd, \"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\tif (marker !== DAEMON_WORKER_STARTUP_GATE_COMMIT) {\n\t\tthrow new Error(\"Daemon session worker startup was cancelled\");\n\t}\n}\n\nexport function requireDaemonWorkerAuthenticationToken(environment: NodeJS.ProcessEnv = process.env): string {\n\tconst token = environment[DAEMON_WORKER_TOKEN_ENV];\n\tif (!token) {\n\t\tthrow new Error(\"Daemon session worker is missing its authentication token\");\n\t}\n\treturn token;\n}\n\nexport function isDaemonWorkerFrameHeader(value: unknown): value is DaemonWorkerFrameHeader {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tif (candidate.kind === \"command\") {\n\t\treturn typeof candidate.requestId === \"string\" && typeof candidate.commandType === \"string\";\n\t}\n\treturn (\n\t\tcandidate.kind === \"outbound\" &&\n\t\ttypeof candidate.outboundType === \"string\" &&\n\t\t(candidate.requestId === undefined || typeof candidate.requestId === \"string\") &&\n\t\t(candidate.activeSessionId === undefined || typeof candidate.activeSessionId === \"string\") &&\n\t\t(candidate.snapshotId === undefined || typeof candidate.snapshotId === \"string\") &&\n\t\t(candidate.sessionEventType === undefined || typeof candidate.sessionEventType === \"string\") &&\n\t\t(candidate.snapshotPurpose === undefined ||\n\t\t\tcandidate.snapshotPurpose === \"attach\" ||\n\t\t\tcandidate.snapshotPurpose === \"replacement\" ||\n\t\t\tcandidate.snapshotPurpose === \"catchup\") &&\n\t\t(candidate.payloadEncoding === undefined ||\n\t\t\tcandidate.payloadEncoding === \"jsonl\" ||\n\t\t\tcandidate.payloadEncoding === \"assistant-delta\")\n\t);\n}\n"]}
1
+ {"version":3,"file":"daemon-worker-protocol.js","sourceRoot":"","sources":["../../../src/modes/daemon/daemon-worker-protocol.ts"],"names":[],"mappings":"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","sourcesContent":["import { closeSync, readFileSync } from \"node:fs\";\nimport type { AgentSessionMessageDeliveryMode, AgentSessionMessageSender } from \"../../core/agent-messages.js\";\nimport type { IdleEvictionMinutes } from \"../../core/session-action-store.js\";\n\nexport { SESSION_LEASE_OWNER_ID_ENV, SESSION_LEASES_ENABLED_ENV } from \"../../core/session-lease.js\";\n\nimport type { DaemonClientCapability, DaemonCommand, DaemonOutbound } from \"./daemon-protocol.js\";\n\nexport const DAEMON_WORKER_ROLE_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER\";\nexport const DAEMON_WORKER_TOKEN_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_TOKEN\";\nexport const DAEMON_WORKER_ACTIVE_SESSION_ID_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_ACTIVE_SESSION_ID\";\nexport const DAEMON_WORKER_SUPERVISOR_SOCKET_ENV = \"ASM_AGENT_INTERNAL_DAEMON_SUPERVISOR_SOCKET\";\nexport const DAEMON_WORKER_RECOVERY_JOURNAL_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_RECOVERY_JOURNAL\";\nexport const DAEMON_WORKER_STARTUP_GATE_FD_ENV = \"ASM_AGENT_INTERNAL_DAEMON_WORKER_STARTUP_GATE_FD\";\nexport const DAEMON_WORKER_STARTUP_GATE_COMMIT = \"start\\n\";\nexport type DaemonWorkerLifecycle = \"starting\" | \"ready\" | \"recovering\" | \"stopping\" | \"failed\";\n\nexport type DaemonWorkerFrameHeader =\n\t| {\n\t\t\tkind: \"command\";\n\t\t\trequestId: string;\n\t\t\tcommandType: string;\n\t }\n\t| {\n\t\t\tkind: \"outbound\";\n\t\t\trequestId?: string;\n\t\t\toutboundType: DaemonOutbound[\"type\"];\n\t\t\tactiveSessionId?: string;\n\t\t\tsnapshotId?: string;\n\t\t\tsessionEventType?: string;\n\t\t\tpayloadEncoding?: \"jsonl\" | \"assistant-delta\";\n\t\t\tsnapshotPurpose?: \"attach\" | \"replacement\" | \"catchup\";\n\t };\n\nexport type DaemonCreateCommand = Extract<DaemonCommand, { type: \"create\" }>;\n\nexport interface DurableDaemonCreateCommand {\n\ttype: \"create\";\n\tsessionPath?: string;\n\tnoSession?: boolean;\n}\n\nexport function durableDaemonCreateCommand(command: DaemonCreateCommand): DurableDaemonCreateCommand {\n\treturn {\n\t\ttype: \"create\",\n\t\t...(command.sessionPath !== undefined ? { sessionPath: command.sessionPath } : {}),\n\t\t...(command.noSession !== undefined ? { noSession: command.noSession } : {}),\n\t};\n}\n\nexport type DaemonWorkerCommand =\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_auth\";\n\t\t\ttoken: string;\n\t\t\tsupervisorGeneration: string;\n\t\t\tsupervisorPid: number;\n\t\t\tsupervisorProcessStartId?: string;\n\t\t\tsupervisorSocketPath: string;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_subscribe\";\n\t\t\tactiveSessionId: string;\n\t\t\tcapabilities?: readonly DaemonClientCapability[];\n\t\t\tsupportsExtensionUi?: boolean;\n\t }\n\t| { id?: string; type: \"worker_unsubscribe\"; activeSessionId: string }\n\t| { id?: string; type: \"worker_archive_and_shutdown\" }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_passivate_idle_children\";\n\t\t\tidleEvictionMinutes: IdleEvictionMinutes;\n\t\t\tnow: number;\n\t\t\tlimit: number;\n\t }\n\t| {\n\t\t\tid?: string;\n\t\t\ttype: \"worker_deliver_message\";\n\t\t\ttargetActiveSessionId: string;\n\t\t\tmessage: string;\n\t\t\tsender: AgentSessionMessageSender;\n\t\t\tdeliveryMode?: AgentSessionMessageDeliveryMode;\n\t }\n\t| { id?: string; type: \"worker_prepare_update\" }\n\t| { id?: string; type: \"worker_commit_update\" }\n\t| { id?: string; type: \"worker_cancel_update\" };\n\nexport type DaemonWorkerCommandBody = DaemonWorkerCommand extends infer TCommand\n\t? TCommand extends { id?: string }\n\t\t? Omit<TCommand, \"id\">\n\t\t: never\n\t: never;\n\nexport interface DaemonWorkerDescriptor {\n\tversion: 1 | 2;\n\tworkerId: string;\n\tpid: number;\n\tprocessStartId?: string;\n\tsocketPath: string;\n\trecoveryJournalPath: string;\n\torphanProcessJournalPath?: string;\n\tsupervisorSocketPath: string;\n\tauthenticationToken: string;\n\trootActiveSessionId: string;\n\t/** Stable protocol client that owns this worker. Omitted for resident sessions. */\n\townerClientId?: string;\n\trootSessionId?: string;\n\tsessionFile?: string;\n\tsessionDir?: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlifecycle: DaemonWorkerLifecycle;\n\tcreateCommand: DurableDaemonCreateCommand;\n\tconsecutiveFailures: number;\n\t/** Durable intent written before root termination so replacement supervisors never recover it. */\n\tstopRequestedAt?: string;\n\t/** Complete the root's archived lifecycle state after its process has stopped. */\n\tarchiveOnStop?: boolean;\n\tlastFailureAt?: string;\n\tlastError?: string;\n}\n\nexport function durableDaemonWorkerDescriptor(descriptor: DaemonWorkerDescriptor): DaemonWorkerDescriptor {\n\tconst versionOneCreateCommand = descriptor.createCommand as unknown as { config?: unknown };\n\tconst versionOneConfig =\n\t\tdescriptor.version === 1 &&\n\t\ttypeof versionOneCreateCommand.config === \"object\" &&\n\t\tversionOneCreateCommand.config !== null\n\t\t\t? (versionOneCreateCommand.config as Record<string, unknown>)\n\t\t\t: undefined;\n\tconst sessionDir =\n\t\tdescriptor.sessionDir ??\n\t\t(typeof versionOneConfig?.sessionDir === \"string\" ? versionOneConfig.sessionDir : undefined);\n\treturn {\n\t\tversion: 2,\n\t\tworkerId: descriptor.workerId,\n\t\tpid: descriptor.pid,\n\t\t...(descriptor.processStartId !== undefined ? { processStartId: descriptor.processStartId } : {}),\n\t\tsocketPath: descriptor.socketPath,\n\t\trecoveryJournalPath: descriptor.recoveryJournalPath,\n\t\t...(descriptor.orphanProcessJournalPath !== undefined\n\t\t\t? { orphanProcessJournalPath: descriptor.orphanProcessJournalPath }\n\t\t\t: {}),\n\t\tsupervisorSocketPath: descriptor.supervisorSocketPath,\n\t\tauthenticationToken: descriptor.authenticationToken,\n\t\trootActiveSessionId: descriptor.rootActiveSessionId,\n\t\t...(descriptor.ownerClientId !== undefined ? { ownerClientId: descriptor.ownerClientId } : {}),\n\t\t...(descriptor.rootSessionId !== undefined ? { rootSessionId: descriptor.rootSessionId } : {}),\n\t\t...(descriptor.sessionFile !== undefined ? { sessionFile: descriptor.sessionFile } : {}),\n\t\t...(sessionDir !== undefined ? { sessionDir } : {}),\n\t\tcreatedAt: descriptor.createdAt,\n\t\tupdatedAt: descriptor.updatedAt,\n\t\tlifecycle: descriptor.lifecycle,\n\t\tcreateCommand: durableDaemonCreateCommand(descriptor.createCommand),\n\t\tconsecutiveFailures: descriptor.consecutiveFailures,\n\t\t...(descriptor.stopRequestedAt !== undefined ? { stopRequestedAt: descriptor.stopRequestedAt } : {}),\n\t\t...(descriptor.archiveOnStop !== undefined ? { archiveOnStop: descriptor.archiveOnStop } : {}),\n\t\t...(descriptor.lastFailureAt !== undefined ? { lastFailureAt: descriptor.lastFailureAt } : {}),\n\t\t...(descriptor.lifecycle === \"failed\" ? { lastError: \"Waiting for a client with fresh runtime context\" } : {}),\n\t};\n}\n\nexport function isDaemonWorkerProcess(environment: NodeJS.ProcessEnv = process.env): boolean {\n\treturn environment[DAEMON_WORKER_ROLE_ENV] === \"1\";\n}\n\nexport function waitForDaemonWorkerStartupGate(environment: NodeJS.ProcessEnv = process.env): void {\n\tconst rawFd = environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tif (rawFd === undefined) {\n\t\treturn;\n\t}\n\tdelete environment[DAEMON_WORKER_STARTUP_GATE_FD_ENV];\n\tconst fd = Number(rawFd);\n\tif (!Number.isInteger(fd) || fd < 3) {\n\t\tthrow new Error(\"Daemon session worker has an invalid startup gate\");\n\t}\n\tlet marker: string;\n\ttry {\n\t\tmarker = readFileSync(fd, \"utf8\");\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n\tif (marker !== DAEMON_WORKER_STARTUP_GATE_COMMIT) {\n\t\tthrow new Error(\"Daemon session worker startup was cancelled\");\n\t}\n}\n\nexport function requireDaemonWorkerAuthenticationToken(environment: NodeJS.ProcessEnv = process.env): string {\n\tconst token = environment[DAEMON_WORKER_TOKEN_ENV];\n\tif (!token) {\n\t\tthrow new Error(\"Daemon session worker is missing its authentication token\");\n\t}\n\treturn token;\n}\n\nexport function isDaemonWorkerFrameHeader(value: unknown): value is DaemonWorkerFrameHeader {\n\tif (!value || typeof value !== \"object\") {\n\t\treturn false;\n\t}\n\tconst candidate = value as Record<string, unknown>;\n\tif (candidate.kind === \"command\") {\n\t\treturn typeof candidate.requestId === \"string\" && typeof candidate.commandType === \"string\";\n\t}\n\treturn (\n\t\tcandidate.kind === \"outbound\" &&\n\t\ttypeof candidate.outboundType === \"string\" &&\n\t\t(candidate.requestId === undefined || typeof candidate.requestId === \"string\") &&\n\t\t(candidate.activeSessionId === undefined || typeof candidate.activeSessionId === \"string\") &&\n\t\t(candidate.snapshotId === undefined || typeof candidate.snapshotId === \"string\") &&\n\t\t(candidate.sessionEventType === undefined || typeof candidate.sessionEventType === \"string\") &&\n\t\t(candidate.snapshotPurpose === undefined ||\n\t\t\tcandidate.snapshotPurpose === \"attach\" ||\n\t\t\tcandidate.snapshotPurpose === \"replacement\" ||\n\t\t\tcandidate.snapshotPurpose === \"catchup\") &&\n\t\t(candidate.payloadEncoding === undefined ||\n\t\t\tcandidate.payloadEncoding === \"jsonl\" ||\n\t\t\tcandidate.payloadEncoding === \"assistant-delta\")\n\t);\n}\n"]}
@@ -63,9 +63,7 @@ export declare class ProviderAuthFlows {
63
63
  private getPrimeInferenceDefaultTeamStatus;
64
64
  private selectPrimeInferenceTeam;
65
65
  private completePrimeInferenceLogin;
66
- private completeAsmAgentTracesLogin;
67
66
  runPrimeInferenceLogin(): Promise<AuthenticationResult>;
68
- runAsmAgentTracesLogin(): Promise<AuthenticationResult>;
69
67
  private showApiKeyLoginDialog;
70
68
  private showOAuthLoginSelect;
71
69
  private showLoginDialog;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-flows.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@asm-agent/ai\";\nimport type { OverlayHandle, TUI } from \"@asm-agent/tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport {\n\tASM_AGENT_TRACES_PROVIDER_ID,\n\tASM_AGENT_TRACES_PROVIDER_NAME,\n\tcheckAsmAgentTracesAccess,\n\tcheckPrimeInferenceAccess,\n\tfetchPrimeTeams,\n\tloadPrimeCliConfig,\n\tloginAsmAgentTraces,\n\tloginPrimeInference,\n\tPRIME_INFERENCE_PROVIDER_ID,\n\tPRIME_INFERENCE_PROVIDER_NAME,\n\ttype PrimeTeam,\n\tresolveAsmAgentTracesBaseUrl,\n} from \"../../core/prime-inference-auth.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { PrimeTeamSelectorComponent } from \"./components/prime-team-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\nexport const ASM_AGENT_LOGIN_PROVIDER_IDS = new Set([\"openai-codex\", \"anthropic\"]);\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No subscription providers available.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === PRIME_INFERENCE_PROVIDER_ID) {\n\t\t\treturn this.runPrimeInferenceLogin();\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.logout(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tif (authType === \"api_key\") {\n\t\t\treturn [];\n\t\t}\n\t\tconst oauthProviders = authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.filter((provider) => ASM_AGENT_LOGIN_PROVIDER_IDS.has(provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t}));\n\n\t\treturn options.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\tif (!options.some((option) => option.id === PRIME_INFERENCE_PROVIDER_ID)) {\n\t\t\tconst primeInferenceStatus = authStorage.getAuthStatus(PRIME_INFERENCE_PROVIDER_ID);\n\t\t\tif (primeInferenceStatus.source === \"prime_cli\") {\n\t\t\t\toptions.push({\n\t\t\t\t\tid: PRIME_INFERENCE_PROVIDER_ID,\n\t\t\t\t\tname: PRIME_INFERENCE_PROVIDER_NAME,\n\t\t\t\t\tauthType: \"api_key\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showPrimeTeamSelector(\n\t\tteams: PrimeTeam[],\n\t\tcurrentTeamId: string | undefined,\n\t): Promise<PrimeTeam | null | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new PrimeTeamSelectorComponent(\n\t\t\t\tteams,\n\t\t\t\tcurrentTeamId,\n\t\t\t\t(team) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(team);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tprivate getPrimeInferenceDefaultTeamStatus(): string {\n\t\tconst configPath = this.host.modelRegistry.authStorage.getPrimeCliConfigPath();\n\t\tif (configPath) {\n\t\t\tlet config: ReturnType<typeof loadPrimeCliConfig>;\n\t\t\ttry {\n\t\t\t\tconfig = loadPrimeCliConfig(configPath);\n\t\t\t} catch {\n\t\t\t\treturn \"Using personal account.\";\n\t\t\t}\n\t\t\tif (config.teamIdFromEnv) {\n\t\t\t\treturn \"Using team from PRIME_TEAM_ID.\";\n\t\t\t}\n\t\t\tif (config.teamName) {\n\t\t\t\treturn `Using team \"${config.teamName}\".`;\n\t\t\t}\n\t\t\tif (config.teamId) {\n\t\t\t\treturn \"Using Prime CLI team.\";\n\t\t\t}\n\t\t}\n\t\tconst storedTeam = this.host.modelRegistry.authStorage.getPrimeInferenceTeamSelection();\n\t\tif (storedTeam) {\n\t\t\treturn `Using team \"${storedTeam.name}\".`;\n\t\t}\n\t\tif (storedTeam === null) {\n\t\t\treturn \"Using personal account.\";\n\t\t}\n\t\treturn \"Using personal account.\";\n\t}\n\n\tprivate async selectPrimeInferenceTeam(apiKey: string, dialog: LoginDialogComponent): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst config = loadPrimeCliConfig(this.host.modelRegistry.authStorage.getPrimeCliConfigPath());\n\t\t\tif (config.teamIdFromEnv) {\n\t\t\t\tthis.host.modelRegistry.authStorage.reload();\n\t\t\t\treturn \"Using team from PRIME_TEAM_ID.\";\n\t\t\t}\n\n\t\t\tdialog.showProgress(\"Loading Prime teams...\");\n\t\t\tconst teams = await fetchPrimeTeams(apiKey, config.baseUrl, { signal: dialog.signal });\n\t\t\tif (dialog.signal.aborted) {\n\t\t\t\treturn this.getPrimeInferenceDefaultTeamStatus();\n\t\t\t}\n\t\t\tif (teams.length === 0) {\n\t\t\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceTeamSelection(null);\n\t\t\t\treturn \"Using personal account.\";\n\t\t\t}\n\n\t\t\tconst storedTeam = this.host.modelRegistry.authStorage.getPrimeInferenceTeamSelection();\n\t\t\tconst currentTeamId = storedTeam === null ? undefined : (storedTeam?.teamId ?? config.teamId);\n\t\t\tconst selectedTeam = await this.showPrimeTeamSelector(teams, currentTeamId);\n\t\t\tif (selectedTeam !== undefined) {\n\t\t\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceTeamSelection(selectedTeam);\n\t\t\t}\n\t\t\treturn selectedTeam\n\t\t\t\t? `Using team \"${selectedTeam.name}\".`\n\t\t\t\t: selectedTeam === null\n\t\t\t\t\t? \"Using personal account.\"\n\t\t\t\t\t: this.getPrimeInferenceDefaultTeamStatus();\n\t\t} catch {\n\t\t\tthis.host.modelRegistry.authStorage.reload();\n\t\t\treturn this.getPrimeInferenceDefaultTeamStatus();\n\t\t}\n\t}\n\n\tprivate async completePrimeInferenceLogin(\n\t\tapiKey: string,\n\t\tdialog: LoginDialogComponent,\n\t\tcloseDialog: () => void,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceApiKey(apiKey);\n\t\tconst teamStatus = await this.selectPrimeInferenceTeam(apiKey, dialog);\n\n\t\tcloseDialog();\n\t\treturn await this.completeProviderAuthentication(\n\t\t\tPRIME_INFERENCE_PROVIDER_ID,\n\t\t\tPRIME_INFERENCE_PROVIDER_NAME,\n\t\t\t\"api_key\",\n\t\t\tteamStatus,\n\t\t\t\"provider\",\n\t\t\tthis.host.modelRegistry.authStorage.getPrimeCliConfigPath() ?? getAuthPath(),\n\t\t);\n\t}\n\n\tprivate async completeAsmAgentTracesLogin(apiKey: string, closeDialog: () => void): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.authStorage.set(ASM_AGENT_TRACES_PROVIDER_ID, {\n\t\t\ttype: \"api_key\",\n\t\t\tkey: apiKey,\n\t\t});\n\n\t\tcloseDialog();\n\t\treturn await this.completeProviderAuthentication(\n\t\t\tASM_AGENT_TRACES_PROVIDER_ID,\n\t\t\tASM_AGENT_TRACES_PROVIDER_NAME,\n\t\t\t\"api_key\",\n\t\t);\n\t}\n\n\tasync runPrimeInferenceLogin(): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(\n\t\t\tthis.host.ui,\n\t\t\tPRIME_INFERENCE_PROVIDER_ID,\n\t\t\t(_success, _message) => {},\n\t\t\tPRIME_INFERENCE_PROVIDER_NAME,\n\t\t);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\t// The browser challenge gets its own controller so a manually pasted key\n\t\t// can stop the polling without tearing down the dialog.\n\t\tconst browserAbort = new AbortController();\n\t\tconst onDialogAbort = () => browserAbort.abort();\n\t\tdialog.signal.addEventListener(\"abort\", onDialogAbort, { once: true });\n\n\t\tlet manualInputArmed = false;\n\t\tlet resolveManualKey: (entry: { apiKey: string; source: \"manual\" }) => void = () => {};\n\t\tconst manualKeyEntry = new Promise<{ apiKey: string; source: \"manual\" }>((resolve) => {\n\t\t\tresolveManualKey = resolve;\n\t\t});\n\t\tconst armManualInput = (prompt: string): void => {\n\t\t\tmanualInputArmed = true;\n\t\t\tvoid (async () => {\n\t\t\t\tlet value = (await dialog.showManualInput(prompt)).trim();\n\t\t\t\twhile (!value) {\n\t\t\t\t\tvalue = (await dialog.waitForInput()).trim();\n\t\t\t\t}\n\t\t\t\tresolveManualKey({ apiKey: value, source: \"manual\" });\n\t\t\t})().catch(() => {\n\t\t\t\t// Cancellation surfaces through the dialog signal.\n\t\t\t});\n\t\t};\n\n\t\ttry {\n\t\t\tconst browserLogin = loginPrimeInference(\n\t\t\t\t{\n\t\t\t\t\tonAuth: (info) => {\n\t\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\t\t\t\t\t\tarmManualInput(\"Complete the sign-in in your browser, or paste an API key below:\");\n\t\t\t\t\t},\n\t\t\t\t\tonProgress: (message) => {\n\t\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t\t},\n\t\t\t\t\tsignal: browserAbort.signal,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tconfigPath: this.host.modelRegistry.authStorage.getPrimeCliConfigPath(),\n\t\t\t\t},\n\t\t\t);\n\t\t\t// When the browser challenge cannot start or breaks down, keep the dialog\n\t\t\t// open and fall back to plain API key entry instead of failing outright.\n\t\t\tconst browserLoginOrFallback = browserLogin.catch((error: unknown) => {\n\t\t\t\tif (browserAbort.signal.aborted) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\t\tdialog.showProgress(`Browser sign-in unavailable (${errorMsg}).`);\n\t\t\t\tif (!manualInputArmed) {\n\t\t\t\t\tarmManualInput(\"Paste a Prime API key below:\");\n\t\t\t\t}\n\t\t\t\treturn manualKeyEntry;\n\t\t\t});\n\t\t\t// Once the browser flow has settled into manual fallback, nothing above\n\t\t\t// rejects on cancel anymore, so the dialog signal must end the race too.\n\t\t\tconst dialogCancelled = new Promise<never>((_, reject) => {\n\t\t\t\tdialog.signal.addEventListener(\"abort\", () => reject(new Error(\"Login cancelled\")), { once: true });\n\t\t\t});\n\t\t\t// Promise.race observes the rejections below, but keep dedicated handlers\n\t\t\t// so neither an aborted browser flow nor a cancelled dialog can surface\n\t\t\t// as an unhandled rejection.\n\t\t\tbrowserLoginOrFallback.catch(() => {});\n\t\t\tdialogCancelled.catch(() => {});\n\n\t\t\tconst result = await Promise.race([browserLoginOrFallback, manualKeyEntry, dialogCancelled]);\n\t\t\tif (dialog.signal.aborted) {\n\t\t\t\tcloseDialog();\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\n\t\t\tif (result.source === \"manual\") {\n\t\t\t\tbrowserAbort.abort();\n\t\t\t\tdialog.showProgress(\"Checking Prime Inference access...\");\n\t\t\t\tconst config = loadPrimeCliConfig(this.host.modelRegistry.authStorage.getPrimeCliConfigPath());\n\t\t\t\tconst access = await checkPrimeInferenceAccess(result.apiKey, config.baseUrl, { signal: dialog.signal });\n\t\t\t\tif (dialog.signal.aborted) {\n\t\t\t\t\tcloseDialog();\n\t\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t\t}\n\t\t\t\tif (!access.ok) {\n\t\t\t\t\tconst status = access.status === undefined ? \"\" : `HTTP ${access.status}: `;\n\t\t\t\t\tthrow new Error(`Prime API key does not have Prime Inference access (${status}${access.message})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn await this.completePrimeInferenceLogin(result.apiKey, dialog, closeDialog);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (!dialog.signal.aborted && errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${PRIME_INFERENCE_PROVIDER_NAME}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t} finally {\n\t\t\tdialog.signal.removeEventListener(\"abort\", onDialogAbort);\n\t\t}\n\t}\n\n\tasync runAsmAgentTracesLogin(): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(\n\t\t\tthis.host.ui,\n\t\t\tASM_AGENT_TRACES_PROVIDER_ID,\n\t\t\t(_success, _message) => {},\n\t\t\tASM_AGENT_TRACES_PROVIDER_NAME,\n\t\t);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\tconst browserAbort = new AbortController();\n\t\tconst onDialogAbort = () => browserAbort.abort();\n\t\tdialog.signal.addEventListener(\"abort\", onDialogAbort, { once: true });\n\n\t\tlet manualInputArmed = false;\n\t\tlet resolveManualKey: (entry: { apiKey: string; source: \"manual\" }) => void = () => {};\n\t\tconst manualKeyEntry = new Promise<{ apiKey: string; source: \"manual\" }>((resolve) => {\n\t\t\tresolveManualKey = resolve;\n\t\t});\n\t\tconst armManualInput = (prompt: string): void => {\n\t\t\tmanualInputArmed = true;\n\t\t\tvoid (async () => {\n\t\t\t\tlet value = (await dialog.showManualInput(prompt)).trim();\n\t\t\t\twhile (!value) {\n\t\t\t\t\tvalue = (await dialog.waitForInput()).trim();\n\t\t\t\t}\n\t\t\t\tresolveManualKey({ apiKey: value, source: \"manual\" });\n\t\t\t})().catch(() => {\n\t\t\t\t// Cancellation surfaces through the dialog signal.\n\t\t\t});\n\t\t};\n\n\t\ttry {\n\t\t\tconst browserLogin = loginAsmAgentTraces({\n\t\t\t\tonAuth: (info) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\t\t\t\t\tarmManualInput(\"Complete the sign-in in your browser, or paste a Prime API key below:\");\n\t\t\t\t},\n\t\t\t\tonProgress: (message) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\t\t\t\tsignal: browserAbort.signal,\n\t\t\t});\n\t\t\tconst browserLoginOrFallback = browserLogin.catch((error: unknown) => {\n\t\t\t\tif (browserAbort.signal.aborted) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\t\tdialog.showProgress(`Browser sign-in unavailable (${errorMsg}).`);\n\t\t\t\tif (!manualInputArmed) {\n\t\t\t\t\tarmManualInput(\"Paste a Prime API key below:\");\n\t\t\t\t}\n\t\t\t\treturn manualKeyEntry;\n\t\t\t});\n\t\t\tconst dialogCancelled = new Promise<never>((_, reject) => {\n\t\t\t\tdialog.signal.addEventListener(\"abort\", () => reject(new Error(\"Login cancelled\")), { once: true });\n\t\t\t});\n\t\t\tbrowserLoginOrFallback.catch(() => {});\n\t\t\tdialogCancelled.catch(() => {});\n\n\t\t\tconst result = await Promise.race([browserLoginOrFallback, manualKeyEntry, dialogCancelled]);\n\t\t\tif (dialog.signal.aborted) {\n\t\t\t\tcloseDialog();\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\n\t\t\tif (result.source === \"manual\") {\n\t\t\t\tbrowserAbort.abort();\n\t\t\t\tdialog.showProgress(\"Checking ASM Agent trace access...\");\n\t\t\t\tconst access = await checkAsmAgentTracesAccess(result.apiKey, resolveAsmAgentTracesBaseUrl(), {\n\t\t\t\t\tsignal: dialog.signal,\n\t\t\t\t});\n\t\t\t\tif (dialog.signal.aborted) {\n\t\t\t\t\tcloseDialog();\n\t\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t\t}\n\t\t\t\tif (!access.ok) {\n\t\t\t\t\tconst status = access.status === undefined ? \"\" : `HTTP ${access.status}: `;\n\t\t\t\t\tthrow new Error(`Prime API key does not have ASM Agent trace access (${status}${access.message})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn await this.completeAsmAgentTracesLogin(result.apiKey, closeDialog);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (!dialog.signal.aborted && errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${ASM_AGENT_TRACES_PROVIDER_NAME}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t} finally {\n\t\t\tdialog.signal.removeEventListener(\"abort\", onDialogAbort);\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\")).trim();\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"API key cannot be empty.\");\n\t\t\t}\n\n\t\t\tthis.host.modelRegistry.authStorage.set(providerId, { type: \"api_key\", key: apiKey });\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"oauth\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"auth-flows.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/auth-flows.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"node:path\";\nimport { getProviders, type OAuthProviderId, type OAuthSelectPrompt } from \"@asm-agent/ai\";\nimport type { OverlayHandle, TUI } from \"@asm-agent/tui\";\nimport { getAuthPath, getDocsPath } from \"../../config.js\";\nimport type { ModelRegistry } from \"../../core/model-registry.js\";\nimport {\n\tcheckPrimeInferenceAccess,\n\tfetchPrimeTeams,\n\tloadPrimeCliConfig,\n\tloginPrimeInference,\n\tPRIME_INFERENCE_PROVIDER_ID,\n\tPRIME_INFERENCE_PROVIDER_NAME,\n\ttype PrimeTeam,\n} from \"../../core/prime-inference-auth.js\";\nimport { BUILT_IN_PROVIDER_DISPLAY_NAMES } from \"../../core/provider-display-names.js\";\nimport { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from \"../../core/websearch-credential.js\";\nimport { showFullPaneOverlay } from \"./components/centered-overlay.js\";\nimport { ExtensionSelectorComponent } from \"./components/extension-selector.js\";\nimport { LoginDialogComponent } from \"./components/login-dialog.js\";\nimport {\n\ttype AuthSelectorCategory,\n\ttype AuthSelectorProvider,\n\tcompareAuthSelectorProviders,\n\tOAuthSelectorComponent,\n} from \"./components/oauth-selector.js\";\nimport { PrimeTeamSelectorComponent } from \"./components/prime-team-selector.js\";\nimport { theme } from \"./theme/theme.js\";\n\nexport type AuthenticationResult =\n\t| {\n\t\t\tstatus: \"success\";\n\t\t\tproviderId: string;\n\t\t\tproviderName: string;\n\t\t\tauthType: \"oauth\" | \"api_key\";\n\t\t\t/** \"service\" credentials (e.g. web search) don't affect model selection. */\n\t\t\tkind?: \"provider\" | \"service\";\n\t }\n\t| { status: \"cancelled\" }\n\t| { status: \"failed\" };\n\nexport const BEDROCK_PROVIDER_ID = \"amazon-bedrock\";\nexport const ASM_AGENT_LOGIN_PROVIDER_IDS = new Set([\"openai-codex\", \"anthropic\"]);\n\nexport const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING =\n\t\"Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage.\";\n\nfunction isAnthropicSubscriptionAuthKey(apiKey: string | undefined): boolean {\n\treturn typeof apiKey === \"string\" && apiKey.startsWith(\"sk-ant-oat\");\n}\n\nexport async function getAnthropicSubscriptionAuthWarning(\n\tmodelRegistry: ModelRegistry,\n\tmodel: { provider: string } | undefined,\n): Promise<string | undefined> {\n\tif (!model || model.provider !== \"anthropic\") {\n\t\treturn undefined;\n\t}\n\n\tconst storedCredential = modelRegistry.authStorage.get(\"anthropic\");\n\tif (storedCredential?.type === \"oauth\") {\n\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t}\n\n\ttry {\n\t\tconst apiKey = await modelRegistry.getApiKeyForProvider(model.provider);\n\t\tif (isAnthropicSubscriptionAuthKey(apiKey)) {\n\t\t\treturn ANTHROPIC_SUBSCRIPTION_AUTH_WARNING;\n\t\t}\n\t} catch {\n\t\t// Ignore auth lookup failures for warning-only checks.\n\t}\n\treturn undefined;\n}\n\nconst BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());\n\nexport function isApiKeyLoginProvider(\n\tproviderId: string,\n\toauthProviderIds: ReadonlySet<string>,\n\tbuiltInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,\n): boolean {\n\tif (BUILT_IN_PROVIDER_DISPLAY_NAMES[providerId]) {\n\t\treturn true;\n\t}\n\tif (builtInProviderIds.has(providerId)) {\n\t\treturn false;\n\t}\n\treturn !oauthProviderIds.has(providerId);\n}\n\nexport interface ProviderAuthFlowsHost {\n\treadonly ui: TUI;\n\treadonly modelRegistry: ModelRegistry;\n\tshowStatus(message: string): void;\n\tshowError(message: string): void;\n\t/** Models currently visible to the host; used to detect providers configured via external credentials. */\n\tgetAvailableModels(): Promise<ReadonlyArray<{ provider: string }>>;\n\t/** Invoked after stored credentials change so the host can refresh dependent UI. */\n\tonAuthChanged?(): void | Promise<void>;\n\t/** Invoked after a successful login (e.g. to surface billing warnings). */\n\tonLoginCompleted?(): void;\n}\n\nexport interface ProviderLoginOptions {\n\tauthType?: \"oauth\" | \"api_key\";\n\tinitialCategory?: AuthSelectorCategory;\n}\n\n/** Shared auth dialogs: host-specific refresh and billing effects remain outside the flow. */\nexport class ProviderAuthFlows {\n\tconstructor(private readonly host: ProviderAuthFlowsHost) {}\n\n\t/**\n\t * Run the OAuth login flow for an MCP integration server.\n\t *\n\t * The provider must already be registered (the McpManager does this as\n\t * `mcp:<server>`). On success the credentials land in auth.json and the\n\t * caller should reload resources so the integration's skill enables.\n\t */\n\trunMcpLogin(server: string, label?: string): Promise<AuthenticationResult> {\n\t\tconst providerId = `mcp:${server}`;\n\t\tconst provider = this.host.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);\n\t\tif (!provider) {\n\t\t\tthis.host.showError(`Unknown MCP integration: ${server}`);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\t\treturn this.showLoginDialog(providerId, label ?? provider.name, \"service\");\n\t}\n\n\trunLogin(options: ProviderLoginOptions = {}): Promise<AuthenticationResult> {\n\t\tconst { authType, initialCategory } = options;\n\t\tconst providerOptions = this.getLoginProviderOptions(authType);\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\tauthType === \"oauth\"\n\t\t\t\t\t? \"No subscription providers available.\"\n\t\t\t\t\t: authType === \"api_key\"\n\t\t\t\t\t\t? \"No API key providers available.\"\n\t\t\t\t\t\t: \"No providers available.\",\n\t\t\t);\n\t\t\treturn Promise.resolve({ status: \"failed\" });\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"login\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(await this.loginProvider(providerOption));\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve({ status: \"cancelled\" });\n\t\t\t\t},\n\t\t\t\t(providerId) => this.host.modelRegistry.getProviderAuthStatus(providerId),\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows, initialCategory },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tloginProvider(providerOption: AuthSelectorProvider): Promise<AuthenticationResult> {\n\t\tconst kind = providerOption.category === \"service\" ? \"service\" : \"provider\";\n\t\tif (providerOption.authType === \"oauth\") {\n\t\t\treturn this.showLoginDialog(providerOption.id, providerOption.name, kind);\n\t\t}\n\t\tif (providerOption.id === PRIME_INFERENCE_PROVIDER_ID) {\n\t\t\treturn this.runPrimeInferenceLogin();\n\t\t}\n\t\tif (providerOption.id === BEDROCK_PROVIDER_ID) {\n\t\t\treturn this.showBedrockSetupDialog(providerOption.id, providerOption.name);\n\t\t}\n\t\treturn this.showApiKeyLoginDialog(providerOption.id, providerOption.name, kind);\n\t}\n\n\trunLogout(): Promise<string | null> {\n\t\tconst providerOptions = this.getLogoutProviderOptions();\n\t\tif (providerOptions.length === 0) {\n\t\t\tthis.host.showStatus(\n\t\t\t\t\"No stored credentials to remove. /logout only removes credentials saved by /login; environment variables and models.json config are unchanged.\",\n\t\t\t);\n\t\t\treturn Promise.resolve(null);\n\t\t}\n\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new OAuthSelectorComponent(\n\t\t\t\t\"logout\",\n\t\t\t\tthis.host.modelRegistry.authStorage,\n\t\t\t\tproviderOptions,\n\t\t\t\tasync (providerOption: AuthSelectorProvider) => {\n\t\t\t\t\tclose();\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tthis.host.modelRegistry.authStorage.logout(providerOption.id);\n\t\t\t\t\t\tthis.host.modelRegistry.refresh();\n\t\t\t\t\t\tawait this.host.onAuthChanged?.();\n\t\t\t\t\t\tconst message =\n\t\t\t\t\t\t\tproviderOption.authType === \"oauth\"\n\t\t\t\t\t\t\t\t? `Logged out of ${providerOption.name}`\n\t\t\t\t\t\t\t\t: `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;\n\t\t\t\t\t\tthis.host.showStatus(message);\n\t\t\t\t\t\tresolve(providerOption.id);\n\t\t\t\t\t} catch (error: unknown) {\n\t\t\t\t\t\tthis.host.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);\n\t\t\t\t\t\tresolve(null);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(null);\n\t\t\t\t},\n\t\t\t\tundefined,\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tgetLoginProviderOptions(authType?: \"oauth\" | \"api_key\"): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tif (authType === \"api_key\") {\n\t\t\treturn [];\n\t\t}\n\t\tconst oauthProviders = authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.filter((provider) => ASM_AGENT_LOGIN_PROVIDER_IDS.has(provider.id));\n\t\tconst options: AuthSelectorProvider[] = oauthProviders.map((provider) => ({\n\t\t\tid: provider.id,\n\t\t\tname: provider.name,\n\t\t\tauthType: \"oauth\",\n\t\t}));\n\n\t\treturn options.sort(compareAuthSelectorProviders);\n\t}\n\n\tprivate getLogoutProviderOptions(): AuthSelectorProvider[] {\n\t\tconst authStorage = this.host.modelRegistry.authStorage;\n\t\tconst options: AuthSelectorProvider[] = [];\n\n\t\tconst oauthProvidersById = new Map(authStorage.getOAuthProviders().map((p) => [p.id, p]));\n\t\tfor (const providerId of authStorage.list()) {\n\t\t\tconst credential = authStorage.get(providerId);\n\t\t\tif (!credential) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst isSerper = providerId === SERPER_CREDENTIAL_ID;\n\t\t\tconst isMcp = providerId.startsWith(\"mcp:\");\n\t\t\tconst name = isSerper\n\t\t\t\t? SERPER_CREDENTIAL_NAME\n\t\t\t\t: isMcp\n\t\t\t\t\t? (oauthProvidersById.get(providerId)?.name ?? providerId.slice(\"mcp:\".length))\n\t\t\t\t\t: this.host.modelRegistry.getProviderDisplayName(providerId);\n\t\t\toptions.push({\n\t\t\t\tid: providerId,\n\t\t\t\tname,\n\t\t\t\tauthType: credential.type,\n\t\t\t\tcategory: isSerper || isMcp ? \"service\" : \"provider\",\n\t\t\t});\n\t\t}\n\n\t\tif (!options.some((option) => option.id === PRIME_INFERENCE_PROVIDER_ID)) {\n\t\t\tconst primeInferenceStatus = authStorage.getAuthStatus(PRIME_INFERENCE_PROVIDER_ID);\n\t\t\tif (primeInferenceStatus.source === \"prime_cli\") {\n\t\t\t\toptions.push({\n\t\t\t\t\tid: PRIME_INFERENCE_PROVIDER_ID,\n\t\t\t\t\tname: PRIME_INFERENCE_PROVIDER_NAME,\n\t\t\t\t\tauthType: \"api_key\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn options.sort((a, b) => a.name.localeCompare(b.name));\n\t}\n\n\tprivate async completeProviderAuthentication(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tauthType: \"oauth\" | \"api_key\",\n\t\tstatusSuffix?: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t\tcredentialPath: string = getAuthPath(),\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\n\t\tconst actionLabel = authType === \"oauth\" ? `Logged in to ${providerName}` : `Saved API key for ${providerName}`;\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(\n\t\t\t`${actionLabel}. Credentials saved to ${credentialPath}${statusSuffix ? `. ${statusSuffix}` : \"\"}`,\n\t\t);\n\t\tthis.host.onLoginCompleted?.();\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType,\n\t\t\tkind,\n\t\t};\n\t}\n\n\tprivate async completeExternalProviderSetup(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.refresh();\n\t\tawait this.host.onAuthChanged?.();\n\t\tthis.host.showStatus(`${providerName} uses external credentials. Select a model after configuring them.`);\n\t\treturn {\n\t\t\tstatus: \"success\",\n\t\t\tproviderId,\n\t\t\tproviderName,\n\t\t\tauthType: \"api_key\",\n\t\t};\n\t}\n\n\tprivate async hasAvailableProviderModels(providerId: string): Promise<boolean> {\n\t\tconst models = await this.host.getAvailableModels();\n\t\treturn models.some((model) => model.provider === providerId);\n\t}\n\n\tprivate async showBedrockSetupDialog(providerId: string, providerName: string): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, () => {}, providerName, \"Amazon Bedrock setup\");\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait dialog.showContinueInfo([\n\t\t\t\ttheme.fg(\"text\", \"Amazon Bedrock uses AWS credentials instead of a single API key.\"),\n\t\t\t\ttheme.fg(\"text\", \"Configure an AWS profile, IAM keys, bearer token, or role-based credentials.\"),\n\t\t\t\ttheme.fg(\"muted\", \"See:\"),\n\t\t\t\ttheme.fg(\"accent\", ` ${path.join(getDocsPath(), \"providers.md\")}`),\n\t\t\t]);\n\t\t\tcloseDialog();\n\t\t\tif (!(await this.hasAvailableProviderModels(providerId))) {\n\t\t\t\tthis.host.showStatus(`${providerName} credentials were not detected. Configure them, then reopen /model.`);\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\t\t\treturn await this.completeExternalProviderSetup(providerId, providerName);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to set up ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showPrimeTeamSelector(\n\t\tteams: PrimeTeam[],\n\t\tcurrentTeamId: string | undefined,\n\t): Promise<PrimeTeam | null | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tlet handle: OverlayHandle | undefined;\n\t\t\tconst close = () => {\n\t\t\t\thandle?.hide();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst selector = new PrimeTeamSelectorComponent(\n\t\t\t\tteams,\n\t\t\t\tcurrentTeamId,\n\t\t\t\t(team) => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(team);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\thandle = showFullPaneOverlay(this.host.ui, selector, 78);\n\t\t});\n\t}\n\n\tprivate getPrimeInferenceDefaultTeamStatus(): string {\n\t\tconst configPath = this.host.modelRegistry.authStorage.getPrimeCliConfigPath();\n\t\tif (configPath) {\n\t\t\tlet config: ReturnType<typeof loadPrimeCliConfig>;\n\t\t\ttry {\n\t\t\t\tconfig = loadPrimeCliConfig(configPath);\n\t\t\t} catch {\n\t\t\t\treturn \"Using personal account.\";\n\t\t\t}\n\t\t\tif (config.teamIdFromEnv) {\n\t\t\t\treturn \"Using team from PRIME_TEAM_ID.\";\n\t\t\t}\n\t\t\tif (config.teamName) {\n\t\t\t\treturn `Using team \"${config.teamName}\".`;\n\t\t\t}\n\t\t\tif (config.teamId) {\n\t\t\t\treturn \"Using Prime CLI team.\";\n\t\t\t}\n\t\t}\n\t\tconst storedTeam = this.host.modelRegistry.authStorage.getPrimeInferenceTeamSelection();\n\t\tif (storedTeam) {\n\t\t\treturn `Using team \"${storedTeam.name}\".`;\n\t\t}\n\t\tif (storedTeam === null) {\n\t\t\treturn \"Using personal account.\";\n\t\t}\n\t\treturn \"Using personal account.\";\n\t}\n\n\tprivate async selectPrimeInferenceTeam(apiKey: string, dialog: LoginDialogComponent): Promise<string | undefined> {\n\t\ttry {\n\t\t\tconst config = loadPrimeCliConfig(this.host.modelRegistry.authStorage.getPrimeCliConfigPath());\n\t\t\tif (config.teamIdFromEnv) {\n\t\t\t\tthis.host.modelRegistry.authStorage.reload();\n\t\t\t\treturn \"Using team from PRIME_TEAM_ID.\";\n\t\t\t}\n\n\t\t\tdialog.showProgress(\"Loading Prime teams...\");\n\t\t\tconst teams = await fetchPrimeTeams(apiKey, config.baseUrl, { signal: dialog.signal });\n\t\t\tif (dialog.signal.aborted) {\n\t\t\t\treturn this.getPrimeInferenceDefaultTeamStatus();\n\t\t\t}\n\t\t\tif (teams.length === 0) {\n\t\t\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceTeamSelection(null);\n\t\t\t\treturn \"Using personal account.\";\n\t\t\t}\n\n\t\t\tconst storedTeam = this.host.modelRegistry.authStorage.getPrimeInferenceTeamSelection();\n\t\t\tconst currentTeamId = storedTeam === null ? undefined : (storedTeam?.teamId ?? config.teamId);\n\t\t\tconst selectedTeam = await this.showPrimeTeamSelector(teams, currentTeamId);\n\t\t\tif (selectedTeam !== undefined) {\n\t\t\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceTeamSelection(selectedTeam);\n\t\t\t}\n\t\t\treturn selectedTeam\n\t\t\t\t? `Using team \"${selectedTeam.name}\".`\n\t\t\t\t: selectedTeam === null\n\t\t\t\t\t? \"Using personal account.\"\n\t\t\t\t\t: this.getPrimeInferenceDefaultTeamStatus();\n\t\t} catch {\n\t\t\tthis.host.modelRegistry.authStorage.reload();\n\t\t\treturn this.getPrimeInferenceDefaultTeamStatus();\n\t\t}\n\t}\n\n\tprivate async completePrimeInferenceLogin(\n\t\tapiKey: string,\n\t\tdialog: LoginDialogComponent,\n\t\tcloseDialog: () => void,\n\t): Promise<AuthenticationResult> {\n\t\tthis.host.modelRegistry.authStorage.setPrimeInferenceApiKey(apiKey);\n\t\tconst teamStatus = await this.selectPrimeInferenceTeam(apiKey, dialog);\n\n\t\tcloseDialog();\n\t\treturn await this.completeProviderAuthentication(\n\t\t\tPRIME_INFERENCE_PROVIDER_ID,\n\t\t\tPRIME_INFERENCE_PROVIDER_NAME,\n\t\t\t\"api_key\",\n\t\t\tteamStatus,\n\t\t\t\"provider\",\n\t\t\tthis.host.modelRegistry.authStorage.getPrimeCliConfigPath() ?? getAuthPath(),\n\t\t);\n\t}\n\n\tasync runPrimeInferenceLogin(): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(\n\t\t\tthis.host.ui,\n\t\t\tPRIME_INFERENCE_PROVIDER_ID,\n\t\t\t(_success, _message) => {},\n\t\t\tPRIME_INFERENCE_PROVIDER_NAME,\n\t\t);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\t// The browser challenge gets its own controller so a manually pasted key\n\t\t// can stop the polling without tearing down the dialog.\n\t\tconst browserAbort = new AbortController();\n\t\tconst onDialogAbort = () => browserAbort.abort();\n\t\tdialog.signal.addEventListener(\"abort\", onDialogAbort, { once: true });\n\n\t\tlet manualInputArmed = false;\n\t\tlet resolveManualKey: (entry: { apiKey: string; source: \"manual\" }) => void = () => {};\n\t\tconst manualKeyEntry = new Promise<{ apiKey: string; source: \"manual\" }>((resolve) => {\n\t\t\tresolveManualKey = resolve;\n\t\t});\n\t\tconst armManualInput = (prompt: string): void => {\n\t\t\tmanualInputArmed = true;\n\t\t\tvoid (async () => {\n\t\t\t\tlet value = (await dialog.showManualInput(prompt)).trim();\n\t\t\t\twhile (!value) {\n\t\t\t\t\tvalue = (await dialog.waitForInput()).trim();\n\t\t\t\t}\n\t\t\t\tresolveManualKey({ apiKey: value, source: \"manual\" });\n\t\t\t})().catch(() => {\n\t\t\t\t// Cancellation surfaces through the dialog signal.\n\t\t\t});\n\t\t};\n\n\t\ttry {\n\t\t\tconst browserLogin = loginPrimeInference(\n\t\t\t\t{\n\t\t\t\t\tonAuth: (info) => {\n\t\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\t\t\t\t\t\tarmManualInput(\"Complete the sign-in in your browser, or paste an API key below:\");\n\t\t\t\t\t},\n\t\t\t\t\tonProgress: (message) => {\n\t\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t\t},\n\t\t\t\t\tsignal: browserAbort.signal,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tconfigPath: this.host.modelRegistry.authStorage.getPrimeCliConfigPath(),\n\t\t\t\t},\n\t\t\t);\n\t\t\t// When the browser challenge cannot start or breaks down, keep the dialog\n\t\t\t// open and fall back to plain API key entry instead of failing outright.\n\t\t\tconst browserLoginOrFallback = browserLogin.catch((error: unknown) => {\n\t\t\t\tif (browserAbort.signal.aborted) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\t\tdialog.showProgress(`Browser sign-in unavailable (${errorMsg}).`);\n\t\t\t\tif (!manualInputArmed) {\n\t\t\t\t\tarmManualInput(\"Paste a Prime API key below:\");\n\t\t\t\t}\n\t\t\t\treturn manualKeyEntry;\n\t\t\t});\n\t\t\t// Once the browser flow has settled into manual fallback, nothing above\n\t\t\t// rejects on cancel anymore, so the dialog signal must end the race too.\n\t\t\tconst dialogCancelled = new Promise<never>((_, reject) => {\n\t\t\t\tdialog.signal.addEventListener(\"abort\", () => reject(new Error(\"Login cancelled\")), { once: true });\n\t\t\t});\n\t\t\t// Promise.race observes the rejections below, but keep dedicated handlers\n\t\t\t// so neither an aborted browser flow nor a cancelled dialog can surface\n\t\t\t// as an unhandled rejection.\n\t\t\tbrowserLoginOrFallback.catch(() => {});\n\t\t\tdialogCancelled.catch(() => {});\n\n\t\t\tconst result = await Promise.race([browserLoginOrFallback, manualKeyEntry, dialogCancelled]);\n\t\t\tif (dialog.signal.aborted) {\n\t\t\t\tcloseDialog();\n\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t}\n\n\t\t\tif (result.source === \"manual\") {\n\t\t\t\tbrowserAbort.abort();\n\t\t\t\tdialog.showProgress(\"Checking Prime Inference access...\");\n\t\t\t\tconst config = loadPrimeCliConfig(this.host.modelRegistry.authStorage.getPrimeCliConfigPath());\n\t\t\t\tconst access = await checkPrimeInferenceAccess(result.apiKey, config.baseUrl, { signal: dialog.signal });\n\t\t\t\tif (dialog.signal.aborted) {\n\t\t\t\t\tcloseDialog();\n\t\t\t\t\treturn { status: \"cancelled\" };\n\t\t\t\t}\n\t\t\t\tif (!access.ok) {\n\t\t\t\t\tconst status = access.status === undefined ? \"\" : `HTTP ${access.status}: `;\n\t\t\t\t\tthrow new Error(`Prime API key does not have Prime Inference access (${status}${access.message})`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn await this.completePrimeInferenceLogin(result.apiKey, dialog, closeDialog);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (!dialog.signal.aborted && errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${PRIME_INFERENCE_PROVIDER_NAME}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t} finally {\n\t\t\tdialog.signal.removeEventListener(\"abort\", onDialogAbort);\n\t\t}\n\t}\n\n\tprivate async showApiKeyLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst handle = showFullPaneOverlay(this.host.ui, dialog, 88);\n\n\t\tconst closeDialog = () => {\n\t\t\thandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tconst apiKey = (await dialog.showPrompt(\"Enter API key:\")).trim();\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(\"API key cannot be empty.\");\n\t\t\t}\n\n\t\t\tthis.host.modelRegistry.authStorage.set(providerId, { type: \"api_key\", key: apiKey });\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"api_key\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n\n\tprivate showOAuthLoginSelect(dialogHandle: OverlayHandle, prompt: OAuthSelectPrompt): Promise<string | undefined> {\n\t\treturn new Promise((resolve) => {\n\t\t\tdialogHandle.setHidden(true);\n\t\t\tlet selectorHandle: OverlayHandle | undefined;\n\t\t\tconst restoreDialog = () => {\n\t\t\t\tselectorHandle?.hide();\n\t\t\t\tdialogHandle.setHidden(false);\n\t\t\t\tdialogHandle.focus();\n\t\t\t\tthis.host.ui.requestRender();\n\t\t\t};\n\t\t\tconst labels = prompt.options.map((option) => option.label);\n\t\t\tconst selector = new ExtensionSelectorComponent(\n\t\t\t\tprompt.message,\n\t\t\t\tlabels,\n\t\t\t\t(optionLabel) => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(prompt.options.find((option) => option.label === optionLabel)?.id);\n\t\t\t\t},\n\t\t\t\t() => {\n\t\t\t\t\trestoreDialog();\n\t\t\t\t\tresolve(undefined);\n\t\t\t\t},\n\t\t\t\t{ getRows: () => this.host.ui.terminal.rows },\n\t\t\t);\n\t\t\tselectorHandle = showFullPaneOverlay(this.host.ui, selector, 76);\n\t\t});\n\t}\n\n\tprivate async showLoginDialog(\n\t\tproviderId: string,\n\t\tproviderName: string,\n\t\tkind: \"provider\" | \"service\" = \"provider\",\n\t): Promise<AuthenticationResult> {\n\t\tconst providerInfo = this.host.modelRegistry.authStorage\n\t\t\t.getOAuthProviders()\n\t\t\t.find((provider) => provider.id === providerId);\n\n\t\tconst usesCallbackServer = providerInfo?.usesCallbackServer ?? false;\n\n\t\tconst dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => {}, providerName);\n\n\t\tconst dialogHandle = showFullPaneOverlay(this.host.ui, dialog, {\n\t\t\tmaxContentWidth: 88,\n\t\t\tsuspendFullscreenMouse: true,\n\t\t});\n\n\t\tlet manualCodeResolve: ((code: string) => void) | undefined;\n\t\tlet manualCodeReject: ((err: Error) => void) | undefined;\n\t\tconst manualCodePromise = new Promise<string>((resolve, reject) => {\n\t\t\tmanualCodeResolve = resolve;\n\t\t\tmanualCodeReject = reject;\n\t\t});\n\n\t\tconst closeDialog = () => {\n\t\t\tdialogHandle.hide();\n\t\t\tthis.host.ui.requestRender();\n\t\t};\n\n\t\ttry {\n\t\t\tawait this.host.modelRegistry.authStorage.login(providerId as OAuthProviderId, {\n\t\t\t\tonAuth: (info: { url: string; instructions?: string }) => {\n\t\t\t\t\tdialog.showAuth(info.url, info.instructions);\n\n\t\t\t\t\tif (usesCallbackServer) {\n\t\t\t\t\t\tdialog\n\t\t\t\t\t\t\t.showManualInput(\"Paste redirect URL below, or complete login in browser:\")\n\t\t\t\t\t\t\t.then((value) => {\n\t\t\t\t\t\t\t\tif (value && manualCodeResolve) {\n\t\t\t\t\t\t\t\t\tmanualCodeResolve(value);\n\t\t\t\t\t\t\t\t\tmanualCodeResolve = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.catch(() => {\n\t\t\t\t\t\t\t\tif (manualCodeReject) {\n\t\t\t\t\t\t\t\t\tmanualCodeReject(new Error(\"Login cancelled\"));\n\t\t\t\t\t\t\t\t\tmanualCodeReject = undefined;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t} else if (providerId === \"github-copilot\") {\n\t\t\t\t\t\tdialog.showWaiting(\"Waiting for browser authentication...\");\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tonPrompt: async (prompt: { message: string; placeholder?: string }) => {\n\t\t\t\t\treturn dialog.showPrompt(prompt.message, prompt.placeholder);\n\t\t\t\t},\n\n\t\t\t\tonProgress: (message: string) => {\n\t\t\t\t\tdialog.showProgress(message);\n\t\t\t\t},\n\n\t\t\t\tonSelect: (prompt: OAuthSelectPrompt) => this.showOAuthLoginSelect(dialogHandle, prompt),\n\n\t\t\t\tonManualCodeInput: () => manualCodePromise,\n\n\t\t\t\tsignal: dialog.signal,\n\t\t\t});\n\n\t\t\tcloseDialog();\n\t\t\treturn await this.completeProviderAuthentication(providerId, providerName, \"oauth\", undefined, kind);\n\t\t} catch (error: unknown) {\n\t\t\tcloseDialog();\n\t\t\tconst errorMsg = error instanceof Error ? error.message : String(error);\n\t\t\tif (errorMsg !== \"Login cancelled\") {\n\t\t\t\tthis.host.showError(`Failed to login to ${providerName}: ${errorMsg}`);\n\t\t\t\treturn { status: \"failed\" };\n\t\t\t}\n\t\t\treturn { status: \"cancelled\" };\n\t\t}\n\t}\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  import * as path from "node:path";
2
2
  import { getProviders } from "@asm-agent/ai";
3
3
  import { getAuthPath, getDocsPath } from "../../config.js";
4
- import { ASM_AGENT_TRACES_PROVIDER_ID, ASM_AGENT_TRACES_PROVIDER_NAME, checkAsmAgentTracesAccess, checkPrimeInferenceAccess, fetchPrimeTeams, loadPrimeCliConfig, loginAsmAgentTraces, loginPrimeInference, PRIME_INFERENCE_PROVIDER_ID, PRIME_INFERENCE_PROVIDER_NAME, resolveAsmAgentTracesBaseUrl, } from "../../core/prime-inference-auth.js";
4
+ import { checkPrimeInferenceAccess, fetchPrimeTeams, loadPrimeCliConfig, loginPrimeInference, PRIME_INFERENCE_PROVIDER_ID, PRIME_INFERENCE_PROVIDER_NAME, } from "../../core/prime-inference-auth.js";
5
5
  import { BUILT_IN_PROVIDER_DISPLAY_NAMES } from "../../core/provider-display-names.js";
6
6
  import { SERPER_CREDENTIAL_ID, SERPER_CREDENTIAL_NAME } from "../../core/websearch-credential.js";
7
7
  import { showFullPaneOverlay } from "./components/centered-overlay.js";
@@ -337,14 +337,6 @@ export class ProviderAuthFlows {
337
337
  closeDialog();
338
338
  return await this.completeProviderAuthentication(PRIME_INFERENCE_PROVIDER_ID, PRIME_INFERENCE_PROVIDER_NAME, "api_key", teamStatus, "provider", this.host.modelRegistry.authStorage.getPrimeCliConfigPath() ?? getAuthPath());
339
339
  }
340
- async completeAsmAgentTracesLogin(apiKey, closeDialog) {
341
- this.host.modelRegistry.authStorage.set(ASM_AGENT_TRACES_PROVIDER_ID, {
342
- type: "api_key",
343
- key: apiKey,
344
- });
345
- closeDialog();
346
- return await this.completeProviderAuthentication(ASM_AGENT_TRACES_PROVIDER_ID, ASM_AGENT_TRACES_PROVIDER_NAME, "api_key");
347
- }
348
340
  async runPrimeInferenceLogin() {
349
341
  const dialog = new LoginDialogComponent(this.host.ui, PRIME_INFERENCE_PROVIDER_ID, (_success, _message) => { }, PRIME_INFERENCE_PROVIDER_NAME);
350
342
  const handle = showFullPaneOverlay(this.host.ui, dialog, {
@@ -447,98 +439,6 @@ export class ProviderAuthFlows {
447
439
  dialog.signal.removeEventListener("abort", onDialogAbort);
448
440
  }
449
441
  }
450
- async runAsmAgentTracesLogin() {
451
- const dialog = new LoginDialogComponent(this.host.ui, ASM_AGENT_TRACES_PROVIDER_ID, (_success, _message) => { }, ASM_AGENT_TRACES_PROVIDER_NAME);
452
- const handle = showFullPaneOverlay(this.host.ui, dialog, {
453
- maxContentWidth: 88,
454
- suspendFullscreenMouse: true,
455
- });
456
- const closeDialog = () => {
457
- handle.hide();
458
- this.host.ui.requestRender();
459
- };
460
- const browserAbort = new AbortController();
461
- const onDialogAbort = () => browserAbort.abort();
462
- dialog.signal.addEventListener("abort", onDialogAbort, { once: true });
463
- let manualInputArmed = false;
464
- let resolveManualKey = () => { };
465
- const manualKeyEntry = new Promise((resolve) => {
466
- resolveManualKey = resolve;
467
- });
468
- const armManualInput = (prompt) => {
469
- manualInputArmed = true;
470
- void (async () => {
471
- let value = (await dialog.showManualInput(prompt)).trim();
472
- while (!value) {
473
- value = (await dialog.waitForInput()).trim();
474
- }
475
- resolveManualKey({ apiKey: value, source: "manual" });
476
- })().catch(() => {
477
- // Cancellation surfaces through the dialog signal.
478
- });
479
- };
480
- try {
481
- const browserLogin = loginAsmAgentTraces({
482
- onAuth: (info) => {
483
- dialog.showAuth(info.url, info.instructions);
484
- armManualInput("Complete the sign-in in your browser, or paste a Prime API key below:");
485
- },
486
- onProgress: (message) => {
487
- dialog.showProgress(message);
488
- },
489
- signal: browserAbort.signal,
490
- });
491
- const browserLoginOrFallback = browserLogin.catch((error) => {
492
- if (browserAbort.signal.aborted) {
493
- throw error;
494
- }
495
- const errorMsg = error instanceof Error ? error.message : String(error);
496
- dialog.showProgress(`Browser sign-in unavailable (${errorMsg}).`);
497
- if (!manualInputArmed) {
498
- armManualInput("Paste a Prime API key below:");
499
- }
500
- return manualKeyEntry;
501
- });
502
- const dialogCancelled = new Promise((_, reject) => {
503
- dialog.signal.addEventListener("abort", () => reject(new Error("Login cancelled")), { once: true });
504
- });
505
- browserLoginOrFallback.catch(() => { });
506
- dialogCancelled.catch(() => { });
507
- const result = await Promise.race([browserLoginOrFallback, manualKeyEntry, dialogCancelled]);
508
- if (dialog.signal.aborted) {
509
- closeDialog();
510
- return { status: "cancelled" };
511
- }
512
- if (result.source === "manual") {
513
- browserAbort.abort();
514
- dialog.showProgress("Checking ASM Agent trace access...");
515
- const access = await checkAsmAgentTracesAccess(result.apiKey, resolveAsmAgentTracesBaseUrl(), {
516
- signal: dialog.signal,
517
- });
518
- if (dialog.signal.aborted) {
519
- closeDialog();
520
- return { status: "cancelled" };
521
- }
522
- if (!access.ok) {
523
- const status = access.status === undefined ? "" : `HTTP ${access.status}: `;
524
- throw new Error(`Prime API key does not have ASM Agent trace access (${status}${access.message})`);
525
- }
526
- }
527
- return await this.completeAsmAgentTracesLogin(result.apiKey, closeDialog);
528
- }
529
- catch (error) {
530
- closeDialog();
531
- const errorMsg = error instanceof Error ? error.message : String(error);
532
- if (!dialog.signal.aborted && errorMsg !== "Login cancelled") {
533
- this.host.showError(`Failed to login to ${ASM_AGENT_TRACES_PROVIDER_NAME}: ${errorMsg}`);
534
- return { status: "failed" };
535
- }
536
- return { status: "cancelled" };
537
- }
538
- finally {
539
- dialog.signal.removeEventListener("abort", onDialogAbort);
540
- }
541
- }
542
442
  async showApiKeyLoginDialog(providerId, providerName, kind = "provider") {
543
443
  const dialog = new LoginDialogComponent(this.host.ui, providerId, (_success, _message) => { }, providerName);
544
444
  const handle = showFullPaneOverlay(this.host.ui, dialog, 88);