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
@@ -0,0 +1,32 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Background completion runner wiring for daemon startup.
4
+ *
5
+ * Subscribes the runner to background_task:{completed,failed} events after
6
+ * the notification service has been wired (so fallbackNotifyFn is live),
7
+ * and returns a shutdown handle for the daemon's system:shutdown sequence
8
+ * to await before tearing down the executor.
9
+ *
10
+ * Per AGENTS §2.4: composition root + factories. This wiring lives in
11
+ * @comis/daemon (composition root); the actual factory body is in
12
+ * @comis/agent.
13
+ *
14
+ * @module
15
+ */
16
+ import { createBackgroundCompletionRunner, } from "@comis/agent";
17
+ /**
18
+ * Wire the background completion runner from daemon-level dependencies.
19
+ * Call this AFTER setupNotifications so fallbackNotifyFn is wired.
20
+ */
21
+ export function setupBackgroundCompletionRunner(deps) {
22
+ const runner = createBackgroundCompletionRunner({
23
+ eventBus: deps.eventBus,
24
+ getExecutor: deps.getExecutor,
25
+ sessionStore: deps.sessionStore,
26
+ taskManager: deps.taskManager,
27
+ fallbackNotifyFn: deps.fallbackNotifyFn,
28
+ maxBackgroundHops: deps.maxBackgroundHops,
29
+ logger: deps.logger,
30
+ });
31
+ return { runner };
32
+ }
@@ -19,9 +19,16 @@ export interface SetupBackgroundTasksDeps {
19
19
  }
20
20
  /**
21
21
  * Wire the background task subsystem from daemon-level dependencies.
22
- * Creates BackgroundTaskManager with file-based persistence, recovers
23
- * incomplete tasks from the previous daemon run (marking them failed),
24
- * and starts an hourly cleanup timer for stale completed/failed tasks.
22
+ * Creates BackgroundTaskManager with file-based persistence and starts
23
+ * an hourly cleanup timer for stale completed/failed tasks.
24
+ *
25
+ * Note: startup recovery (marking interrupted tasks as failed) is
26
+ * INTENTIONALLY deferred to daemon.ts. The daemon calls it AFTER
27
+ * `setupBackgroundCompletionRunner` has subscribed to
28
+ * background_task:{completed,failed}. If recovery fires before the runner
29
+ * subscribes, the synthesized failure events for restart-interrupted tasks
30
+ * land in an empty handler set and the user never sees the recovery
31
+ * announcement.
25
32
  * @param deps - Daemon-level dependencies
26
33
  * @returns BackgroundTasksContext with manager instance
27
34
  */
@@ -9,9 +9,16 @@ import { createBackgroundTaskManager } from "@comis/agent";
9
9
  import { safePath } from "@comis/core";
10
10
  /**
11
11
  * Wire the background task subsystem from daemon-level dependencies.
12
- * Creates BackgroundTaskManager with file-based persistence, recovers
13
- * incomplete tasks from the previous daemon run (marking them failed),
14
- * and starts an hourly cleanup timer for stale completed/failed tasks.
12
+ * Creates BackgroundTaskManager with file-based persistence and starts
13
+ * an hourly cleanup timer for stale completed/failed tasks.
14
+ *
15
+ * Note: startup recovery (marking interrupted tasks as failed) is
16
+ * INTENTIONALLY deferred to daemon.ts. The daemon calls it AFTER
17
+ * `setupBackgroundCompletionRunner` has subscribed to
18
+ * background_task:{completed,failed}. If recovery fires before the runner
19
+ * subscribes, the synthesized failure events for restart-interrupted tasks
20
+ * land in an empty handler set and the user never sees the recovery
21
+ * announcement.
15
22
  * @param deps - Daemon-level dependencies
16
23
  * @returns BackgroundTasksContext with manager instance
17
24
  */
@@ -21,8 +28,7 @@ export function setupBackgroundTasks(deps) {
21
28
  eventBus: deps.eventBus,
22
29
  logger: deps.logger,
23
30
  });
24
- // Mark incomplete tasks from previous daemon run as failed
25
- manager.recoverOnStartup();
31
+ // Startup recovery is deferred to daemon.ts (after the completion runner subscribes).
26
32
  // Periodic cleanup of stale completed/failed tasks (24h TTL)
27
33
  const cleanupInterval = setInterval(() => manager.cleanup(), 3_600_000); // every hour
28
34
  cleanupInterval.unref(); // don't keep daemon alive for cleanup
@@ -96,7 +96,7 @@ export async function setupChannels(deps) {
96
96
  return;
97
97
  }
98
98
  const workspacePath = deps.workspaceDirs?.get(agentId) ?? "";
99
- const reviewLogger = logger.child({ agentId, module: "memory-review" });
99
+ const reviewLogger = logger.child({ agentId, submodule: "memory-review" });
100
100
  const reviewResult = await runMemoryReview({
101
101
  agentId,
102
102
  tenantId: deps.tenantId ?? container.config.tenantId ?? "default",
@@ -142,6 +142,25 @@ export async function setupChannels(deps) {
142
142
  // Extract session strategy from event payload (defaults: fresh, 3 turns)
143
143
  const sessionStrategy = payload.sessionStrategy ?? "fresh";
144
144
  const maxHistoryTurns = payload.maxHistoryTurns ?? 3;
145
+ // Cadence-aware cache-waste guard: rolling/accumulate strategies on long cadences
146
+ // guarantee cache-write waste because the prompt cache TTL is short (5 min for "cron"
147
+ // operations — see OPERATION_CACHE_DEFAULTS.cron in @comis/agent). Threshold = 2x TTL
148
+ // so we don't warn on borderline-useful 6-9 min cadences. Operator intent is preserved
149
+ // (no auto-downgrade) — this is informational only. Scoped to schedule.kind === "every"
150
+ // because cron-expression cadence is not threaded through the event payload.
151
+ const CRON_CACHE_TTL_2X_MS = 600_000;
152
+ if (sessionStrategy !== "fresh" &&
153
+ payload.cadenceMs !== undefined &&
154
+ payload.cadenceMs > CRON_CACHE_TTL_2X_MS) {
155
+ logger.warn({
156
+ jobName,
157
+ agentId: payload.agentId,
158
+ sessionStrategy,
159
+ cadenceMs: payload.cadenceMs,
160
+ hint: "Cadence exceeds cache TTL by 2x; rolling/accumulate guarantees per-tick cache-write waste. Set sessionStrategy:'fresh' unless cross-tick session memory is essential.",
161
+ errorKind: "config",
162
+ }, "Cron sessionStrategy may waste cache writes at this cadence");
163
+ }
145
164
  // Resolve cron operation model via 5-level priority chain
146
165
  const agentConfig = agents[payload.agentId];
147
166
  let cronOverrides;
@@ -581,7 +581,7 @@ export function setupCrossSession(deps) {
581
581
  maxAgeMs: 3_600_000,
582
582
  maxEntries: 100,
583
583
  eventBus: container.eventBus,
584
- logger: deps.logger?.child({ module: "dead-letter-queue" }),
584
+ logger: deps.logger?.child({ submodule: "dead-letter-queue" }),
585
585
  });
586
586
  // Create announcement batcher for coalescing near-simultaneous sub-agent completions
587
587
  const announcementBatcher = createAnnouncementBatcher({
@@ -5,8 +5,9 @@
5
5
  * Queue two-phase lifecycle resolves the circular dependency between the
6
6
  * queue and channel adapters:
7
7
  * 1. setupDeliveryQueue() creates the adapter immediately (before setupChannels).
8
- * 2. drainAndStartPrune() runs drain + starts prune timer AFTER setupChannels
9
- * populates channelAdapters.
8
+ * 2. drainAndStart() recovers in_flight rows, runs startup drain, then starts
9
+ * both the recurring drain timer and the prune timer AFTER
10
+ * setupChannels populates channelAdapters.
10
11
  * Crash-Safe Delivery Queue.
11
12
  * Session Mirroring.
12
13
  * @module setup-delivery — Delivery subsystem wiring (queue + mirror)
@@ -18,9 +19,9 @@ import type { PluginRegistry } from "@comis/core";
18
19
  export interface DeliveryQueueResult {
19
20
  /** The delivery queue adapter (real or no-op), available immediately. */
20
21
  deliveryQueue: DeliveryQueuePort;
21
- /** Runs startup drain then starts periodic prune timer. Call AFTER setupChannels. */
22
- drainAndStartPrune: () => Promise<void>;
23
- /** Clears the prune interval timer (call on shutdown). */
22
+ /** Recovers in_flight rows, runs startup drain, then starts the recurring drain + prune timers. Call AFTER setupChannels. */
23
+ drainAndStart: () => Promise<void>;
24
+ /** Clears the recurring drain interval AND the prune interval (call on shutdown). */
24
25
  shutdown: () => void;
25
26
  }
26
27
  export declare function setupDeliveryQueue(deps: {
@@ -31,6 +32,14 @@ export declare function setupDeliveryQueue(deps: {
31
32
  logger: ComisLogger;
32
33
  channelAdapters: Map<string, DeliveryAdapter>;
33
34
  }): Promise<DeliveryQueueResult>;
35
+ export declare function drainDeliveryQueue(deps: {
36
+ deliveryQueue: DeliveryQueuePort;
37
+ channelAdapters: Map<string, DeliveryAdapter>;
38
+ eventBus: TypedEventBus;
39
+ logger: ComisLogger;
40
+ drainBudgetMs: number;
41
+ defaultMaxAttempts: number;
42
+ }): Promise<void>;
34
43
  export interface DeliveryMirrorResult {
35
44
  /** The delivery mirror adapter (real or no-op), available immediately. */
36
45
  deliveryMirror: DeliveryMirrorPort;
@@ -6,8 +6,9 @@
6
6
  * Queue two-phase lifecycle resolves the circular dependency between the
7
7
  * queue and channel adapters:
8
8
  * 1. setupDeliveryQueue() creates the adapter immediately (before setupChannels).
9
- * 2. drainAndStartPrune() runs drain + starts prune timer AFTER setupChannels
10
- * populates channelAdapters.
9
+ * 2. drainAndStart() recovers in_flight rows, runs startup drain, then starts
10
+ * both the recurring drain timer and the prune timer AFTER
11
+ * setupChannels populates channelAdapters.
11
12
  * Crash-Safe Delivery Queue.
12
13
  * Session Mirroring.
13
14
  * @module setup-delivery — Delivery subsystem wiring (queue + mirror)
@@ -15,7 +16,7 @@
15
16
  import { createNoOpDeliveryQueue, createNoOpDeliveryMirror } from "@comis/core";
16
17
  import { createSqliteDeliveryQueue, createSqliteDeliveryMirror } from "@comis/memory";
17
18
  import { isPermanentError, computeQueueBackoff } from "@comis/channels";
18
- import { ok } from "@comis/shared";
19
+ import { ok, suppressError } from "@comis/shared";
19
20
  import { createHash } from "node:crypto";
20
21
  // ---------------------------------------------------------------------------
21
22
  // Setup function
@@ -28,28 +29,56 @@ export async function setupDeliveryQueue(deps) {
28
29
  logger.debug("Delivery queue disabled by config");
29
30
  return {
30
31
  deliveryQueue: createNoOpDeliveryQueue(),
31
- drainAndStartPrune: async () => { },
32
+ drainAndStart: async () => { },
32
33
  shutdown: () => { },
33
34
  };
34
35
  }
35
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- db is better-sqlite3 Database; typed as unknown to avoid cross-package type dependency
36
- const deliveryQueue = createSqliteDeliveryQueue(db);
37
+ const deliveryQueue = createSqliteDeliveryQueue(db, eventBus);
37
38
  logger.info({ maxQueueDepth: queueConfig.maxQueueDepth, defaultMaxAttempts: queueConfig.defaultMaxAttempts }, "Delivery queue enabled");
38
39
  let pruneInterval;
39
- // 2. Startup drain + 3. Periodic prune (deferred until channelAdapters populated)
40
- const drainAndStartPrune = async () => {
41
- // --- Drain ---
40
+ let drainInterval;
41
+ // Single-tick gate: in-flight Promise prevents overlapping ticks.
42
+ let draining = null;
43
+ // Inner helper: one drain pass. Reused by startup drain AND each recurring tick.
44
+ const runOneDrainPass = async () => {
45
+ await drainDeliveryQueue({
46
+ deliveryQueue,
47
+ channelAdapters,
48
+ eventBus,
49
+ logger,
50
+ drainBudgetMs: queueConfig.drainBudgetMs,
51
+ defaultMaxAttempts: queueConfig.defaultMaxAttempts,
52
+ });
53
+ };
54
+ // 2. Startup drain + recurring drain timer + prune timer (deferred until channelAdapters populated)
55
+ const drainAndStart = async () => {
56
+ // --- Step 1: Recover in_flight rows. ---
57
+ // Runs UNCONDITIONALLY -- independent of drainOnStartup policy. An 'in_flight'
58
+ // row from a prior crash is a correctness bug regardless of the drain policy.
59
+ const recoverResult = await deliveryQueue.recoverInFlight();
60
+ if (!recoverResult.ok) {
61
+ logger.warn({ err: recoverResult.error, hint: "Could not recover in_flight rows on startup; messages may stall until next restart", errorKind: "internal" }, "Delivery queue: recoverInFlight failed");
62
+ }
63
+ else if (recoverResult.value > 0) {
64
+ logger.info({ recovered: recoverResult.value }, "Delivery queue: recovered in_flight rows to pending");
65
+ }
66
+ // --- Step 2: Startup drain (existing behavior, unchanged). ---
42
67
  if (queueConfig.drainOnStartup) {
43
- await drainDeliveryQueue({
44
- deliveryQueue,
45
- channelAdapters,
46
- eventBus,
47
- logger,
48
- drainBudgetMs: queueConfig.drainBudgetMs,
49
- defaultMaxAttempts: queueConfig.defaultMaxAttempts,
50
- });
68
+ await runOneDrainPass();
51
69
  }
52
- // --- Prune timer ---
70
+ // --- Step 3: Recurring drain timer. ---
71
+ drainInterval = setInterval(() => {
72
+ if (draining)
73
+ return; // single-tick gate
74
+ draining = runOneDrainPass().finally(() => { draining = null; });
75
+ // Fire-and-forget: failures inside drainDeliveryQueue are already logged
76
+ // and do not propagate (it never throws). suppressError satisfies the
77
+ // no-floating-promise lint without altering semantics.
78
+ suppressError(draining, "delivery queue recurring drain tick");
79
+ }, queueConfig.drainIntervalMs);
80
+ drainInterval.unref();
81
+ // --- Step 4: Prune timer (existing behavior, unchanged). ---
53
82
  pruneInterval = setInterval(async () => {
54
83
  const result = await deliveryQueue.pruneExpired();
55
84
  if (result.ok && result.value > 0) {
@@ -59,17 +88,21 @@ export async function setupDeliveryQueue(deps) {
59
88
  pruneInterval.unref();
60
89
  };
61
90
  const shutdown = () => {
91
+ if (drainInterval) {
92
+ clearInterval(drainInterval);
93
+ drainInterval = undefined;
94
+ }
62
95
  if (pruneInterval) {
63
96
  clearInterval(pruneInterval);
64
97
  pruneInterval = undefined;
65
98
  }
66
99
  };
67
- return { deliveryQueue, drainAndStartPrune, shutdown };
100
+ return { deliveryQueue, drainAndStart, shutdown };
68
101
  }
69
102
  // ---------------------------------------------------------------------------
70
103
  // Drain implementation
71
104
  // ---------------------------------------------------------------------------
72
- async function drainDeliveryQueue(deps) {
105
+ export async function drainDeliveryQueue(deps) {
73
106
  const { deliveryQueue, channelAdapters, eventBus, logger, drainBudgetMs, defaultMaxAttempts } = deps;
74
107
  const drainStart = Date.now();
75
108
  const deadline = drainStart + drainBudgetMs;
@@ -108,6 +108,10 @@ export async function setupSchedulers(deps) {
108
108
  payloadKind: job.payload.kind,
109
109
  sessionStrategy: job.sessionStrategy,
110
110
  maxHistoryTurns: job.maxHistoryTurns,
111
+ // Cadence is a literal number only for kind === "every"; cron-expression
112
+ // schedules would require parsing the expression to estimate a cadence,
113
+ // which is out of scope. Consumers must treat undefined as "unknown".
114
+ cadenceMs: job.schedule.kind === "every" ? job.schedule.everyMs : undefined,
111
115
  cronJobModel: job.payload.kind === "agent_turn" ? job.payload.model : undefined,
112
116
  cacheRetention: job.cacheRetention,
113
117
  toolPolicy: job.toolPolicy,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@comis/daemon",
3
3
  "private": true,
4
- "version": "1.0.34",
4
+ "version": "1.0.36",
5
5
  "author": "Moshe Anconina",
6
6
  "license": "Apache-2.0",
7
7
  "description": "Background daemon and orchestrator for the Comis platform",
@@ -9,6 +9,8 @@ export type { RpcAdapterDeps } from "./rpc/rpc-adapters.js";
9
9
  export { WsConnectionManager } from "./rpc/ws-handler.js";
10
10
  export { createMappedWebhookEndpoint } from "./webhook/webhook-endpoint.js";
11
11
  export { getPresetMappings } from "./webhook/webhook-presets.js";
12
+ export { createOAuthCallbackRoute, insertPendingFlow, PENDING_FLOW_TIMEOUT_MS, } from "./oauth/oauth-callback-route.js";
13
+ export type { OAuthCallbackDeps, PendingFlow, } from "./oauth/oauth-callback-route.js";
12
14
  export { createMediaRoutes } from "./web/index.js";
13
15
  export { createOpenaiCompletionsRoute } from "./openai/index.js";
14
16
  export { createOpenaiModelsRoute } from "./openai/index.js";
@@ -16,6 +16,8 @@ export { WsConnectionManager } from "./rpc/ws-handler.js";
16
16
  // Webhook
17
17
  export { createMappedWebhookEndpoint } from "./webhook/webhook-endpoint.js";
18
18
  export { getPresetMappings } from "./webhook/webhook-presets.js";
19
+ // OAuth callback route exports
20
+ export { createOAuthCallbackRoute, insertPendingFlow, PENDING_FLOW_TIMEOUT_MS, } from "./oauth/oauth-callback-route.js";
19
21
  // Web -- media routes
20
22
  export { createMediaRoutes } from "./web/index.js";
21
23
  // OpenAI compatibility endpoints
@@ -0,0 +1,66 @@
1
+ /**
2
+ * OAuth callback route for the Comis gateway.
3
+ *
4
+ * Mounted at `GET /callback/:provider` via `app.route("/oauth", subApp)`. The
5
+ * handler validates code+state, looks up the state in the in-memory pending-
6
+ * flow map, verifies path-vs-flow provider match, deletes the entry BEFORE
7
+ * the token exchange (one-time-use invariant), exchanges the code at
8
+ * auth.openai.com/oauth/token, resolves identity via
9
+ * resolveCodexAuthIdentity, persists via OAuthCredentialStorePort.set, emits
10
+ * auth:profile_bootstrapped, and returns a static "Login Successful" HTML
11
+ * page (200) on success or a "Login Failed" HTML page (400/500) on failure.
12
+ *
13
+ * HTTP method is GET, NOT POST (OAuth servers always redirect with GET).
14
+ * Logging discipline (CLAUDE.md): submodule: "oauth-callback"
15
+ * on every line; NEVER log code/state/verifier/access/refresh values.
16
+ *
17
+ * @module
18
+ */
19
+ import { Hono } from "hono";
20
+ import type { OAuthCredentialStorePort, TypedEventBus } from "@comis/core";
21
+ import type { GatewayLogger } from "../server/hono-server.js";
22
+ /** 5-minute pending-flow expiry. Exported for test parity. */
23
+ export declare const PENDING_FLOW_TIMEOUT_MS: number;
24
+ /**
25
+ * Pending-flow entry stored in the in-memory map keyed by `state`.
26
+ * Created by insertPendingFlow at flow-initiation time; consumed by the
27
+ * /callback/:provider handler on a successful state-match.
28
+ */
29
+ export interface PendingFlow {
30
+ /** PKCE code_verifier generated at flow initiation. */
31
+ verifier: string;
32
+ /** "openai-codex" — used for path-vs-flow provider validation. */
33
+ provider: string;
34
+ /** Date.now() at insertion time. */
35
+ createdAt: number;
36
+ /**
37
+ * Cleanup timer reference. The handler clearTimeout(timer) on consume;
38
+ * the auto-expiry path (PENDING_FLOW_TIMEOUT_MS) deletes the entry.
39
+ */
40
+ timer: ReturnType<typeof setTimeout>;
41
+ }
42
+ /** Dependencies for createOAuthCallbackRoute. */
43
+ export interface OAuthCallbackDeps {
44
+ readonly credentialStore: OAuthCredentialStorePort;
45
+ readonly eventBus: TypedEventBus;
46
+ readonly logger: GatewayLogger;
47
+ /** State -> PendingFlow map; mutated by handler + insertPendingFlow. */
48
+ readonly pendingFlows: Map<string, PendingFlow>;
49
+ }
50
+ /**
51
+ * Seed the pending-flow map with a new state -> PendingFlow entry, scheduling
52
+ * a 5-minute auto-delete cleanup timer.
53
+ *
54
+ * The caller is responsible for generating `state` via crypto.randomBytes(16)
55
+ * — never hand-roll a PRNG.
56
+ */
57
+ export declare function insertPendingFlow(map: Map<string, PendingFlow>, state: string, flow: Omit<PendingFlow, "timer">, logger: GatewayLogger): void;
58
+ /**
59
+ * Create the OAuth callback Hono sub-app.
60
+ *
61
+ * Mount via:
62
+ * const app = new Hono();
63
+ * app.route("/oauth", createOAuthCallbackRoute(deps));
64
+ * Resulting URL: GET /oauth/callback/:provider
65
+ */
66
+ export declare function createOAuthCallbackRoute(deps: OAuthCallbackDeps): Hono;
@@ -0,0 +1,212 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * OAuth callback route for the Comis gateway.
4
+ *
5
+ * Mounted at `GET /callback/:provider` via `app.route("/oauth", subApp)`. The
6
+ * handler validates code+state, looks up the state in the in-memory pending-
7
+ * flow map, verifies path-vs-flow provider match, deletes the entry BEFORE
8
+ * the token exchange (one-time-use invariant), exchanges the code at
9
+ * auth.openai.com/oauth/token, resolves identity via
10
+ * resolveCodexAuthIdentity, persists via OAuthCredentialStorePort.set, emits
11
+ * auth:profile_bootstrapped, and returns a static "Login Successful" HTML
12
+ * page (200) on success or a "Login Failed" HTML page (400/500) on failure.
13
+ *
14
+ * HTTP method is GET, NOT POST (OAuth servers always redirect with GET).
15
+ * Logging discipline (CLAUDE.md): submodule: "oauth-callback"
16
+ * on every line; NEVER log code/state/verifier/access/refresh values.
17
+ *
18
+ * @module
19
+ */
20
+ import { Hono } from "hono";
21
+ import { resolveCodexAuthIdentity, rewriteOAuthError, redactEmailForLog, } from "@comis/agent";
22
+ // ---------------------------------------------------------------------------
23
+ // Constants
24
+ // ---------------------------------------------------------------------------
25
+ /** 5-minute pending-flow expiry. Exported for test parity. */
26
+ export const PENDING_FLOW_TIMEOUT_MS = 5 * 60_000;
27
+ /** OpenAI Codex token endpoint — same as oauth-token-manager.ts:301. */
28
+ const OPENAI_TOKEN_URL = "https://auth.openai.com/oauth/token";
29
+ /** Public OpenAI Codex client_id (NOT a comis secret — per pi-ai source). */
30
+ const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
31
+ /** Redirect URI matches pi-ai device-callback convention. */
32
+ const OPENAI_CODEX_DEVICE_CALLBACK_URL = "https://auth.openai.com/deviceauth/callback";
33
+ // ---------------------------------------------------------------------------
34
+ // Internal helpers
35
+ // ---------------------------------------------------------------------------
36
+ function escapeHtml(s) {
37
+ return s
38
+ .replace(/&/g, "&amp;")
39
+ .replace(/</g, "&lt;")
40
+ .replace(/>/g, "&gt;")
41
+ .replace(/"/g, "&quot;")
42
+ .replace(/'/g, "&#39;");
43
+ }
44
+ function oauthSuccessHtml(message) {
45
+ return [
46
+ "<!DOCTYPE html>",
47
+ '<html><head><meta charset="utf-8"><title>Login Successful</title>',
48
+ "<style>body{font-family:system-ui,sans-serif;max-width:480px;margin:8em auto;padding:2em;text-align:center;color:#222}h1{color:#0a7d2c}</style>",
49
+ "</head><body><h1>Login Successful</h1>",
50
+ `<p>${escapeHtml(message)}</p>`,
51
+ "<p>You can close this window.</p>",
52
+ "</body></html>",
53
+ ].join("");
54
+ }
55
+ function oauthErrorHtml(message) {
56
+ return [
57
+ "<!DOCTYPE html>",
58
+ '<html><head><meta charset="utf-8"><title>Login Failed</title>',
59
+ "<style>body{font-family:system-ui,sans-serif;max-width:480px;margin:8em auto;padding:2em;text-align:center;color:#222}h1{color:#b00020}</style>",
60
+ "</head><body><h1>Login Failed</h1>",
61
+ `<p>${escapeHtml(message)}</p>`,
62
+ "</body></html>",
63
+ ].join("");
64
+ }
65
+ /**
66
+ * Exchange an authorization_code at OpenAI's /oauth/token endpoint.
67
+ *
68
+ * Mirrors refreshOpenAICodexTokenLocal (oauth-token-manager.ts:298-385) but
69
+ * uses grant_type=authorization_code with code + code_verifier. Throws on
70
+ * non-OK status; the public boundary catches and routes through
71
+ * rewriteOAuthError.
72
+ */
73
+ async function exchangeAuthorizationCode(params) {
74
+ const body = new URLSearchParams({
75
+ grant_type: "authorization_code",
76
+ code: params.code,
77
+ redirect_uri: OPENAI_CODEX_DEVICE_CALLBACK_URL,
78
+ client_id: OPENAI_CODEX_CLIENT_ID,
79
+ code_verifier: params.verifier,
80
+ });
81
+ const response = await fetch(OPENAI_TOKEN_URL, {
82
+ method: "POST",
83
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
84
+ body,
85
+ });
86
+ if (!response.ok) {
87
+ let bodyText = "";
88
+ try {
89
+ bodyText = await response.text();
90
+ }
91
+ catch {
92
+ /* defensive — body read may fail */
93
+ }
94
+ // Surface the wire error verbatim so rewriteOAuthError can detect
95
+ // invalid_grant / unsupported_country_region_territory substrings.
96
+ throw new Error(`OAuth token exchange failed: HTTP ${response.status} ${bodyText}`);
97
+ }
98
+ const json = (await response.json());
99
+ return {
100
+ access: json.access_token,
101
+ refresh: json.refresh_token,
102
+ expires: Date.now() + json.expires_in * 1000,
103
+ };
104
+ }
105
+ // ---------------------------------------------------------------------------
106
+ // Public boundary
107
+ // ---------------------------------------------------------------------------
108
+ /**
109
+ * Seed the pending-flow map with a new state -> PendingFlow entry, scheduling
110
+ * a 5-minute auto-delete cleanup timer.
111
+ *
112
+ * The caller is responsible for generating `state` via crypto.randomBytes(16)
113
+ * — never hand-roll a PRNG.
114
+ */
115
+ export function insertPendingFlow(map, state, flow, logger) {
116
+ const timer = setTimeout(() => {
117
+ map.delete(state);
118
+ logger.debug({ provider: flow.provider, submodule: "oauth-callback" }, "Pending OAuth flow expired");
119
+ }, PENDING_FLOW_TIMEOUT_MS);
120
+ map.set(state, { ...flow, timer });
121
+ }
122
+ /**
123
+ * Create the OAuth callback Hono sub-app.
124
+ *
125
+ * Mount via:
126
+ * const app = new Hono();
127
+ * app.route("/oauth", createOAuthCallbackRoute(deps));
128
+ * Resulting URL: GET /oauth/callback/:provider
129
+ */
130
+ export function createOAuthCallbackRoute(deps) {
131
+ const app = new Hono();
132
+ app.get("/callback/:provider", async (c) => {
133
+ const provider = c.req.param("provider");
134
+ const code = c.req.query("code");
135
+ const state = c.req.query("state");
136
+ if (!state || !code) {
137
+ return c.html(oauthErrorHtml("Missing code or state parameter"), 400);
138
+ }
139
+ const flow = deps.pendingFlows.get(state);
140
+ if (!flow) {
141
+ // No log — stale browser tab is a benign user error (debug-level
142
+ // logging acceptable but not required by tests; keep silent here).
143
+ return c.html(oauthErrorHtml("Invalid or expired state"), 400);
144
+ }
145
+ if (flow.provider !== provider) {
146
+ // Preserve the entry — the legitimate provider's callback may still
147
+ // arrive.
148
+ return c.html(oauthErrorHtml("Provider mismatch"), 400);
149
+ }
150
+ // One-time-use: cancel the timer + remove the entry BEFORE the
151
+ // exchange so even a failed exchange does not leave a reusable state.
152
+ clearTimeout(flow.timer);
153
+ deps.pendingFlows.delete(state);
154
+ try {
155
+ const tokens = await exchangeAuthorizationCode({
156
+ code,
157
+ verifier: flow.verifier,
158
+ });
159
+ const identity = resolveCodexAuthIdentity({
160
+ accessToken: tokens.access,
161
+ });
162
+ const identityKey = identity.email ?? identity.profileName;
163
+ if (!identityKey) {
164
+ // Treat as identity_decode_failed; rewriteOAuthError will route
165
+ // the substring "Failed to extract accountId" to the right code.
166
+ throw new Error("Failed to extract accountId — identity decode failed");
167
+ }
168
+ const profileId = `${provider}:${identityKey}`;
169
+ const profile = {
170
+ provider,
171
+ profileId,
172
+ access: tokens.access,
173
+ refresh: tokens.refresh,
174
+ expires: tokens.expires,
175
+ email: identity.email,
176
+ displayName: identity.profileName,
177
+ version: 1,
178
+ };
179
+ const writeResult = await deps.credentialStore.set(profileId, profile);
180
+ if (!writeResult.ok) {
181
+ throw new Error(`Failed to persist OAuth profile: ${writeResult.error.message}`);
182
+ }
183
+ const identityForEvent = redactEmailForLog(identity.email) ??
184
+ identity.profileName ??
185
+ identityKey;
186
+ deps.eventBus.emit("auth:profile_bootstrapped", {
187
+ provider,
188
+ profileId,
189
+ identity: identityForEvent,
190
+ timestamp: Date.now(),
191
+ });
192
+ deps.logger.info({
193
+ provider,
194
+ profileId,
195
+ identity: identityForEvent,
196
+ submodule: "oauth-callback",
197
+ }, "Gateway OAuth callback success");
198
+ return c.html(oauthSuccessHtml("Login successful — you can close this window and return to Comis."));
199
+ }
200
+ catch (caught) {
201
+ const rewritten = rewriteOAuthError(caught);
202
+ deps.logger.warn({
203
+ provider,
204
+ errorKind: rewritten.errorKind,
205
+ hint: rewritten.hint,
206
+ submodule: "oauth-callback",
207
+ }, "OAuth callback exchange failed");
208
+ return c.html(oauthErrorHtml(rewritten.userMessage), 500);
209
+ }
210
+ });
211
+ return app;
212
+ }
@@ -7,6 +7,8 @@ import type { HmacAlgorithm } from "../webhook/hmac-verifier.js";
7
7
  import { type TokenStore } from "../auth/token-auth.js";
8
8
  import { WsConnectionManager } from "../rpc/ws-handler.js";
9
9
  import { type WebhookHandler } from "../webhook/webhook-endpoint.js";
10
+ import { type PendingFlow } from "../oauth/oauth-callback-route.js";
11
+ import type { OAuthCredentialStorePort } from "@comis/core";
10
12
  /**
11
13
  * Logger interface for gateway server (minimal pino-compatible).
12
14
  */
@@ -41,6 +43,18 @@ export interface GatewayServerDeps {
41
43
  algorithm?: HmacAlgorithm;
42
44
  headerName?: string;
43
45
  };
46
+ /**
47
+ * Optional OAuth callback deps. When provided, the gateway mounts
48
+ * GET /oauth/callback/:provider for browser-redirect OAuth flows
49
+ * (web-UI-initiated logins). Pending-flow map is owned by the caller
50
+ * (e.g., setup-gateway.ts) so daemon restart cleanly drops all in-flight
51
+ * states.
52
+ */
53
+ readonly oauthCallbackDeps?: {
54
+ credentialStore: OAuthCredentialStorePort;
55
+ eventBus: TypedEventBus;
56
+ pendingFlows: Map<string, PendingFlow>;
57
+ };
44
58
  /** Optional hook runner for lifecycle hooks (no-op when absent) */
45
59
  readonly hookRunner?: HookRunner;
46
60
  /** Optional web dashboard deps (mount REST/SSE/static when provided) */