comisai 1.0.34 → 1.0.36

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 (284) hide show
  1. package/node_modules/@comis/agent/dist/background/auto-background-middleware.d.ts +11 -1
  2. package/node_modules/@comis/agent/dist/background/auto-background-middleware.js +21 -4
  3. package/node_modules/@comis/agent/dist/background/background-task-manager.d.ts +2 -2
  4. package/node_modules/@comis/agent/dist/background/background-task-manager.js +61 -20
  5. package/node_modules/@comis/agent/dist/background/background-task-persistence.js +10 -3
  6. package/node_modules/@comis/agent/dist/background/background-task-types.d.ts +10 -3
  7. package/node_modules/@comis/agent/dist/background/background-task-types.js +1 -1
  8. package/node_modules/@comis/agent/dist/background/completion-formatter.d.ts +39 -0
  9. package/node_modules/@comis/agent/dist/background/completion-formatter.js +77 -0
  10. package/node_modules/@comis/agent/dist/background/completion-runner.d.ts +53 -0
  11. package/node_modules/@comis/agent/dist/background/completion-runner.js +151 -0
  12. package/node_modules/@comis/agent/dist/background/index.d.ts +4 -0
  13. package/node_modules/@comis/agent/dist/background/index.js +2 -0
  14. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +17 -2
  15. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +14 -2
  16. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +23 -23
  17. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +72 -60
  18. package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.d.ts +6 -7
  19. package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.js +24 -25
  20. package/node_modules/@comis/agent/dist/budget/cost-tracker.d.ts +1 -1
  21. package/node_modules/@comis/agent/dist/context-engine/constants.d.ts +5 -5
  22. package/node_modules/@comis/agent/dist/context-engine/constants.js +12 -12
  23. package/node_modules/@comis/agent/dist/context-engine/context-engine.js +13 -4
  24. package/node_modules/@comis/agent/dist/context-engine/dag-annotator.d.ts +1 -2
  25. package/node_modules/@comis/agent/dist/context-engine/dag-annotator.js +1 -2
  26. package/node_modules/@comis/agent/dist/context-engine/llm-compaction.js +20 -16
  27. package/node_modules/@comis/agent/dist/context-engine/rehydration.js +6 -6
  28. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +12 -12
  29. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +36 -22
  30. package/node_modules/@comis/agent/dist/context-engine/types-core.d.ts +15 -0
  31. package/node_modules/@comis/agent/dist/executor/cache-break-detection.d.ts +6 -6
  32. package/node_modules/@comis/agent/dist/executor/cache-break-detection.js +8 -8
  33. package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.d.ts +16 -0
  34. package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.js +46 -5
  35. package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +30 -0
  36. package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +17 -1
  37. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +1 -1
  38. package/node_modules/@comis/agent/dist/executor/executor-response-filter.d.ts +7 -6
  39. package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +9 -42
  40. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +2 -3
  41. package/node_modules/@comis/agent/dist/executor/gemini-cache-injector.d.ts +2 -2
  42. package/node_modules/@comis/agent/dist/executor/gemini-cache-injector.js +4 -4
  43. package/node_modules/@comis/agent/dist/executor/phase-filter.d.ts +2 -2
  44. package/node_modules/@comis/agent/dist/executor/phase-filter.js +5 -7
  45. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +13 -0
  46. package/node_modules/@comis/agent/dist/executor/pi-executor.js +71 -6
  47. package/node_modules/@comis/agent/dist/executor/post-batch-continuation.js +7 -7
  48. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.d.ts +1 -1
  49. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +1 -1
  50. package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +2 -2
  51. package/node_modules/@comis/agent/dist/executor/tool-deferral.js +7 -7
  52. package/node_modules/@comis/agent/dist/index.d.ts +17 -0
  53. package/node_modules/@comis/agent/dist/index.js +32 -11
  54. package/node_modules/@comis/agent/dist/model/auth-provider.d.ts +25 -2
  55. package/node_modules/@comis/agent/dist/model/auth-provider.js +6 -0
  56. package/node_modules/@comis/agent/dist/model/compaction-model-resolver.d.ts +3 -3
  57. package/node_modules/@comis/agent/dist/model/compaction-model-resolver.js +3 -3
  58. package/node_modules/@comis/agent/dist/model/oauth-credential-store-file.d.ts +37 -0
  59. package/node_modules/@comis/agent/dist/model/oauth-credential-store-file.js +279 -0
  60. package/node_modules/@comis/agent/dist/model/oauth-credential-store-selector.d.ts +49 -0
  61. package/node_modules/@comis/agent/dist/model/oauth-credential-store-selector.js +50 -0
  62. package/node_modules/@comis/agent/dist/model/oauth-device-code.d.ts +57 -0
  63. package/node_modules/@comis/agent/dist/model/oauth-device-code.js +302 -0
  64. package/node_modules/@comis/agent/dist/model/oauth-env.d.ts +33 -0
  65. package/node_modules/@comis/agent/dist/model/oauth-env.js +38 -0
  66. package/node_modules/@comis/agent/dist/model/oauth-errors.d.ts +41 -0
  67. package/node_modules/@comis/agent/dist/model/oauth-errors.js +88 -0
  68. package/node_modules/@comis/agent/dist/model/oauth-identity.d.ts +53 -0
  69. package/node_modules/@comis/agent/dist/model/oauth-identity.js +141 -0
  70. package/node_modules/@comis/agent/dist/model/oauth-login-runner.d.ts +99 -0
  71. package/node_modules/@comis/agent/dist/model/oauth-login-runner.js +374 -0
  72. package/node_modules/@comis/agent/dist/model/oauth-tls-preflight.d.ts +58 -0
  73. package/node_modules/@comis/agent/dist/model/oauth-tls-preflight.js +82 -0
  74. package/node_modules/@comis/agent/dist/model/oauth-token-manager.d.ts +86 -16
  75. package/node_modules/@comis/agent/dist/model/oauth-token-manager.js +961 -66
  76. package/node_modules/@comis/agent/dist/model/operation-model-defaults.d.ts +9 -4
  77. package/node_modules/@comis/agent/dist/model/operation-model-defaults.js +36 -9
  78. package/node_modules/@comis/agent/dist/model/resolve-provider-api-key.d.ts +48 -0
  79. package/node_modules/@comis/agent/dist/model/resolve-provider-api-key.js +66 -0
  80. package/node_modules/@comis/agent/dist/provider/capabilities.d.ts +5 -5
  81. package/node_modules/@comis/agent/dist/provider/capabilities.js +10 -23
  82. package/node_modules/@comis/agent/dist/safety/tool-output-safety.js +3 -3
  83. package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +1 -1
  84. package/node_modules/@comis/agent/dist/session/comis-session-manager.js +1 -1
  85. package/node_modules/@comis/agent/dist/spawn/narrative-caster.d.ts +10 -0
  86. package/node_modules/@comis/agent/dist/spawn/narrative-caster.js +5 -1
  87. package/node_modules/@comis/agent/package.json +1 -1
  88. package/node_modules/@comis/channels/dist/email/email-adapter.js +6 -6
  89. package/node_modules/@comis/channels/dist/email/imap-lifecycle.js +7 -7
  90. package/node_modules/@comis/channels/dist/shared/deliver-to-channel.js +12 -10
  91. package/node_modules/@comis/channels/dist/telegram/telegram-adapter.js +1 -1
  92. package/node_modules/@comis/channels/package.json +1 -1
  93. package/node_modules/@comis/cli/dist/cli.js +2 -0
  94. package/node_modules/@comis/cli/dist/commands/agent.d.ts +3 -3
  95. package/node_modules/@comis/cli/dist/commands/agent.js +46 -3
  96. package/node_modules/@comis/cli/dist/commands/auth.d.ts +37 -0
  97. package/node_modules/@comis/cli/dist/commands/auth.js +433 -0
  98. package/node_modules/@comis/cli/dist/commands/doctor.d.ts +4 -1
  99. package/node_modules/@comis/cli/dist/commands/doctor.js +20 -5
  100. package/node_modules/@comis/cli/dist/doctor/checks/oauth-health.d.ts +39 -0
  101. package/node_modules/@comis/cli/dist/doctor/checks/oauth-health.js +399 -0
  102. package/node_modules/@comis/cli/dist/doctor/types.d.ts +19 -0
  103. package/node_modules/@comis/cli/dist/index.d.ts +1 -0
  104. package/node_modules/@comis/cli/dist/index.js +10 -4
  105. package/node_modules/@comis/cli/dist/output/relative-time.d.ts +23 -0
  106. package/node_modules/@comis/cli/dist/output/relative-time.js +36 -0
  107. package/node_modules/@comis/cli/dist/wizard/non-interactive.js +17 -8
  108. package/node_modules/@comis/cli/dist/wizard/steps/03-provider.js +2 -1
  109. package/node_modules/@comis/cli/dist/wizard/steps/04-credentials.js +223 -34
  110. package/node_modules/@comis/cli/dist/wizard/steps/10-write-config.js +14 -0
  111. package/node_modules/@comis/cli/dist/wizard/steps/11-daemon-start.js +3 -3
  112. package/node_modules/@comis/cli/dist/wizard/types.d.ts +7 -0
  113. package/node_modules/@comis/cli/package.json +1 -1
  114. package/node_modules/@comis/core/dist/bootstrap.d.ts +1 -1
  115. package/node_modules/@comis/core/dist/config/env-substitution.d.ts +66 -0
  116. package/node_modules/@comis/core/dist/config/env-substitution.js +115 -0
  117. package/node_modules/@comis/core/dist/config/index.d.ts +3 -1
  118. package/node_modules/@comis/core/dist/config/index.js +2 -1
  119. package/node_modules/@comis/core/dist/config/loader.js +61 -0
  120. package/node_modules/@comis/core/dist/config/managed-sections.d.ts +3 -3
  121. package/node_modules/@comis/core/dist/config/managed-sections.js +10 -5
  122. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +4 -0
  123. package/node_modules/@comis/core/dist/config/schema-agent.js +16 -1
  124. package/node_modules/@comis/core/dist/config/schema-background-tasks.d.ts +7 -0
  125. package/node_modules/@comis/core/dist/config/schema-background-tasks.js +7 -0
  126. package/node_modules/@comis/core/dist/config/schema-delivery.d.ts +2 -0
  127. package/node_modules/@comis/core/dist/config/schema-delivery.js +2 -0
  128. package/node_modules/@comis/core/dist/config/schema-gemini-cache.d.ts +0 -2
  129. package/node_modules/@comis/core/dist/config/schema-gemini-cache.js +0 -2
  130. package/node_modules/@comis/core/dist/config/schema-oauth.d.ts +23 -0
  131. package/node_modules/@comis/core/dist/config/schema-oauth.js +19 -0
  132. package/node_modules/@comis/core/dist/config/schema-skills.d.ts +6 -8
  133. package/node_modules/@comis/core/dist/config/schema-skills.js +3 -4
  134. package/node_modules/@comis/core/dist/config/schema.d.ts +10 -0
  135. package/node_modules/@comis/core/dist/config/schema.js +3 -0
  136. package/node_modules/@comis/core/dist/domain/background-task-origin.d.ts +39 -0
  137. package/node_modules/@comis/core/dist/domain/background-task-origin.js +39 -0
  138. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +71 -2
  139. package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
  140. package/node_modules/@comis/core/dist/exports/config.js +1 -1
  141. package/node_modules/@comis/core/dist/exports/domain.d.ts +2 -0
  142. package/node_modules/@comis/core/dist/exports/domain.js +1 -0
  143. package/node_modules/@comis/core/dist/exports/ports.d.ts +2 -2
  144. package/node_modules/@comis/core/dist/exports/ports.js +1 -1
  145. package/node_modules/@comis/core/dist/ports/delivery-queue.d.ts +23 -0
  146. package/node_modules/@comis/core/dist/ports/delivery-queue.js +2 -0
  147. package/node_modules/@comis/core/dist/ports/index.d.ts +2 -0
  148. package/node_modules/@comis/core/dist/ports/index.js +1 -0
  149. package/node_modules/@comis/core/dist/ports/oauth-credential-store.d.ts +64 -0
  150. package/node_modules/@comis/core/dist/ports/oauth-credential-store.js +37 -0
  151. package/node_modules/@comis/core/dist/tool-metadata.d.ts +20 -0
  152. package/node_modules/@comis/core/package.json +1 -1
  153. package/node_modules/@comis/daemon/dist/daemon-types.d.ts +23 -3
  154. package/node_modules/@comis/daemon/dist/daemon.js +82 -19
  155. package/node_modules/@comis/daemon/dist/index.d.ts +2 -0
  156. package/node_modules/@comis/daemon/dist/index.js +5 -0
  157. package/node_modules/@comis/daemon/dist/observability/channel-health-logger.js +3 -3
  158. package/node_modules/@comis/daemon/dist/observability/delivery-queue-logger.js +1 -1
  159. package/node_modules/@comis/daemon/dist/rpc/agent-handlers.d.ts +22 -1
  160. package/node_modules/@comis/daemon/dist/rpc/agent-handlers.js +84 -21
  161. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +2 -2
  162. package/node_modules/@comis/daemon/dist/rpc/config-handlers.d.ts +9 -1
  163. package/node_modules/@comis/daemon/dist/rpc/config-handlers.js +104 -23
  164. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.d.ts +30 -1
  165. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +74 -11
  166. package/node_modules/@comis/daemon/dist/rpc/mcp-handlers.d.ts +8 -0
  167. package/node_modules/@comis/daemon/dist/rpc/mcp-handlers.js +22 -8
  168. package/node_modules/@comis/daemon/dist/rpc/provider-handlers.js +9 -12
  169. package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.d.ts +1 -0
  170. package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.js +27 -2
  171. package/node_modules/@comis/daemon/dist/setup-docker-restart-warn.js +0 -1
  172. package/node_modules/@comis/daemon/dist/wiring/index.d.ts +2 -0
  173. package/node_modules/@comis/daemon/dist/wiring/index.js +1 -0
  174. package/node_modules/@comis/daemon/dist/wiring/oauth-preflight.d.ts +21 -0
  175. package/node_modules/@comis/daemon/dist/wiring/oauth-preflight.js +134 -0
  176. package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +46 -1
  177. package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +127 -3
  178. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.d.ts +39 -0
  179. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.js +32 -0
  180. package/node_modules/@comis/daemon/dist/wiring/setup-background-tasks.d.ts +10 -3
  181. package/node_modules/@comis/daemon/dist/wiring/setup-background-tasks.js +11 -5
  182. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +20 -1
  183. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +1 -1
  184. package/node_modules/@comis/daemon/dist/wiring/setup-delivery.d.ts +14 -5
  185. package/node_modules/@comis/daemon/dist/wiring/setup-delivery.js +52 -19
  186. package/node_modules/@comis/daemon/dist/wiring/setup-schedulers.js +4 -0
  187. package/node_modules/@comis/daemon/package.json +1 -1
  188. package/node_modules/@comis/gateway/dist/index.d.ts +2 -0
  189. package/node_modules/@comis/gateway/dist/index.js +2 -0
  190. package/node_modules/@comis/gateway/dist/oauth/oauth-callback-route.d.ts +66 -0
  191. package/node_modules/@comis/gateway/dist/oauth/oauth-callback-route.js +212 -0
  192. package/node_modules/@comis/gateway/dist/server/hono-server.d.ts +14 -0
  193. package/node_modules/@comis/gateway/dist/server/hono-server.js +10 -0
  194. package/node_modules/@comis/gateway/package.json +1 -1
  195. package/node_modules/@comis/infra/dist/logging/log-fields.d.ts +23 -0
  196. package/node_modules/@comis/infra/package.json +1 -1
  197. package/node_modules/@comis/memory/dist/compaction.d.ts +3 -5
  198. package/node_modules/@comis/memory/dist/compaction.js +2 -3
  199. package/node_modules/@comis/memory/dist/delivery-queue-adapter.d.ts +2 -2
  200. package/node_modules/@comis/memory/dist/delivery-queue-adapter.js +49 -1
  201. package/node_modules/@comis/memory/dist/index.d.ts +2 -0
  202. package/node_modules/@comis/memory/dist/index.js +3 -0
  203. package/node_modules/@comis/memory/dist/memory-api.d.ts +1 -1
  204. package/node_modules/@comis/memory/dist/memory-api.js +1 -1
  205. package/node_modules/@comis/memory/dist/oauth-profile-schema.d.ts +17 -0
  206. package/node_modules/@comis/memory/dist/oauth-profile-schema.js +33 -0
  207. package/node_modules/@comis/memory/dist/oauth-profile-store-encrypted.d.ts +27 -0
  208. package/node_modules/@comis/memory/dist/oauth-profile-store-encrypted.js +144 -0
  209. package/node_modules/@comis/memory/dist/session-store.d.ts +1 -1
  210. package/node_modules/@comis/memory/dist/session-store.js +1 -1
  211. package/node_modules/@comis/memory/dist/sqlite-secret-store.d.ts +29 -3
  212. package/node_modules/@comis/memory/dist/sqlite-secret-store.js +11 -3
  213. package/node_modules/@comis/memory/package.json +1 -1
  214. package/node_modules/@comis/scheduler/dist/execution/execution-lock.d.ts +13 -0
  215. package/node_modules/@comis/scheduler/dist/execution/execution-lock.js +1 -1
  216. package/node_modules/@comis/scheduler/dist/execution/index.d.ts +2 -0
  217. package/node_modules/@comis/scheduler/dist/execution/index.js +2 -0
  218. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +1 -1
  219. package/node_modules/@comis/scheduler/dist/index.d.ts +2 -0
  220. package/node_modules/@comis/scheduler/dist/index.js +2 -0
  221. package/node_modules/@comis/scheduler/package.json +1 -1
  222. package/node_modules/@comis/shared/package.json +1 -1
  223. package/node_modules/@comis/skills/dist/bridge/schema-validator.d.ts +38 -0
  224. package/node_modules/@comis/skills/dist/bridge/schema-validator.js +169 -0
  225. package/node_modules/@comis/skills/dist/bridge/tool-metadata-enforcement.js +12 -0
  226. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +130 -0
  227. package/node_modules/@comis/skills/dist/builtin/exec-diagnostics.d.ts +32 -0
  228. package/node_modules/@comis/skills/dist/builtin/exec-diagnostics.js +127 -0
  229. package/node_modules/@comis/skills/dist/builtin/exec-security.js +38 -0
  230. package/node_modules/@comis/skills/dist/builtin/exec-tool.js +9 -0
  231. package/node_modules/@comis/skills/dist/builtin/file-tools/grep-tool.js +6 -6
  232. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +5 -4
  233. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +38 -27
  234. package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.d.ts +4 -1
  235. package/node_modules/@comis/skills/dist/builtin/platform/background-tasks-tool.js +3 -3
  236. package/node_modules/@comis/skills/dist/builtin/platform/cron-tool.js +1 -1
  237. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +6 -6
  238. package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.d.ts +1 -1
  239. package/node_modules/@comis/skills/dist/builtin/platform/mcp-manage-tool.js +9 -9
  240. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.d.ts +11 -0
  241. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.js +114 -1
  242. package/node_modules/@comis/skills/dist/builtin/sandbox/detect-provider.js +40 -15
  243. package/node_modules/@comis/skills/dist/media/ssrf-fetcher.d.ts +7 -0
  244. package/node_modules/@comis/skills/dist/media/ssrf-fetcher.js +9 -2
  245. package/node_modules/@comis/skills/package.json +1 -1
  246. package/node_modules/@comis/web/dist/assets/{agent-detail-71BSbSfD.js → agent-detail-q8t1NB7w.js} +1 -1
  247. package/node_modules/@comis/web/dist/assets/{agent-editor-CTSDZhwT.js → agent-editor-B46io5gv.js} +1 -1
  248. package/node_modules/@comis/web/dist/assets/{agent-list-BEhni2ea.js → agent-list-DQ6g2Rcx.js} +1 -1
  249. package/node_modules/@comis/web/dist/assets/{billing-view-DVP1IvVs.js → billing-view-IWPR8LgF.js} +1 -1
  250. package/node_modules/@comis/web/dist/assets/{channel-detail-N_YK74xC.js → channel-detail-DlNNZuuC.js} +1 -1
  251. package/node_modules/@comis/web/dist/assets/{channel-list-DRk6ZJaF.js → channel-list-DhGwxiMc.js} +1 -1
  252. package/node_modules/@comis/web/dist/assets/{chat-console-Dm-GtSf9.js → chat-console-Nv6fM3Rc.js} +1 -1
  253. package/node_modules/@comis/web/dist/assets/{config-editor-CIferYX6.js → config-editor-BYKuJF76.js} +1 -1
  254. package/node_modules/@comis/web/dist/assets/{context-dag-browser-CL84rXXM.js → context-dag-browser-ClNEtzYE.js} +1 -1
  255. package/node_modules/@comis/web/dist/assets/{context-engine-B1HOTEZv.js → context-engine-BZJ6HChd.js} +1 -1
  256. package/node_modules/@comis/web/dist/assets/{delivery-view-Y6JKYVFw.js → delivery-view-Cb7I3vGu.js} +1 -1
  257. package/node_modules/@comis/web/dist/assets/{diagnostics-view-DWV1UQjz.js → diagnostics-view-9u9Lyu5a.js} +1 -1
  258. package/node_modules/@comis/web/dist/assets/{ic-chat-message-DfSERzzg.js → ic-chat-message-BFt3cVpx.js} +1 -1
  259. package/node_modules/@comis/web/dist/assets/{ic-connection-dot-CXyhlJup.js → ic-connection-dot-y77LZ3Gu.js} +1 -1
  260. package/node_modules/@comis/web/dist/assets/{ic-tool-call-DNmwTjek.js → ic-tool-call-qt6w1NQl.js} +1 -1
  261. package/node_modules/@comis/web/dist/assets/{index-CBr0Tm9_.js → index-8Tg9oc-C.js} +2 -2
  262. package/node_modules/@comis/web/dist/assets/{mcp-management-BaH2-vox.js → mcp-management-69dtH_kY.js} +2 -2
  263. package/node_modules/@comis/web/dist/assets/{media-config-CZLshJoN.js → media-config-BdjLj5c1.js} +1 -1
  264. package/node_modules/@comis/web/dist/assets/{media-test-C9NUWgo_.js → media-test-DuPqrixi.js} +1 -1
  265. package/node_modules/@comis/web/dist/assets/{memory-inspector-D_fmTcRN.js → memory-inspector-B-Pepbq-.js} +1 -1
  266. package/node_modules/@comis/web/dist/assets/{message-center-BBFlNCZn.js → message-center-B7l0yNYY.js} +1 -1
  267. package/node_modules/@comis/web/dist/assets/{models-BytGLm99.js → models-JHFHuv5S.js} +1 -1
  268. package/node_modules/@comis/web/dist/assets/{observe-view-VXtHqaqq.js → observe-view-r8mqhy4O.js} +1 -1
  269. package/node_modules/@comis/web/dist/assets/{pipeline-builder-CfXczlfJ.js → pipeline-builder-XjkiZRcR.js} +1 -1
  270. package/node_modules/@comis/web/dist/assets/{pipeline-history-CPmXFnbe.js → pipeline-history-CZqJv_Hj.js} +1 -1
  271. package/node_modules/@comis/web/dist/assets/{pipeline-history-detail-DcueTMs9.js → pipeline-history-detail-BEFGMoDy.js} +1 -1
  272. package/node_modules/@comis/web/dist/assets/{pipeline-list-B-xG5WZh.js → pipeline-list-B6q5LvO1.js} +1 -1
  273. package/node_modules/@comis/web/dist/assets/{pipeline-monitor-pnIOYaSY.js → pipeline-monitor-BNomXjVL.js} +1 -1
  274. package/node_modules/@comis/web/dist/assets/{scheduler-BtUIFHhA.js → scheduler-BJEjcGKA.js} +1 -1
  275. package/node_modules/@comis/web/dist/assets/{security-C8mWRq2y.js → security-2G1jhBfV.js} +1 -1
  276. package/node_modules/@comis/web/dist/assets/{session-detail-DgdkO5ka.js → session-detail-DmVPzFBR.js} +1 -1
  277. package/node_modules/@comis/web/dist/assets/{session-list-DcylcfTn.js → session-list-CsqMQoHs.js} +1 -1
  278. package/node_modules/@comis/web/dist/assets/{setup-wizard-BP5yjsuL.js → setup-wizard-CAdM-gSP.js} +1 -1
  279. package/node_modules/@comis/web/dist/assets/{skills-DXt1bX8Z.js → skills-2ODqKaWr.js} +1 -1
  280. package/node_modules/@comis/web/dist/assets/{subagents-C7YbUHXY.js → subagents-BFlwfTbD.js} +1 -1
  281. package/node_modules/@comis/web/dist/assets/{workspace-manager-DP6pW4wa.js → workspace-manager--CbOx_dI.js} +1 -1
  282. package/node_modules/@comis/web/dist/index.html +1 -1
  283. package/node_modules/@comis/web/package.json +1 -1
  284. package/package.json +17 -16
@@ -56,7 +56,7 @@ export function setupChannelHealthLogging(deps) {
56
56
  lastMessageAt,
57
57
  hint,
58
58
  errorKind: ERROR_KIND_MAP[currentState] ?? "internal",
59
- module: MODULE,
59
+ submodule: MODULE,
60
60
  }, "Channel health degraded: %s -> %s", previousState, currentState);
61
61
  }
62
62
  else {
@@ -66,7 +66,7 @@ export function setupChannelHealthLogging(deps) {
66
66
  previousState,
67
67
  currentState,
68
68
  connectionMode,
69
- module: MODULE,
69
+ submodule: MODULE,
70
70
  }, "Channel health changed: %s -> %s", previousState, currentState);
71
71
  }
72
72
  });
@@ -76,7 +76,7 @@ export function setupChannelHealthLogging(deps) {
76
76
  channelType: event.channelType,
77
77
  state: event.state,
78
78
  responseTimeMs: event.responseTimeMs,
79
- module: MODULE,
79
+ submodule: MODULE,
80
80
  }, "Health check: %s = %s", event.channelType, event.state);
81
81
  });
82
82
  }
@@ -21,7 +21,7 @@
21
21
  export function setupDeliveryQueueLogging(deps) {
22
22
  const { eventBus, logger } = deps;
23
23
  const MODULE = "delivery-queue";
24
- const log = logger.child({ module: MODULE });
24
+ const log = logger.child({ submodule: MODULE });
25
25
  // 1. Enqueue: message enters queue (boundary event -> INFO)
26
26
  eventBus.on("delivery:enqueued", (data) => {
27
27
  log.info({
@@ -12,7 +12,7 @@
12
12
  * the runtime agents map, and returns structured results.
13
13
  * @module
14
14
  */
15
- import type { PerAgentConfig, ProviderEntry } from "@comis/core";
15
+ import type { PerAgentConfig, ProviderEntry, OAuthCredentialStorePort } from "@comis/core";
16
16
  import { type PersistToConfigDeps } from "./persist-to-config.js";
17
17
  import type { RpcHandler } from "./types.js";
18
18
  /** Dependencies required by agent management RPC handlers. */
@@ -36,6 +36,27 @@ export interface AgentHandlerDeps {
36
36
  };
37
37
  /** Provider entries map for probe lookups when agents switch providers. */
38
38
  providerEntries?: Record<string, ProviderEntry>;
39
+ /**
40
+ * Optional OAuth credential store for validating that `oauthProfiles`
41
+ * patches reference existing stored profile IDs. The
42
+ * agents.update handler iterates over each (provider, profileId) entry
43
+ * in the patched config and calls `has(profileId)`; on miss it throws
44
+ * with the documented "not found in store" wording BEFORE the
45
+ * `deps.agents[agentId] = parsedConfig` reference-replacement at the
46
+ * end of the handler — failure leaves the daemon's in-memory map AND
47
+ * the YAML both unchanged. When this field is absent (e.g. test
48
+ * contexts without OAuth wiring) the validation block is a no-op so
49
+ * existing behavior is preserved.
50
+ */
51
+ oauthCredentialStore?: OAuthCredentialStorePort;
52
+ /**
53
+ * Models config — passed to the credential resolver so that
54
+ * `provider: "default"` is resolved to `models.defaultProvider` for the
55
+ * key check, mirroring runtime resolution in `resolveAgentModel`.
56
+ */
57
+ modelsConfig?: {
58
+ defaultProvider?: string;
59
+ };
39
60
  }
40
61
  /**
41
62
  * Create a record of agent management RPC handlers bound to the given deps.
@@ -39,12 +39,12 @@ export function createAgentHandlers(deps) {
39
39
  if (deps.agents[agentId] !== undefined) {
40
40
  throw new Error(`Agent already exists: ${agentId}`);
41
41
  }
42
- // 260428-vyf L2: extract inlineContent BEFORE config processing.
43
- // role/identity are write-once side-effects (ROLE.md / IDENTITY.md
44
- // file writes), NOT durable state — they NEVER enter the persisted
45
- // config patch. The L1 tool boundary is responsible for stripping
46
- // them from `config.workspace` before this RPC is called; this
47
- // handler only consumes the dedicated top-level `inlineContent`
42
+ // Extract inlineContent BEFORE config processing. role/identity
43
+ // are write-once side-effects (ROLE.md / IDENTITY.md file writes),
44
+ // NOT durable state — they NEVER enter the persisted config patch.
45
+ // The tool boundary is responsible for stripping them from
46
+ // `config.workspace` before this RPC is called; this handler only
47
+ // consumes the dedicated top-level `inlineContent`
48
48
  // field. If a (mis)caller leaves them inside config.workspace, the
49
49
  // downstream Zod strict-object will reject them — that's an
50
50
  // explicit failure mode, not a silent drop.
@@ -69,17 +69,37 @@ export function createAgentHandlers(deps) {
69
69
  existingSkills.builtinTools = { ...DEFAULT_BUILTIN_TOOLS, ...existingBt };
70
70
  raw.skills = existingSkills;
71
71
  const parsedConfig = PerAgentConfigSchema.parse(config);
72
- // Credential guard (260501-2pz): fail-loud if the new agent's
73
- // provider has no resolvable API key. Mirrors agents.update guard
72
+ // Credential guard: fail-loud if the new agent's provider has no
73
+ // resolvable API key. Mirrors agents.update guard
74
74
  // ordering — runs BEFORE the in-memory commit so rejection prevents
75
75
  // assignment, file persist, and hot-add. Same helper as the patch /
76
76
  // update call sites for cross-handler consistency.
77
- const credCheck = resolveProviderCredential(parsedConfig.provider, {
78
- providerEntries: deps.providerEntries ?? {},
79
- secretManager: deps.secretManager,
80
- });
81
- if (!credCheck.ok) {
82
- throw new Error(credCheck.reason);
77
+ //
78
+ // Also plumb agents.<id>.oauthProfiles + the daemon-level OAuth
79
+ // credential store so OAuth-only providers (e.g. openai-codex) can
80
+ // resolve via Source C. Pre-resolve has() so the resolver itself stays
81
+ // synchronous (port-side validator does no I/O).
82
+ {
83
+ const targetProvider = parsedConfig.provider;
84
+ // eslint-disable-next-line security/detect-object-injection -- typed Record<string, string> read; targetProvider validated by schema parse
85
+ const configuredProfileId = parsedConfig.oauthProfiles?.[targetProvider];
86
+ let loaderHasProfile = false;
87
+ if (configuredProfileId && deps.oauthCredentialStore) {
88
+ const hasResult = await deps.oauthCredentialStore.has(configuredProfileId);
89
+ loaderHasProfile = hasResult.ok && hasResult.value === true;
90
+ }
91
+ const credCheck = resolveProviderCredential(targetProvider, {
92
+ providerEntries: deps.providerEntries ?? {},
93
+ secretManager: deps.secretManager,
94
+ modelsConfig: deps.modelsConfig,
95
+ oauthProfiles: parsedConfig.oauthProfiles,
96
+ oauthProfileLoader: configuredProfileId
97
+ ? { has: (id) => id === configuredProfileId && loaderHasProfile }
98
+ : undefined,
99
+ });
100
+ if (!credCheck.ok) {
101
+ throw new Error(credCheck.reason);
102
+ }
83
103
  }
84
104
  deps.agents[agentId] = parsedConfig;
85
105
  // Best-effort persistence to config.yaml
@@ -109,7 +129,7 @@ export function createAgentHandlers(deps) {
109
129
  }
110
130
  }
111
131
  const workspaceDir = resolveWorkspaceDir(parsedConfig, agentId);
112
- // 260428-vyf L2: best-effort inline ROLE.md / IDENTITY.md write.
132
+ // Best-effort inline ROLE.md / IDENTITY.md write.
113
133
  // Only invoke when inlineContent has at least one populated field
114
134
  // AND the persistDeps logger is available (the helper requires a
115
135
  // structured logger; the in-memory-only test path skips it).
@@ -220,20 +240,63 @@ export function createAgentHandlers(deps) {
220
240
  }
221
241
  const merged = { ...existing, ...config };
222
242
  const parsedConfig = PerAgentConfigSchema.parse(merged);
223
- // Credential guard + probe (260501-2pz): when provider OR model
224
- // changes, (a) GUARD fail-loud if the resulting provider's API key
225
- // is not resolvable from any source (no silent skip), then (b) PROBE
226
- // preexisting wire validation when an explicit providers.entries
243
+ // Validate oauthProfiles patch each profileId must exist in the
244
+ // OAuth credential store. Skipped when no oauthCredentialStore is
245
+ // wired (test contexts; non-OAuth-aware setups). Critical: this
246
+ // throws BEFORE the `deps.agents[agentId] = parsedConfig`
247
+ // reference-replacement at the end of the handler, so on failure the
248
+ // daemon's in-memory map AND the YAML are both unchanged. The
249
+ // Zod-layer format check has already run during
250
+ // PerAgentConfigSchema.parse(merged) above — this block ONLY checks
251
+ // existence in the store.
252
+ if (parsedConfig.oauthProfiles !== undefined && deps.oauthCredentialStore) {
253
+ for (const [provider, profileId] of Object.entries(parsedConfig.oauthProfiles)) {
254
+ const has = await deps.oauthCredentialStore.has(profileId);
255
+ if (!has.ok || !has.value) {
256
+ throw new Error(`profile ${profileId} not found in store. Run "comis auth list" to see available profiles.`);
257
+ }
258
+ // The provider variable is iterated for completeness; the
259
+ // existence check is keyed on profileId alone (validateProfileId
260
+ // — invoked by the Zod refine — already enforced that the
261
+ // profile-id's provider portion equals the map key).
262
+ void provider;
263
+ }
264
+ }
265
+ // Credential guard + probe: when provider changes,
266
+ // (a) GUARD — fail-loud if the resulting provider's API key is not
267
+ // resolvable from any source (no silent skip), then (b) PROBE —
268
+ // preexisting wire validation when an explicit providers.entries
227
269
  // record with apiKeyName exists. Order matters: guard runs first
228
270
  // (cheap, all paths), probe runs second (only when applicable).
271
+ //
272
+ // Model-only changes with unchanged provider DO NOT fire the guard
273
+ // or probe — they introduce no new credential surface.
274
+ // Stale-broken-config detection moves to the next chat turn
275
+ // (fail-loud at the request boundary), where the message is
276
+ // correctly shaped for the actual failure mode (not a pre-emptive
277
+ // API-key prompt that is wrong for OAuth providers like
278
+ // openai-codex). Also plumbs agents.<id>.oauthProfiles + the
279
+ // daemon-level OAuth credential store so Source C can fire.
229
280
  const providerChanging = config.provider !== undefined && config.provider !== existing.provider;
230
- const modelChanging = config.model !== undefined && config.model !== existing.model;
231
- if (providerChanging || modelChanging) {
281
+ if (providerChanging) {
232
282
  const targetProvider = parsedConfig.provider;
283
+ // Pre-resolve has() at the daemon edge so the resolver stays sync.
284
+ // eslint-disable-next-line security/detect-object-injection -- typed Record<string, string> read; targetProvider validated by schema parse
285
+ const configuredProfileId = parsedConfig.oauthProfiles?.[targetProvider];
286
+ let loaderHasProfile = false;
287
+ if (configuredProfileId && deps.oauthCredentialStore) {
288
+ const hasResult = await deps.oauthCredentialStore.has(configuredProfileId);
289
+ loaderHasProfile = hasResult.ok && hasResult.value === true;
290
+ }
233
291
  // (a) GUARD — fail-loud if no credential source resolves
234
292
  const resolution = resolveProviderCredential(targetProvider, {
235
293
  providerEntries: deps.providerEntries ?? {},
236
294
  secretManager: deps.secretManager,
295
+ modelsConfig: deps.modelsConfig,
296
+ oauthProfiles: parsedConfig.oauthProfiles,
297
+ oauthProfileLoader: configuredProfileId
298
+ ? { has: (id) => id === configuredProfileId && loaderHasProfile }
299
+ : undefined,
237
300
  });
238
301
  if (!resolution.ok) {
239
302
  throw new Error(resolution.reason);
@@ -63,7 +63,7 @@ async function attemptWrite(deps, agentId, filename, targetPath, content) {
63
63
  catch (e) {
64
64
  const message = e instanceof Error ? e.message : String(e);
65
65
  deps.logger.warn({
66
- module: "daemon.rpc.agent-handlers",
66
+ submodule: "rpc.agent-handlers",
67
67
  agentId,
68
68
  file: filename,
69
69
  err: e,
@@ -128,7 +128,7 @@ export async function writeInlineWorkspaceFiles(deps, params) {
128
128
  // Pure no-op invocations (both fields absent) stay silent.
129
129
  if (roleWritten || identityWritten) {
130
130
  deps.logger.info({
131
- module: "daemon.rpc.agent-handlers",
131
+ submodule: "rpc.agent-handlers",
132
132
  agentId: params.agentId,
133
133
  roleBytes: roleWritten ? params.role.length : 0,
134
134
  identityBytes: identityWritten ? params.identity.length : 0,
@@ -7,7 +7,7 @@
7
7
  * Extracted from daemon.ts rpcCallInner switch block
8
8
  * @module
9
9
  */
10
- import { type AppContainer, type ConfigGitManager } from "@comis/core";
10
+ import { type AppContainer, type ConfigGitManager, type OAuthCredentialStorePort } from "@comis/core";
11
11
  import type { ComisLogger } from "@comis/infra";
12
12
  import { z } from "zod";
13
13
  import type { RpcHandler } from "./types.js";
@@ -26,6 +26,14 @@ export interface ConfigHandlerDeps {
26
26
  timeoutMs?: number;
27
27
  secret?: string;
28
28
  };
29
+ /**
30
+ * Optional OAuth credential store, used by the credential guard to
31
+ * confirm that an agent's `oauthProfiles[provider]` entry refers to a
32
+ * profile that actually exists in ~/.comis/auth-profiles.json (or the
33
+ * encrypted-SQLite equivalent). When absent, the OAuth branch of the
34
+ * resolver is a no-op — existing API-key behavior is preserved.
35
+ */
36
+ oauthCredentialStore?: OAuthCredentialStorePort;
29
37
  }
30
38
  /**
31
39
  * Unwrap Zod schema wrappers (Optional / Nullable / Default / Pipe) to get
@@ -8,7 +8,7 @@
8
8
  * Extracted from daemon.ts rpcCallInner switch block
9
9
  * @module
10
10
  */
11
- import { isImmutableConfigPath, getConfigSchema, getConfigSections, deepMerge, AppConfigSchema, redactConfigSecrets, warnSuspiciousEnvValues, getManagedSectionRedirect, formatRedirectHint, } from "@comis/core";
11
+ import { isImmutableConfigPath, getConfigSchema, getConfigSections, deepMerge, AppConfigSchema, redactConfigSecrets, warnSuspiciousEnvValues, findUnresolvedEnvRefs, formatMissingEnvRefError, getManagedSectionRedirect, formatRedirectHint, } from "@comis/core";
12
12
  import { suppressError } from "@comis/shared";
13
13
  import { stringify as yamlStringify } from "yaml";
14
14
  import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
@@ -444,7 +444,7 @@ export function createConfigHandlers(deps) {
444
444
  // pre-flight and bridge metadata validator catch this earlier for
445
445
  // LLM tool calls -- this path is reached when those layers are
446
446
  // bypassed. Emit the same redirect hint so all clients see
447
- // identical, model-agnostic recovery instructions (quick-260425-t40).
447
+ // identical, model-agnostic recovery instructions.
448
448
  if (isImmutableConfigPath(section, key)) {
449
449
  const redirect = getManagedSectionRedirect(section, key);
450
450
  const suffix = redirect
@@ -452,29 +452,74 @@ export function createConfigHandlers(deps) {
452
452
  : " This setting requires manual operator intervention via config files.";
453
453
  throw new Error(`Config path "${key ? `${section}.${key}` : section}" is immutable and cannot be modified at runtime.${suffix}`);
454
454
  }
455
- // Credential guard (260501-2pz): when a patch targets an agent's
456
- // provider/model field, verify the resulting provider's API key is
457
- // resolvable from at least one source pi-coding-agent will consult
458
- // at runtime. Fail-loud here rather than letting an unauthorized
459
- // provider config persist and explode at the next chat turn.
460
- // See `.planning/design/daemon-credential-guard/PLAN.md`.
455
+ // Credential guard: when a patch targets an agent's provider/model
456
+ // field, verify the resulting provider's API key is resolvable from
457
+ // at least one source pi-coding-agent will consult at runtime.
458
+ // Fail-loud here rather than letting an unauthorized provider
459
+ // config persist and explode at the next chat turn.
460
+ //
461
+ // Model-only patches with unchanged provider introduce no new
462
+ // credential surface — short-circuit the guard entirely. The
463
+ // runtime auth chain that just authenticated the LLM call making
464
+ // this patch will keep working. Stale-broken-config detection
465
+ // moves back to the next chat turn (fail-loud at the request
466
+ // boundary), where the message is correctly shaped for the actual
467
+ // failure mode (not a pre-emptive API-key prompt that is wrong for
468
+ // OAuth providers like openai-codex).
461
469
  if (section === "agents" && isAgentProviderOrModelKey(key)) {
462
470
  const targetProvider = extractTargetProvider(key, coercedValue, deps.container.config);
463
471
  if (targetProvider !== undefined) {
464
- const resolution = resolveProviderCredential(targetProvider, {
465
- providerEntries: deps.container.config.providers?.entries ?? {},
466
- secretManager: deps.container.secretManager,
467
- });
468
- if (!resolution.ok) {
469
- deps.logger.warn({
470
- method: "config.patch",
471
- section,
472
- key,
473
- targetProvider,
474
- hint: "Provider credential not resolvable",
475
- errorKind: "validation",
476
- }, "Config patch rejected: missing provider credential");
477
- throw new Error(resolution.reason);
472
+ // For `.model` keys, extractTargetProvider returns the agent's
473
+ // CURRENT provider (config-handlers.ts:65-69) so the resolved
474
+ // targetProvider always equals the current provider. The
475
+ // model-only short-circuit therefore always fires for `.model`
476
+ // keys; we still compute the equality explicitly so the
477
+ // intent is readable and the check survives any future change
478
+ // to extractTargetProvider's contract.
479
+ const isModelOnlyPatch = key.endsWith(".model");
480
+ const agentId = key.split(".")[0];
481
+ // eslint-disable-next-line security/detect-object-injection -- agentId from validated key; agents map is typed Record
482
+ const currentProvider = agentId
483
+ ? deps.container.config.agents?.[agentId]?.provider
484
+ : undefined;
485
+ const providerUnchanged = currentProvider === targetProvider;
486
+ if (!(isModelOnlyPatch && providerUnchanged)) {
487
+ // Provider is changing (or this is a `.provider` patch where
488
+ // targetProvider is the new value) — run the credential guard.
489
+ // eslint-disable-next-line security/detect-object-injection -- agentId from validated key; agents map is typed Record
490
+ const agentOauthProfiles = (agentId
491
+ ? deps.container.config.agents?.[agentId]?.oauthProfiles
492
+ : undefined);
493
+ // Pre-resolve the loader has() for the configured profile so the
494
+ // resolver can stay synchronous. This keeps Hexagonal port-side
495
+ // validators I/O-free.
496
+ // eslint-disable-next-line security/detect-object-injection -- typed Record<string, string> read; targetProvider validated above
497
+ const configuredProfileId = agentOauthProfiles?.[targetProvider];
498
+ let loaderHasProfile = false;
499
+ if (configuredProfileId && deps.oauthCredentialStore) {
500
+ const hasResult = await deps.oauthCredentialStore.has(configuredProfileId);
501
+ loaderHasProfile = hasResult.ok && hasResult.value === true;
502
+ }
503
+ const resolution = resolveProviderCredential(targetProvider, {
504
+ providerEntries: deps.container.config.providers?.entries ?? {},
505
+ secretManager: deps.container.secretManager,
506
+ modelsConfig: deps.container.config.models,
507
+ oauthProfiles: agentOauthProfiles,
508
+ oauthProfileLoader: configuredProfileId
509
+ ? { has: (id) => id === configuredProfileId && loaderHasProfile }
510
+ : undefined,
511
+ });
512
+ if (!resolution.ok) {
513
+ deps.logger.warn({
514
+ method: "config.patch",
515
+ section,
516
+ key,
517
+ targetProvider,
518
+ hint: "Provider credential not resolvable",
519
+ errorKind: "validation",
520
+ }, "Config patch rejected: missing provider credential");
521
+ throw new Error(resolution.reason);
522
+ }
478
523
  }
479
524
  }
480
525
  }
@@ -537,6 +582,42 @@ export function createConfigHandlers(deps) {
537
582
  throw new Error(`Suspicious env value(s) in config patch: ${hints}. ` +
538
583
  `Use \${VAR_NAME} syntax to reference secrets stored via env_set.`);
539
584
  }
585
+ // Reject patches that reference env vars not in the secrets store, on
586
+ // enabled MCP servers only. The env-substitution skip on disabled
587
+ // servers makes `enabled:false + ${VAR}` harmless at bootstrap; this
588
+ // gate forbids the partially-valid `enabled:true + missing ${VAR}`
589
+ // shape.
590
+ //
591
+ // We walk `patch` (not the deep-merged config) because we only
592
+ // validate what's being WRITTEN this RPC. `restoreMcpServerEnv` above
593
+ // already restored env from existing YAML for partial-update-without-
594
+ // env patches, so `patch.integrations.mcp.servers[].env` is the
595
+ // post-restore truth. Full-config validation would re-flag pre-
596
+ // existing valid-at-write-time refs whose secrets were later removed
597
+ // (out of scope, separate problem).
598
+ const patchInteg = patch.integrations;
599
+ const patchMcp = patchInteg?.mcp;
600
+ const patchServers = patchMcp?.servers;
601
+ if (Array.isArray(patchServers)) {
602
+ for (const s of patchServers) {
603
+ if (!s || typeof s !== "object")
604
+ continue;
605
+ const server = s;
606
+ // McpServerEntrySchema.enabled defaults to true → absent = enabled.
607
+ // Only explicit `enabled: false` skips the check (preserves the
608
+ // placeholder-for-later pattern).
609
+ if (server.enabled === false)
610
+ continue;
611
+ if (!server.env)
612
+ continue;
613
+ const serverName = typeof server.name === "string" ? server.name : "<unnamed>";
614
+ const unresolved = findUnresolvedEnvRefs(server.env, (key) => deps.container.secretManager.get(key));
615
+ if (unresolved.length > 0) {
616
+ const missingNames = unresolved.map((u) => u.varName);
617
+ throw new Error(formatMissingEnvRefError(serverName, missingNames));
618
+ }
619
+ }
620
+ }
540
621
  const updatedLocal = deepMerge(existingLocal, patch);
541
622
  // ${VAR} env var references in string values are preserved
542
623
  // through YAML round-trip. yamlStringify writes them literally, parseYaml
@@ -651,7 +732,7 @@ export function createConfigHandlers(deps) {
651
732
  }
652
733
  // Check immutable paths -- entire section is being replaced.
653
734
  // Backstop for direct-RPC clients; LLM tool calls hit the same redirect
654
- // earlier via gateway-tool / bridge validator (quick-260425-t40).
735
+ // earlier via gateway-tool / bridge validator.
655
736
  if (isImmutableConfigPath(section)) {
656
737
  const redirect = getManagedSectionRedirect(section);
657
738
  const suffix = redirect
@@ -6,12 +6,41 @@ export interface CredentialResolverDeps {
6
6
  secretManager?: {
7
7
  has(key: string): boolean;
8
8
  };
9
+ /**
10
+ * Models config — used to resolve `provider: "default"` to the operator's
11
+ * configured `models.defaultProvider`, mirroring runtime resolution in
12
+ * `resolveAgentModel`. When omitted or `defaultProvider` is empty, a
13
+ * literal `"default"` input passes through and produces a clear rejection
14
+ * pointing the operator at `models.defaultProvider`.
15
+ */
16
+ modelsConfig?: {
17
+ defaultProvider?: string;
18
+ };
19
+ /**
20
+ * Per-agent OAuth profile map (Record<provider, profileId>) sourced from
21
+ * `agents.<id>.oauthProfiles` on the daemon's container.config. When an
22
+ * entry exists for the resolved provider AND `oauthProfileLoader.has`
23
+ * returns true, the resolver returns ok with source: "oauth_profile".
24
+ */
25
+ oauthProfiles?: Record<string, string>;
26
+ /**
27
+ * Synchronous facade over OAuthCredentialStorePort.has. The async port
28
+ * call MUST be performed at the daemon edge (config-handlers /
29
+ * agent-handlers) and adapted to this sync shape — the resolver itself
30
+ * does no I/O (hexagonal: port-side validator). Pass a closure such as
31
+ * `{ has: () => storeHasResult.ok && storeHasResult.value }`.
32
+ */
33
+ oauthProfileLoader?: {
34
+ has(profileId: string): boolean;
35
+ };
9
36
  }
10
37
  export interface CredentialResolution {
11
38
  ok: boolean;
12
39
  /** When ok=false: actionable error message ready to throw. */
13
40
  reason?: string;
14
41
  /** When ok=true: which source resolved. Useful for debug logs. */
15
- source?: "keyless" | "providers_entry" | "env_canonical";
42
+ source?: "keyless" | "providers_entry" | "env_canonical" | "oauth_profile";
43
+ /** When ok=true: the provider name actually checked (after "default" resolution). */
44
+ resolvedProvider?: string;
16
45
  }
17
46
  export declare function resolveProviderCredential(targetProvider: string, deps: CredentialResolverDeps): CredentialResolution;
@@ -9,11 +9,24 @@
9
9
  * Resolution chain (matches pi-coding-agent runtime semantics):
10
10
  * 1. KEYLESS_PROVIDER_TYPES.has(entry.type) — ollama / lm-studio
11
11
  * 2. providers.entries.<provider>.apiKeyName → secretManager.has(...)
12
- * 3. pi-ai's getEnvApiKey(provider) — canonical env + OAuth + ADC + AWS
12
+ * 3. pi-ai's getEnvApiKey(provider) — canonical env vars (incl. ANTHROPIC_OAUTH_TOKEN
13
+ * and AWS/ADC special-cases). Does NOT cover comis-managed OAuth profiles in
14
+ * ~/.comis/auth-profiles.json (e.g. openai-codex).
15
+ * 4. Comis OAuth profiles — agent.oauthProfiles[provider] resolved against an
16
+ * injected oauthProfileLoader (the OAuthCredentialStorePort handle held by
17
+ * the daemon, adapted to a synchronous has-check at the call site).
18
+ *
19
+ * Note on synchronous loader facade (quick-260504-irq): `OAuthCredentialStorePort.has`
20
+ * is async (returns Promise<Result<boolean, Error>>). To avoid an async cascade
21
+ * through every call site, this resolver remains SYNCHRONOUS and accepts a
22
+ * sync facade (`oauthProfileLoader: { has(profileId: string): boolean }`).
23
+ * The async port `has()` call MUST be performed at the daemon edge
24
+ * (config-handlers / agent-handlers) and adapted into the closure. This keeps
25
+ * the port-side validator I/O-free (Hexagonal: validator does no I/O).
13
26
  *
14
27
  * @module
15
28
  */
16
- import { getEnvApiKey } from "@mariozechner/pi-ai";
29
+ import { getEnvApiKey, getProviders, getModels } from "@mariozechner/pi-ai";
17
30
  /**
18
31
  * Provider types that don't need an API key. Mirrors agent's
19
32
  * KEYLESS_PROVIDER_TYPES at model-registry-adapter.ts:60 — extended here to
@@ -28,23 +41,73 @@ export function resolveProviderCredential(targetProvider, deps) {
28
41
  reason: `Invalid provider value: must be a non-empty string (got ${JSON.stringify(targetProvider)})`,
29
42
  };
30
43
  }
31
- // eslint-disable-next-line security/detect-object-injection -- typed Record<string, ProviderEntry> read; targetProvider validated above
32
- const entry = deps.providerEntries?.[targetProvider];
44
+ // Resolve `provider: "default"` to the operator's configured default,
45
+ // mirroring runtime resolution in `resolveAgentModel`:
46
+ // 1. If `providers.entries.default` is explicitly configured, treat that
47
+ // as the operator's intent — the entry itself carries the credential
48
+ // resolution path (keyless / apiKeyName).
49
+ // 2. Else, if `models.defaultProvider` is set, use that.
50
+ // 3. Otherwise, fall back to the most-populated native provider in the
51
+ // pi-ai catalog (same heuristic the runtime applies).
52
+ // This keeps the credential check semantically aligned with the literal
53
+ // provider the runtime will select.
54
+ let effectiveProvider = targetProvider;
55
+ if (targetProvider.toLowerCase() === "default") {
56
+ const explicitDefault = deps.providerEntries?.default;
57
+ if (!explicitDefault) {
58
+ const dp = deps.modelsConfig?.defaultProvider;
59
+ if (dp && dp.length > 0) {
60
+ effectiveProvider = dp;
61
+ }
62
+ else {
63
+ const allProviders = getProviders();
64
+ if (allProviders.length > 0) {
65
+ effectiveProvider = allProviders
66
+ .map((p) => ({ p, n: getModels(p).length }))
67
+ .sort((a, b) => b.n - a.n)[0].p;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ // eslint-disable-next-line security/detect-object-injection -- typed Record<string, ProviderEntry> read; effectiveProvider validated above
73
+ const entry = deps.providerEntries?.[effectiveProvider];
33
74
  // 1. Keyless types
34
75
  if (entry && KEYLESS_PROVIDER_TYPES.has(entry.type)) {
35
- return { ok: true, source: "keyless" };
76
+ return { ok: true, source: "keyless", resolvedProvider: effectiveProvider };
36
77
  }
37
78
  // 2. Source A: providers.entries with secret-manager-resolvable apiKeyName
38
79
  if (entry?.apiKeyName && deps.secretManager?.has(entry.apiKeyName)) {
39
- return { ok: true, source: "providers_entry" };
80
+ return { ok: true, source: "providers_entry", resolvedProvider: effectiveProvider };
81
+ }
82
+ // 3. Source C: comis OAuth profile (per-agent agents.<id>.oauthProfiles).
83
+ // Covers OAuth-only providers like openai-codex whose tokens live in
84
+ // ~/.comis/auth-profiles.json — pi-ai's getEnvApiKey does NOT see them.
85
+ // Inserted before Source B so OAuth profiles win over env-canonical when
86
+ // both would resolve (the operator explicitly configured the profile).
87
+ // eslint-disable-next-line security/detect-object-injection -- typed Record<string, string> read; effectiveProvider validated above
88
+ const configuredProfileId = deps.oauthProfiles?.[effectiveProvider];
89
+ if (configuredProfileId && deps.oauthProfileLoader?.has(configuredProfileId)) {
90
+ return { ok: true, source: "oauth_profile", resolvedProvider: effectiveProvider };
40
91
  }
41
- // 3. Source B: pi-ai canonical env / OAuth / ADC chain
42
- if (getEnvApiKey(targetProvider)) {
43
- return { ok: true, source: "env_canonical" };
92
+ // 4. Source B: pi-ai canonical env / OAuth / ADC chain
93
+ if (getEnvApiKey(effectiveProvider)) {
94
+ return { ok: true, source: "env_canonical", resolvedProvider: effectiveProvider };
44
95
  }
45
- return { ok: false, reason: buildRejectionMessage(targetProvider, entry) };
96
+ return { ok: false, reason: buildRejectionMessage(effectiveProvider, entry, configuredProfileId) };
46
97
  }
47
- function buildRejectionMessage(targetProvider, entry) {
98
+ function buildRejectionMessage(targetProvider, entry, configuredProfileId) {
99
+ // OAuth-aware rejection: when the agent has an oauthProfiles entry for this
100
+ // provider but the loader could not confirm the profile, the failure mode is
101
+ // a missing OAuth profile (not a missing API key). Point the operator at
102
+ // `comis auth login` rather than env_set / apiKeyName recovery.
103
+ if (configuredProfileId) {
104
+ const lines = [];
105
+ lines.push(`Cannot set agent provider to "${targetProvider}": OAuth profile "${configuredProfileId}" is configured but not found in the OAuth credential store (~/.comis/auth-profiles.json).`);
106
+ lines.push(`Recovery:`);
107
+ lines.push(` Run \`comis auth login --provider ${targetProvider}\` to (re)authenticate and create the profile, then retry this patch.`);
108
+ lines.push(` Run \`comis auth list\` to see currently stored profiles.`);
109
+ return lines.join("\n");
110
+ }
48
111
  const lines = [];
49
112
  lines.push(`Cannot set agent provider to "${targetProvider}": no API key found.`);
50
113
  if (entry?.apiKeyName) {
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import type { McpClientManager } from "@comis/skills";
8
8
  import type { ComisLogger } from "@comis/infra";
9
+ import type { SecretManager } from "@comis/core";
9
10
  import type { RpcHandler } from "./types.js";
10
11
  /** Dependencies required by MCP management RPC handlers. */
11
12
  export interface McpHandlerDeps {
@@ -17,6 +18,13 @@ export interface McpHandlerDeps {
17
18
  mcpClientManager: McpClientManager;
18
19
  /** Logger for MCP test connection (used by temporary manager). */
19
20
  logger: ComisLogger;
21
+ /**
22
+ * Optional SecretManager for env-ref validation on mcp.connect. When
23
+ * undefined (legacy/test wiring), the env-ref check is skipped — the
24
+ * existing connect behavior is preserved. In production it is always
25
+ * wired via `deps.container.secretManager` from rpc-dispatch.
26
+ */
27
+ secretManager?: SecretManager;
20
28
  }
21
29
  /**
22
30
  * Create a record of MCP management RPC handlers bound to the given deps.