comisai 1.0.36 → 1.0.37

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 (239) hide show
  1. package/node_modules/@comis/agent/dist/background/auto-background-middleware.js +9 -0
  2. package/node_modules/@comis/agent/dist/background/background-task-manager.d.ts +22 -2
  3. package/node_modules/@comis/agent/dist/background/background-task-manager.js +48 -41
  4. package/node_modules/@comis/agent/dist/background/background-task-persistence.js +28 -5
  5. package/node_modules/@comis/agent/dist/background/background-task-types.d.ts +49 -0
  6. package/node_modules/@comis/agent/dist/background/completion-dispatcher.d.ts +130 -0
  7. package/node_modules/@comis/agent/dist/background/completion-dispatcher.js +215 -0
  8. package/node_modules/@comis/agent/dist/background/completion-runner.d.ts +10 -1
  9. package/node_modules/@comis/agent/dist/background/completion-runner.js +98 -15
  10. package/node_modules/@comis/agent/dist/background/index.d.ts +6 -1
  11. package/node_modules/@comis/agent/dist/background/index.js +2 -0
  12. package/node_modules/@comis/agent/dist/background/session-resolver.d.ts +85 -0
  13. package/node_modules/@comis/agent/dist/background/session-resolver.js +78 -0
  14. package/node_modules/@comis/agent/dist/bootstrap/sections/messaging-sections.js +1 -0
  15. package/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +3 -3
  16. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.d.ts +30 -2
  17. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +51 -2
  18. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +22 -0
  19. package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +2 -2
  20. package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.d.ts +1 -5
  21. package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.js +2 -14
  22. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +26 -0
  23. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +3 -0
  24. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +9 -0
  25. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +73 -2
  26. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +10 -10
  27. package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +14 -14
  28. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.d.ts +11 -13
  29. package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.js +14 -15
  30. package/node_modules/@comis/agent/dist/executor/capability-index-context.d.ts +72 -0
  31. package/node_modules/@comis/agent/dist/executor/capability-index-context.js +329 -0
  32. package/node_modules/@comis/agent/dist/executor/drain-helper.d.ts +122 -0
  33. package/node_modules/@comis/agent/dist/executor/drain-helper.js +173 -0
  34. package/node_modules/@comis/agent/dist/executor/error-classifier.js +2 -2
  35. package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +48 -4
  36. package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +134 -31
  37. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +7 -0
  38. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +25 -4
  39. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.d.ts +18 -1
  40. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +19 -16
  41. package/node_modules/@comis/agent/dist/executor/jit-guide-injector.d.ts +11 -2
  42. package/node_modules/@comis/agent/dist/executor/jit-guide-injector.js +16 -2
  43. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +8 -2
  44. package/node_modules/@comis/agent/dist/executor/pi-executor.js +25 -12
  45. package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +9 -1
  46. package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +15 -1
  47. package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +18 -27
  48. package/node_modules/@comis/agent/dist/executor/tool-deferral.js +29 -38
  49. package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +1 -1
  50. package/node_modules/@comis/agent/dist/model/model-scanner.js +1 -1
  51. package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.d.ts +11 -1
  52. package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.js +19 -22
  53. package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +16 -2
  54. package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.d.ts +1 -1
  55. package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.js +5 -5
  56. package/node_modules/@comis/agent/dist/workspace/data-env.d.ts +38 -0
  57. package/node_modules/@comis/agent/dist/workspace/data-env.js +56 -0
  58. package/node_modules/@comis/agent/dist/workspace/index.d.ts +1 -0
  59. package/node_modules/@comis/agent/dist/workspace/index.js +1 -0
  60. package/node_modules/@comis/agent/dist/workspace/templates.js +5 -1
  61. package/node_modules/@comis/agent/package.json +1 -1
  62. package/node_modules/@comis/channels/dist/index.d.ts +1 -1
  63. package/node_modules/@comis/channels/dist/index.js +1 -1
  64. package/node_modules/@comis/channels/dist/shared/channel-manager.d.ts +9 -3
  65. package/node_modules/@comis/channels/dist/shared/inbound-gate.d.ts +1 -1
  66. package/node_modules/@comis/channels/dist/shared/inbound-gate.js +22 -7
  67. package/node_modules/@comis/channels/dist/shared/inbound-pipeline.d.ts +10 -3
  68. package/node_modules/@comis/channels/dist/shared/inbound-route.d.ts +1 -1
  69. package/node_modules/@comis/channels/dist/shared/inbound-route.js +13 -2
  70. package/node_modules/@comis/channels/dist/shared/response-filter.d.ts +11 -24
  71. package/node_modules/@comis/channels/dist/shared/response-filter.js +25 -53
  72. package/node_modules/@comis/channels/package.json +1 -1
  73. package/node_modules/@comis/cli/dist/commands/providers.d.ts +1 -2
  74. package/node_modules/@comis/cli/dist/commands/providers.js +5 -6
  75. package/node_modules/@comis/cli/package.json +1 -1
  76. package/node_modules/@comis/core/dist/config/field-metadata.js +2 -0
  77. package/node_modules/@comis/core/dist/config/immutable-keys.js +4 -1
  78. package/node_modules/@comis/core/dist/config/index.d.ts +4 -0
  79. package/node_modules/@comis/core/dist/config/index.js +2 -0
  80. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +0 -792
  81. package/node_modules/@comis/core/dist/config/schema-approvals.d.ts +0 -14
  82. package/node_modules/@comis/core/dist/config/schema-auto-reply-engine.d.ts +0 -6
  83. package/node_modules/@comis/core/dist/config/schema-background-tasks.d.ts +0 -12
  84. package/node_modules/@comis/core/dist/config/schema-browser.d.ts +0 -18
  85. package/node_modules/@comis/core/dist/config/schema-channel.d.ts +0 -158
  86. package/node_modules/@comis/core/dist/config/schema-coalescer.d.ts +0 -5
  87. package/node_modules/@comis/core/dist/config/schema-daemon.d.ts +0 -32
  88. package/node_modules/@comis/core/dist/config/schema-delivery.d.ts +0 -18
  89. package/node_modules/@comis/core/dist/config/schema-documentation.d.ts +0 -12
  90. package/node_modules/@comis/core/dist/config/schema-embedding.d.ts +0 -20
  91. package/node_modules/@comis/core/dist/config/schema-envelope.d.ts +0 -15
  92. package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +0 -37
  93. package/node_modules/@comis/core/dist/config/schema-gemini-cache.d.ts +0 -2
  94. package/node_modules/@comis/core/dist/config/schema-integrations.d.ts +0 -318
  95. package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.d.ts +0 -18
  96. package/node_modules/@comis/core/dist/config/schema-memory-review.d.ts +0 -7
  97. package/node_modules/@comis/core/dist/config/schema-memory.d.ts +0 -16
  98. package/node_modules/@comis/core/dist/config/schema-messages.d.ts +0 -8
  99. package/node_modules/@comis/core/dist/config/schema-models.d.ts +0 -15
  100. package/node_modules/@comis/core/dist/config/schema-notification.d.ts +0 -5
  101. package/node_modules/@comis/core/dist/config/schema-oauth.d.ts +0 -5
  102. package/node_modules/@comis/core/dist/config/schema-observability.d.ts +0 -38
  103. package/node_modules/@comis/core/dist/config/schema-output-retention.d.ts +34 -0
  104. package/node_modules/@comis/core/dist/config/schema-output-retention.js +48 -0
  105. package/node_modules/@comis/core/dist/config/schema-plugins.d.ts +0 -8
  106. package/node_modules/@comis/core/dist/config/schema-providers.d.ts +0 -64
  107. package/node_modules/@comis/core/dist/config/schema-queue.d.ts +0 -58
  108. package/node_modules/@comis/core/dist/config/schema-response-prefix.d.ts +0 -2
  109. package/node_modules/@comis/core/dist/config/schema-retry.d.ts +0 -6
  110. package/node_modules/@comis/core/dist/config/schema-scheduler.d.ts +0 -39
  111. package/node_modules/@comis/core/dist/config/schema-secrets.d.ts +0 -3
  112. package/node_modules/@comis/core/dist/config/schema-security.d.ts +0 -18
  113. package/node_modules/@comis/core/dist/config/schema-send-policy.d.ts +0 -13
  114. package/node_modules/@comis/core/dist/config/schema-sender-trust-display.d.ts +0 -5
  115. package/node_modules/@comis/core/dist/config/schema-serializer.js +2 -0
  116. package/node_modules/@comis/core/dist/config/schema-skills.d.ts +0 -61
  117. package/node_modules/@comis/core/dist/config/schema-streaming.d.ts +0 -38
  118. package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.d.ts +0 -3
  119. package/node_modules/@comis/core/dist/config/schema-tooling.d.ts +87 -0
  120. package/node_modules/@comis/core/dist/config/schema-tooling.js +152 -0
  121. package/node_modules/@comis/core/dist/config/schema-verbosity.d.ts +0 -12
  122. package/node_modules/@comis/core/dist/config/schema-webhooks.d.ts +0 -40
  123. package/node_modules/@comis/core/dist/config/schema.d.ts +41 -38
  124. package/node_modules/@comis/core/dist/config/schema.js +6 -0
  125. package/node_modules/@comis/core/dist/context/context.d.ts +0 -4
  126. package/node_modules/@comis/core/dist/domain/approval-request.d.ts +0 -17
  127. package/node_modules/@comis/core/dist/domain/background-task-origin.d.ts +0 -10
  128. package/node_modules/@comis/core/dist/domain/delivery-origin.d.ts +0 -5
  129. package/node_modules/@comis/core/dist/domain/execution-graph.d.ts +0 -48
  130. package/node_modules/@comis/core/dist/domain/memory-entry.d.ts +0 -3
  131. package/node_modules/@comis/core/dist/domain/model-compat.d.ts +0 -4
  132. package/node_modules/@comis/core/dist/domain/normalized-message.d.ts +0 -15
  133. package/node_modules/@comis/core/dist/domain/provider-capabilities.d.ts +0 -6
  134. package/node_modules/@comis/core/dist/domain/rich-message.d.ts +0 -14
  135. package/node_modules/@comis/core/dist/domain/subagent-context-config.d.ts +0 -22
  136. package/node_modules/@comis/core/dist/domain/subagent-context-types.d.ts +0 -8
  137. package/node_modules/@comis/core/dist/event-bus/events-agent.d.ts +31 -0
  138. package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +5 -0
  139. package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
  140. package/node_modules/@comis/core/dist/exports/config.js +3 -1
  141. package/node_modules/@comis/core/dist/exports/hooks.d.ts +1 -1
  142. package/node_modules/@comis/core/dist/exports/ports.d.ts +2 -2
  143. package/node_modules/@comis/core/dist/exports/ports.js +1 -1
  144. package/node_modules/@comis/core/dist/ports/channel-plugin.d.ts +0 -13
  145. package/node_modules/@comis/core/dist/ports/index.d.ts +2 -0
  146. package/node_modules/@comis/core/dist/ports/index.js +4 -0
  147. package/node_modules/@comis/core/dist/ports/no-op-tool-capability.d.ts +30 -0
  148. package/node_modules/@comis/core/dist/ports/no-op-tool-capability.js +47 -0
  149. package/node_modules/@comis/core/dist/ports/tool-capability.d.ts +165 -0
  150. package/node_modules/@comis/core/dist/ports/tool-capability.js +15 -0
  151. package/node_modules/@comis/core/dist/security/audit.d.ts +0 -11
  152. package/node_modules/@comis/core/dist/tool-metadata.d.ts +21 -1
  153. package/node_modules/@comis/core/dist/tool-metadata.js +1 -1
  154. package/node_modules/@comis/core/package.json +1 -1
  155. package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +1 -1
  156. package/node_modules/@comis/daemon/dist/daemon.js +89 -14
  157. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.d.ts +1 -1
  158. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +1 -1
  159. package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.js +2 -2
  160. package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +1 -1
  161. package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +1 -1
  162. package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +2 -2
  163. package/node_modules/@comis/daemon/dist/sub-agent-runner.d.ts +18 -0
  164. package/node_modules/@comis/daemon/dist/sub-agent-runner.js +41 -9
  165. package/node_modules/@comis/daemon/dist/wiring/index.d.ts +2 -0
  166. package/node_modules/@comis/daemon/dist/wiring/index.js +1 -0
  167. package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +36 -2
  168. package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +45 -8
  169. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.d.ts +28 -9
  170. package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.js +36 -9
  171. package/node_modules/@comis/daemon/dist/wiring/setup-background-tasks.js +2 -2
  172. package/node_modules/@comis/daemon/dist/wiring/setup-channels.d.ts +9 -2
  173. package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +15 -9
  174. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.d.ts +20 -5
  175. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +20 -15
  176. package/node_modules/@comis/daemon/dist/wiring/setup-delivery.js +14 -2
  177. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.d.ts +4 -6
  178. package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +3 -5
  179. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.d.ts +20 -5
  180. package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +11 -2
  181. package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.d.ts +89 -0
  182. package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.js +212 -0
  183. package/node_modules/@comis/daemon/dist/wiring/setup-tools.d.ts +18 -4
  184. package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +29 -10
  185. package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.d.ts +75 -0
  186. package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.js +253 -0
  187. package/node_modules/@comis/daemon/package.json +1 -1
  188. package/node_modules/@comis/gateway/dist/webhook/webhook-endpoint.d.ts +0 -4
  189. package/node_modules/@comis/gateway/package.json +1 -1
  190. package/node_modules/@comis/infra/package.json +1 -1
  191. package/node_modules/@comis/memory/package.json +1 -1
  192. package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +0 -42
  193. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +29 -8
  194. package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +19 -7
  195. package/node_modules/@comis/scheduler/dist/system-events/system-event-types.d.ts +0 -3
  196. package/node_modules/@comis/scheduler/dist/tasks/task-types.d.ts +0 -17
  197. package/node_modules/@comis/scheduler/package.json +1 -1
  198. package/node_modules/@comis/shared/dist/index.d.ts +3 -0
  199. package/node_modules/@comis/shared/dist/index.js +4 -0
  200. package/node_modules/@comis/shared/dist/mcp-tool-name.d.ts +78 -0
  201. package/node_modules/@comis/shared/dist/mcp-tool-name.js +92 -0
  202. package/node_modules/@comis/shared/dist/silent-tokens.d.ts +38 -0
  203. package/node_modules/@comis/shared/dist/silent-tokens.js +51 -0
  204. package/node_modules/@comis/shared/dist/visible-delivery.d.ts +28 -0
  205. package/node_modules/@comis/shared/dist/visible-delivery.js +16 -0
  206. package/node_modules/@comis/shared/package.json +1 -1
  207. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.d.ts +2 -13
  208. package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.js +3 -21
  209. package/node_modules/@comis/skills/dist/bridge/tool-metadata-enforcement.js +1 -1
  210. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +4 -4
  211. package/node_modules/@comis/skills/dist/builtin/exec-tool.d.ts +55 -9
  212. package/node_modules/@comis/skills/dist/builtin/exec-tool.js +383 -19
  213. package/node_modules/@comis/skills/dist/builtin/install-detour.d.ts +67 -0
  214. package/node_modules/@comis/skills/dist/builtin/install-detour.js +342 -0
  215. package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.js +5 -5
  216. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +2 -2
  217. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +2 -2
  218. package/node_modules/@comis/skills/dist/builtin/platform/message-tool.js +18 -0
  219. package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.d.ts +18 -1
  220. package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.js +18 -2
  221. package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.js +3 -3
  222. package/node_modules/@comis/skills/dist/builtin/process-registry.d.ts +14 -0
  223. package/node_modules/@comis/skills/dist/builtin/process-tool.d.ts +24 -4
  224. package/node_modules/@comis/skills/dist/builtin/process-tool.js +25 -7
  225. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.d.ts +1 -1
  226. package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.js +9 -0
  227. package/node_modules/@comis/skills/dist/index.d.ts +4 -1
  228. package/node_modules/@comis/skills/dist/index.js +3 -1
  229. package/node_modules/@comis/skills/dist/manifest/capability-parser.d.ts +44 -0
  230. package/node_modules/@comis/skills/dist/manifest/capability-parser.js +68 -0
  231. package/node_modules/@comis/skills/dist/manifest/schema.d.ts +44 -37
  232. package/node_modules/@comis/skills/dist/manifest/schema.js +35 -0
  233. package/node_modules/@comis/skills/dist/registry/discovery.d.ts +8 -0
  234. package/node_modules/@comis/skills/dist/registry/discovery.js +10 -3
  235. package/node_modules/@comis/skills/dist/registry/skill-registry.d.ts +45 -1
  236. package/node_modules/@comis/skills/dist/registry/skill-registry.js +70 -7
  237. package/node_modules/@comis/skills/package.json +1 -1
  238. package/node_modules/@comis/web/package.json +1 -1
  239. package/package.json +21 -21
@@ -22,6 +22,34 @@ import { classifyAbortReason, buildAnnouncementMessage, deliverAnnouncement, del
22
22
  * Parent agents may call slow tools (image generation at 120s, web search, etc.)
23
23
  * in response to announcements. 30s caused premature fallback + duplicate delivery. */
24
24
  export const ANNOUNCE_PARENT_TIMEOUT_MS = 300_000;
25
+ /**
26
+ * Build the composite-key triple from a SubAgentRun for resolver lookups
27
+ * (R3, B37). Sub-agent runs only carry a formatted `sessionKey` string +
28
+ * `agentId` + optional announce-channel context, so:
29
+ * - agentId -> run.agentId
30
+ * - channelType-> run.announceChannelType ?? "sub-agent"
31
+ * - channelId -> run.announceChannelId
32
+ * ?? parseFormattedSessionKey(run.sessionKey)?.channelId
33
+ * ?? run.sessionKey (last-resort: the formatted key
34
+ * itself; never empty so the
35
+ * resolver's empty-field guard
36
+ * does not trip)
37
+ *
38
+ * The "sub-agent" channelType fallback acknowledges that production
39
+ * SubAgentRun does not carry the original inbound channelType -- the
40
+ * abort path is best-effort regardless (the SDK session may already be
41
+ * in cleanup), so a no-op when the resolver does not find a handle is
42
+ * acceptable. There is a runtime semantic gap (deviation: composite-key
43
+ * triple vs registered formatted-key shape).
44
+ */
45
+ function deriveCompositeForRun(run) {
46
+ const parsed = parseFormattedSessionKey(run.sessionKey);
47
+ return {
48
+ agentId: run.agentId,
49
+ channelType: run.announceChannelType ?? "sub-agent",
50
+ channelId: run.announceChannelId ?? parsed?.channelId ?? run.sessionKey,
51
+ };
52
+ }
25
53
  // Re-export extracted result processor types and functions for backward compatibility
26
54
  export { classifyAbortReason, buildAnnouncementMessage, deliverFailureNotification, sweepResultFiles, persistFailureRecord, validateOutputs } from "./sub-agent-result-processor.js";
27
55
  // ---------------------------------------------------------------------------
@@ -181,9 +209,9 @@ export function createSubAgentRunner(deps) {
181
209
  runtimeMs: runningDurationMs,
182
210
  }, deps.logger), "ghost-sweep-failure-record");
183
211
  }
184
- // Abort SDK session (best-effort)
185
- if (deps.activeRunRegistry) {
186
- const handle = deps.activeRunRegistry.get(run.sessionKey);
212
+ // Abort SDK session (best-effort, R3 / B37 -- composite-key resolver)
213
+ if (deps.sessionResolver) {
214
+ const handle = deps.sessionResolver.resolveActiveSession(deriveCompositeForRun(run));
187
215
  if (handle) {
188
216
  // eslint-disable-next-line no-restricted-syntax -- intentional fire-and-forget
189
217
  handle.abort().catch(() => { });
@@ -1000,9 +1028,12 @@ export function createSubAgentRunner(deps) {
1000
1028
  runtimeMs,
1001
1029
  }, deps.logger), "watchdog-failure-record");
1002
1030
  }
1003
- // Abort SDK session (keyed by sessionKey, NOT runId)
1004
- if (deps.activeRunRegistry) {
1005
- const handle = deps.activeRunRegistry.get(run.sessionKey);
1031
+ // Abort SDK session via composite-key resolver (R3 / B37). The
1032
+ // previous lookup keyed on the formatted sessionKey only; the
1033
+ // resolver makes the `(agentId, channelType, channelId)` triple
1034
+ // explicit so multi-agent collisions are distinguishable.
1035
+ if (deps.sessionResolver) {
1036
+ const handle = deps.sessionResolver.resolveActiveSession(deriveCompositeForRun(run));
1006
1037
  if (handle) {
1007
1038
  handle.abort().catch((abortErr) => {
1008
1039
  deps.logger?.debug({ runId, err: abortErr }, "Watchdog SDK abort best-effort failed");
@@ -1133,9 +1164,10 @@ export function createSubAgentRunner(deps) {
1133
1164
  runtimeMs: run.completedAt - run.startedAt,
1134
1165
  }, deps.logger), "kill-failure-record");
1135
1166
  }
1136
- // Abort the in-flight SDK session to stop LLM API calls (best-effort)
1137
- if (deps.activeRunRegistry) {
1138
- const handle = deps.activeRunRegistry.get(run.sessionKey);
1167
+ // Abort the in-flight SDK session via composite-key resolver
1168
+ // (R3 / B37, best-effort).
1169
+ if (deps.sessionResolver) {
1170
+ const handle = deps.sessionResolver.resolveActiveSession(deriveCompositeForRun(run));
1139
1171
  if (handle) {
1140
1172
  handle.abort().catch((abortErr) => {
1141
1173
  deps.logger?.debug({ runId, err: abortErr }, "Sub-agent SDK abort best-effort failed");
@@ -19,3 +19,5 @@ export { setupNotifications, type NotificationContext } from "./setup-notificati
19
19
  export { setupBackgroundTasks, type BackgroundTasksContext } from "./setup-background-tasks.js";
20
20
  export { setupBackgroundCompletionRunner } from "./setup-background-completion-runner.js";
21
21
  export type { BackgroundCompletionRunnerContext, SetupBackgroundCompletionRunnerDeps, } from "./setup-background-completion-runner.js";
22
+ export { setupOutputRetention, validateOutputRetentionConfig, } from "./setup-output-retention.js";
23
+ export type { SetupOutputRetentionDeps, SetupOutputRetentionHandle, } from "./setup-output-retention.js";
@@ -17,3 +17,4 @@ export { setupDeliveryQueue, setupDeliveryMirror } from "./setup-delivery.js";
17
17
  export { setupNotifications } from "./setup-notifications.js";
18
18
  export { setupBackgroundTasks } from "./setup-background-tasks.js";
19
19
  export { setupBackgroundCompletionRunner } from "./setup-background-completion-runner.js";
20
+ export { setupOutputRetention, validateOutputRetentionConfig, } from "./setup-output-retention.js";
@@ -3,14 +3,23 @@
3
3
  * dependencies (circuit breaker, budget guard, cost tracker, step counter),
4
4
  * and PiExecutor creation.
5
5
  * All agents use PiExecutor (pi-coding-agent AgentSession wrapper).
6
+ *
7
+ * The live ToolCapabilityPort adapter is constructed inside setupSingleAgent
8
+ * (this function), NOT at a higher composition site (daemon.ts). Rationale:
9
+ * skillRegistry is per-agent and the skill-allow/deny precedence chain is
10
+ * per-agent; a daemon-global adapter cannot satisfy this without breaking
11
+ * the port interface (would require adding agentId to every method). The
12
+ * per-agent port is exposed via AgentsResult.toolCapabilityPorts and threaded
13
+ * into setupTools via the getCapabilityPortForAgent closure on ToolsDeps.
14
+ *
6
15
  * @module
7
16
  */
8
- import { type AppContainer, type InjectionRateLimiter, type OAuthCredentialStorePort, type SecretsCrypto, type PerAgentConfig } from "@comis/core";
17
+ import { type AppContainer, type InjectionRateLimiter, type OAuthCredentialStorePort, type SecretsCrypto, type PerAgentConfig, type ToolCapabilityPort } from "@comis/core";
9
18
  import type { ComisLogger } from "@comis/infra";
10
19
  import type Database from "better-sqlite3";
11
20
  import type { SqliteMemoryAdapter, createSessionStore } from "@comis/memory";
12
21
  import { createBudgetGuard, createCostTracker, createStepCounter, createSessionLifecycle, createComisSessionManager, type AgentExecutor, type ActiveRunRegistry, type ProviderHealthMonitor, type LastKnownModelTracker } from "@comis/agent";
13
- import { type SkillRegistry, type SkillWatcherHandle } from "@comis/skills";
22
+ import { type SkillRegistry, type SkillWatcherHandle, type McpClientManager } from "@comis/skills";
14
23
  type PiSessionAdapter = ReturnType<typeof createComisSessionManager>;
15
24
  /** Shared dependencies computed once before the agent loop and passed to each
16
25
  * setupSingleAgent() call. Exposed on AgentsResult so daemon.ts can capture
@@ -86,6 +95,12 @@ export interface SingleAgentDeps {
86
95
  * shares the secretsDb connection).
87
96
  */
88
97
  oauthCredentialStore: OAuthCredentialStorePort;
98
+ /**
99
+ * Daemon-global MCP client manager. Live-runtime view consumed by the
100
+ * per-agent ToolCapabilityPort adapter constructed inside setupSingleAgent.
101
+ * Threaded from daemon.ts; setupMcp runs before setupAgents.
102
+ */
103
+ mcpClientManager: McpClientManager;
89
104
  }
90
105
  /** Per-agent outputs from setupSingleAgent(), matching the Maps in AgentsResult. */
91
106
  export interface SingleAgentResult {
@@ -97,6 +112,12 @@ export interface SingleAgentResult {
97
112
  piSessionAdapter: PiSessionAdapter;
98
113
  skillWatcherHandle?: SkillWatcherHandle;
99
114
  skillRegistry: SkillRegistry;
115
+ /**
116
+ * Per-agent live ToolCapabilityPort. Constructed via
117
+ * createToolCapabilityAdapter using this agent's skillRegistry and the
118
+ * daemon-global mcpClientManager.
119
+ */
120
+ toolCapabilityPort: ToolCapabilityPort;
100
121
  }
101
122
  /** All services produced by the per-agent executor setup phase. */
102
123
  export interface AgentsResult {
@@ -124,6 +145,13 @@ export interface AgentsResult {
124
145
  skillWatcherHandles: Map<string, SkillWatcherHandle>;
125
146
  /** Per-agent skill registries for skills.list RPC method. */
126
147
  skillRegistries: Map<string, SkillRegistry>;
148
+ /**
149
+ * Per-agent ToolCapabilityPort instances. Consumed by setupTools via the
150
+ * getCapabilityPortForAgent closure on ToolsDeps; mutated by hot-add /
151
+ * hot-remove in daemon.ts to keep the parallel map consistent with
152
+ * skillRegistries.
153
+ */
154
+ toolCapabilityPorts: Map<string, ToolCapabilityPort>;
127
155
  /** Periodic lock cleanup timer (cleared on shutdown). */
128
156
  lockCleanupTimer: ReturnType<typeof setInterval>;
129
157
  /** Shared single-agent dependencies for hot-add closure capture. */
@@ -207,6 +235,12 @@ export declare function setupAgents(deps: {
207
235
  * `appConfig.oauth.storage === "encrypted"` mode.
208
236
  */
209
237
  secretsDb?: Database.Database;
238
+ /**
239
+ * Daemon-global MCP client manager. setupSingleAgent constructs a
240
+ * per-agent ToolCapabilityPort adapter that closes over this manager.
241
+ * daemon.ts threads it in after running setupMcp before setupAgents.
242
+ */
243
+ mcpClientManager: McpClientManager;
210
244
  }): Promise<AgentsResult>;
211
245
  /**
212
246
  * Resolve "default" model/provider placeholders to concrete values from the
@@ -4,9 +4,19 @@
4
4
  * dependencies (circuit breaker, budget guard, cost tracker, step counter),
5
5
  * and PiExecutor creation.
6
6
  * All agents use PiExecutor (pi-coding-agent AgentSession wrapper).
7
+ *
8
+ * The live ToolCapabilityPort adapter is constructed inside setupSingleAgent
9
+ * (this function), NOT at a higher composition site (daemon.ts). Rationale:
10
+ * skillRegistry is per-agent and the skill-allow/deny precedence chain is
11
+ * per-agent; a daemon-global adapter cannot satisfy this without breaking
12
+ * the port interface (would require adding agentId to every method). The
13
+ * per-agent port is exposed via AgentsResult.toolCapabilityPorts and threaded
14
+ * into setupTools via the getCapabilityPortForAgent closure on ToolsDeps.
15
+ *
7
16
  * @module
8
17
  */
9
18
  import { safePath, SkillsConfigSchema, createScopedSecretManager, createOutputGuard, generateCanaryToken, createInputSecurityGuard, validateInput, PerAgentConfigSchema } from "@comis/core";
19
+ import { createToolCapabilityAdapter } from "./tool-capability-adapter.js";
10
20
  import { suppressError } from "@comis/shared";
11
21
  import { createHmac } from "node:crypto";
12
22
  import { homedir } from "node:os";
@@ -154,18 +164,22 @@ export async function setupSingleAgent(agentId, rawAgentConfig, deps) {
154
164
  // Closure-stability: the closure dereferences
155
165
  // container.config.agents[agentId]?.oauthProfiles on every call.
156
166
  // This is the only correct shape because:
157
- // 1. Line ~222 above writes effectiveConfig (a NEW object built
158
- // from { ...agentConfig, model, provider }) into
159
- // container.config.agents[agentId]. The local `agentConfig`
160
- // parameter diverges from the daemon's map immediately at
161
- // startup capturing it would observe the wrong value.
167
+ // 1. The `container.config.agents[agentId] = effectiveConfig`
168
+ // writeback above (search for that assignment in this file)
169
+ // stores a NEW object built from
170
+ // { ...agentConfig, model, provider } into the daemon's map.
171
+ // The local `agentConfig` parameter diverges from the map
172
+ // immediately at startup — capturing it would observe the
173
+ // wrong value.
162
174
  // 2. agents.update at agent-handlers.ts:341 executes
163
175
  // `deps.agents[agentId] = parsedConfig`, REPLACING the
164
176
  // reference at that key with a new validated object. Capturing
165
177
  // the local agentConfig parameter would miss this hot-update.
166
- // 3. daemon.ts:594, 634 confirm `deps.agents` and
167
- // `container.config.agents` are THE SAME map object — the
168
- // daemon's single per-process Container.config instance.
178
+ // 3. daemon.ts confirms `deps.agents` and `container.config.agents`
179
+ // are THE SAME map object — search for
180
+ // `agents: container.config.agents` in the RpcDispatchDeps
181
+ // construction. The daemon holds a single per-process
182
+ // Container.config instance.
169
183
  // The map identity is stable; only the value at the agent key
170
184
  // changes. The closure-evaluated dereference observes (1) at
171
185
  // startup AND (2) on every agents.update without an event-bus
@@ -222,6 +236,16 @@ export async function setupSingleAgent(agentId, rawAgentConfig, deps) {
222
236
  };
223
237
  const skillRegistry = createSkillRegistry(resolvedSkillsConfig, container.eventBus, { agentId, tenantId: container.config.tenantId, userId: "system" }, perAgentLogger, eligibilityContext);
224
238
  skillRegistry.init();
239
+ // Per-agent ToolCapabilityPort adapter. Construction sits here so the
240
+ // adapter can close over this agent's skillRegistry; the adapter is
241
+ // reused by pi-executor (capability-index renderer) AND by exec/process
242
+ // tools (install-detour parser via setupTools.getCapabilityPortForAgent).
243
+ const toolCapabilityPort = createToolCapabilityAdapter({
244
+ toolingConfig: container.config.tooling,
245
+ skillRegistry,
246
+ mcpClientManager: deps.mcpClientManager,
247
+ logger: perAgentLogger,
248
+ });
225
249
  // Opt-in file watching for automatic skill reload
226
250
  let skillWatcherHandle;
227
251
  if (skillsConfig.watchEnabled) {
@@ -349,6 +373,7 @@ export async function setupSingleAgent(agentId, rawAgentConfig, deps) {
349
373
  : undefined,
350
374
  embeddingEnqueue: deps.embeddingQueue?.enqueue.bind(deps.embeddingQueue),
351
375
  embeddingPort: deps.embeddingPort, // Semantic search in discover_tools
376
+ toolCapabilityPort, // Live adapter constructed above from container.config.tooling + skillRegistry + mcpClientManager.
352
377
  // DAG context engine deps (optional -- only when context engine version is dag)
353
378
  contextStore: deps.contextStore,
354
379
  db: deps.db,
@@ -376,6 +401,7 @@ export async function setupSingleAgent(agentId, rawAgentConfig, deps) {
376
401
  piSessionAdapter: sessionAdapter,
377
402
  skillWatcherHandle,
378
403
  skillRegistry,
404
+ toolCapabilityPort, // Exposed for AgentsResult.toolCapabilityPorts map
379
405
  };
380
406
  }
381
407
  // ---------------------------------------------------------------------------
@@ -446,6 +472,9 @@ export async function setupAgents(deps) {
446
472
  const piSessionAdapters = new Map();
447
473
  const skillWatcherHandles = new Map();
448
474
  const skillRegistries = new Map();
475
+ // Per-agent live ToolCapabilityPort adapters, parallel to skillRegistries;
476
+ // mutated in lockstep by hot-add/hot-remove.
477
+ const toolCapabilityPorts = new Map();
449
478
  // Resolve sub-agent tool names from config for delegation awareness
450
479
  const subAgentToolGroups = container.config.security?.agentToAgent?.subAgentToolGroups ?? [];
451
480
  const subAgentToolNames = subAgentToolGroups.length === 0 || subAgentToolGroups.includes("full")
@@ -509,6 +538,10 @@ export async function setupAgents(deps) {
509
538
  // Daemon-level OAuth credential store handle (constructed once above,
510
539
  // reused per-agent + threaded into RPC deps).
511
540
  oauthCredentialStore,
541
+ // Daemon-global MCP client manager. Threaded through so each
542
+ // setupSingleAgent invocation can construct a per-agent
543
+ // ToolCapabilityPort adapter that closes over the live MCP state.
544
+ mcpClientManager: deps.mcpClientManager,
512
545
  };
513
546
  for (const [agentId, agentConfig] of Object.entries(agents)) {
514
547
  const result = await setupSingleAgent(agentId, agentConfig, singleAgentDeps);
@@ -521,6 +554,7 @@ export async function setupAgents(deps) {
521
554
  if (result.skillWatcherHandle)
522
555
  skillWatcherHandles.set(agentId, result.skillWatcherHandle);
523
556
  skillRegistries.set(agentId, result.skillRegistry);
557
+ toolCapabilityPorts.set(agentId, result.toolCapabilityPort);
524
558
  }
525
559
  const defaultAgentId = routingConfig.defaultAgentId;
526
560
  const defaultWorkspaceDir = workspaceDirs.get(defaultAgentId);
@@ -562,6 +596,9 @@ export async function setupAgents(deps) {
562
596
  piSessionAdapters,
563
597
  skillWatcherHandles,
564
598
  skillRegistries,
599
+ // daemon.ts threads this Map into setupTools via
600
+ // getCapabilityPortForAgent and mutates it on hot-add/hot-remove.
601
+ toolCapabilityPorts,
565
602
  lockCleanupTimer,
566
603
  singleAgentDeps,
567
604
  providerHealth,
@@ -1,18 +1,20 @@
1
1
  /**
2
- * Background completion runner wiring for daemon startup.
2
+ * Background completion dispatcher + runner wiring for daemon startup.
3
3
  *
4
- * Subscribes the runner to background_task:{completed,failed} events after
5
- * the notification service has been wired (so fallbackNotifyFn is live),
6
- * and returns a shutdown handle for the daemon's system:shutdown sequence
7
- * to await before tearing down the executor.
4
+ * Subscribes the dispatcher BEFORE the runner so its synchronous
5
+ * `transitionDispatchState` runs first; the runner's handler then reads
6
+ * the updated `task.dispatchState` and skips when state is "notified"
7
+ * (the dispatcher already fired fallback). Subscription order matters
8
+ * because the event bus fires handlers in registration order; the
9
+ * dispatcher MUST come first.
8
10
  *
9
11
  * Per AGENTS §2.4: composition root + factories. This wiring lives in
10
- * @comis/daemon (composition root); the actual factory body is in
12
+ * @comis/daemon (composition root); the actual factory bodies live in
11
13
  * @comis/agent.
12
14
  *
13
15
  * @module
14
16
  */
15
- import { type BackgroundCompletionRunner, type BackgroundTaskManager, type NotifyFn } from "@comis/agent";
17
+ import { type BackgroundCompletionRunner, type BackgroundTaskManager, type CompletionDispatcher, type NotifyFn } from "@comis/agent";
16
18
  import type { TypedEventBus } from "@comis/core";
17
19
  import type { ComisLogger } from "@comis/infra";
18
20
  import type { AgentExecutor } from "@comis/agent";
@@ -20,12 +22,25 @@ import type { RunnerSessionStore } from "@comis/agent";
20
22
  /** Result of setupBackgroundCompletionRunner -- exposed to the daemon for shutdown. */
21
23
  export interface BackgroundCompletionRunnerContext {
22
24
  runner: BackgroundCompletionRunner;
25
+ dispatcher: CompletionDispatcher;
23
26
  }
27
+ /**
28
+ * The taskManager arg widened to require `transitionDispatchState` so the
29
+ * dispatcher can persist state-machine transitions.
30
+ *
31
+ * The runner only consumes `getTask` (existing contract); the dispatcher
32
+ * consumes both `getTask` and `transitionDispatchState`. Daemon callers
33
+ * pass the full BackgroundTaskManager so structural subtyping covers both.
34
+ */
24
35
  export interface SetupBackgroundCompletionRunnerDeps {
25
36
  eventBus: TypedEventBus;
26
37
  getExecutor: (agentId: string) => AgentExecutor;
27
38
  sessionStore: RunnerSessionStore;
28
- taskManager: Pick<BackgroundTaskManager, "getTask">;
39
+ /**
40
+ * Must support `transitionDispatchState`; the dispatcher persists state
41
+ * machine transitions through it. The runner only needs `getTask`.
42
+ */
43
+ taskManager: Pick<BackgroundTaskManager, "getTask" | "transitionDispatchState">;
29
44
  /** bgNotifyFn closure used when the originating session is gone. */
30
45
  fallbackNotifyFn: NotifyFn;
31
46
  /** From config.backgroundTasks.maxBackgroundHops (default 3). NOT config.workflow.*. */
@@ -33,7 +48,11 @@ export interface SetupBackgroundCompletionRunnerDeps {
33
48
  logger: ComisLogger;
34
49
  }
35
50
  /**
36
- * Wire the background completion runner from daemon-level dependencies.
51
+ * Wire the dispatcher + completion runner from daemon-level dependencies.
37
52
  * Call this AFTER setupNotifications so fallbackNotifyFn is wired.
53
+ *
54
+ * Subscription order: dispatcher first (synchronous transition), runner
55
+ * second (reads updated state). Reverse-order shutdown so the runner
56
+ * stops accepting events before the dispatcher tears down.
38
57
  */
39
58
  export declare function setupBackgroundCompletionRunner(deps: SetupBackgroundCompletionRunnerDeps): BackgroundCompletionRunnerContext;
@@ -1,24 +1,41 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  /**
3
- * Background completion runner wiring for daemon startup.
3
+ * Background completion dispatcher + runner wiring for daemon startup.
4
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.
5
+ * Subscribes the dispatcher BEFORE the runner so its synchronous
6
+ * `transitionDispatchState` runs first; the runner's handler then reads
7
+ * the updated `task.dispatchState` and skips when state is "notified"
8
+ * (the dispatcher already fired fallback). Subscription order matters
9
+ * because the event bus fires handlers in registration order; the
10
+ * dispatcher MUST come first.
9
11
  *
10
12
  * Per AGENTS §2.4: composition root + factories. This wiring lives in
11
- * @comis/daemon (composition root); the actual factory body is in
13
+ * @comis/daemon (composition root); the actual factory bodies live in
12
14
  * @comis/agent.
13
15
  *
14
16
  * @module
15
17
  */
16
- import { createBackgroundCompletionRunner, } from "@comis/agent";
18
+ import { createBackgroundCompletionRunner, createCompletionDispatcher, } from "@comis/agent";
17
19
  /**
18
- * Wire the background completion runner from daemon-level dependencies.
20
+ * Wire the dispatcher + completion runner from daemon-level dependencies.
19
21
  * Call this AFTER setupNotifications so fallbackNotifyFn is wired.
22
+ *
23
+ * Subscription order: dispatcher first (synchronous transition), runner
24
+ * second (reads updated state). Reverse-order shutdown so the runner
25
+ * stops accepting events before the dispatcher tears down.
20
26
  */
21
27
  export function setupBackgroundCompletionRunner(deps) {
28
+ // Dispatcher subscribes FIRST so its synchronous transitionDispatchState
29
+ // runs before the runner's handler reads task.dispatchState within the
30
+ // same event-bus tick.
31
+ const dispatcher = createCompletionDispatcher({
32
+ eventBus: deps.eventBus,
33
+ sessionStore: deps.sessionStore,
34
+ taskManager: deps.taskManager,
35
+ fallbackNotifyFn: deps.fallbackNotifyFn,
36
+ maxBackgroundHops: deps.maxBackgroundHops,
37
+ logger: deps.logger,
38
+ });
22
39
  const runner = createBackgroundCompletionRunner({
23
40
  eventBus: deps.eventBus,
24
41
  getExecutor: deps.getExecutor,
@@ -28,5 +45,15 @@ export function setupBackgroundCompletionRunner(deps) {
28
45
  maxBackgroundHops: deps.maxBackgroundHops,
29
46
  logger: deps.logger,
30
47
  });
31
- return { runner };
48
+ return {
49
+ runner: {
50
+ // Reverse-order shutdown: runner first (stops accepting events), then
51
+ // dispatcher (ensures the at-most-once gate is the last to tear down).
52
+ async shutdown() {
53
+ await runner.shutdown();
54
+ await dispatcher.shutdown();
55
+ },
56
+ },
57
+ dispatcher,
58
+ };
32
59
  }
@@ -5,7 +5,7 @@
5
5
  * daemon runs, and starts periodic cleanup of stale completed/failed tasks.
6
6
  * @module
7
7
  */
8
- import { createBackgroundTaskManager } from "@comis/agent";
8
+ import { createBackgroundTaskManager, TASK_DIR_NAME } from "@comis/agent";
9
9
  import { safePath } from "@comis/core";
10
10
  /**
11
11
  * Wire the background task subsystem from daemon-level dependencies.
@@ -24,7 +24,7 @@ import { safePath } from "@comis/core";
24
24
  */
25
25
  export function setupBackgroundTasks(deps) {
26
26
  const manager = createBackgroundTaskManager({
27
- dataDir: safePath(deps.dataDir, "background-tasks"),
27
+ dataDir: safePath(deps.dataDir, TASK_DIR_NAME),
28
28
  eventBus: deps.eventBus,
29
29
  logger: deps.logger,
30
30
  });
@@ -10,7 +10,7 @@
10
10
  */
11
11
  import type { AppContainer, ChannelPort, ChannelPluginPort, NormalizedMessage, SessionKey, TranscriptionPort, TTSPort, ImageAnalysisPort, FileExtractionPort, FileExtractionConfig, MemoryPort, QueueConfig } from "@comis/core";
12
12
  import type { ComisLogger } from "@comis/infra";
13
- import type { AgentExecutor, createSessionLifecycle, ActiveRunRegistry } from "@comis/agent";
13
+ import type { AgentExecutor, createSessionLifecycle, ActiveRunRegistry, BackgroundSessionResolver } from "@comis/agent";
14
14
  import type { createSessionStore } from "@comis/memory";
15
15
  import { type CommandQueue } from "@comis/agent";
16
16
  import { type ChannelManager, type ApprovalNotifier } from "@comis/channels";
@@ -107,6 +107,13 @@ export interface ChannelsDeps {
107
107
  deliveryQueue?: import("@comis/core").DeliveryQueuePort;
108
108
  /** Optional active run registry for SDK-native steer+followup inbound routing */
109
109
  activeRunRegistry?: ActiveRunRegistry;
110
+ /**
111
+ * Optional composite-key resolver. Wired by the daemon as
112
+ * `createBackgroundSessionResolver({ activeRunRegistry })`; supersedes
113
+ * `activeRunRegistry.has/.get` for production lookups in the inbound
114
+ * pipeline.
115
+ */
116
+ sessionResolver?: BackgroundSessionResolver;
110
117
  /** RPC call dispatcher for /config chat commands (deferred dispatch -- safe to pass before wireDispatch). */
111
118
  rpcCall?: RpcCall;
112
119
  /** Optional callback for task extraction after successful agent execution (gated by config.scheduler.tasks.enabled). */
@@ -120,7 +127,7 @@ export interface ChannelsDeps {
120
127
  onMessageReceived?: (msg: NormalizedMessage, channelType: string) => void;
121
128
  /** Optional callback fired AFTER each successful inbound message processing. Used by post-processing state (e.g. notification session activity recording). */
122
129
  onMessageProcessed?: (msg: NormalizedMessage, channelType: string) => void;
123
- /** Optional approval gate for /approve and /deny chat commands in inbound pipeline (APPR-CHAT). */
130
+ /** Optional approval gate for /approve and /deny chat commands in inbound pipeline. */
124
131
  approvalGate?: import("@comis/core").ApprovalGate;
125
132
  /** Per-agent PI session adapters for session stats/destroy in slash commands (CMD-WIRE). */
126
133
  piSessionAdapters?: Map<string, {
@@ -11,9 +11,8 @@
11
11
  */
12
12
  import { readdir, readFile, stat } from "node:fs/promises";
13
13
  import { randomUUID } from "node:crypto";
14
- import { join } from "node:path";
15
14
  import { formatSessionKey, runWithContext, createDeliveryOrigin, safePath } from "@comis/core";
16
- import { createMessageRouter, createCommandQueue, createCommandHandler, parseSlashCommand, sanitizeAssistantResponse, resolveOperationModel, resolveProviderFamily, runMemoryReview } from "@comis/agent";
15
+ import { createMessageRouter, createCommandQueue, createCommandHandler, parseSlashCommand, sanitizeAssistantResponse, resolveOperationModel, resolveProviderFamily, runMemoryReview, classifyError } from "@comis/agent";
17
16
  import { createChannelManager, createRetryEngine, createApprovalNotifier, deliverToChannel, filterResponse, } from "@comis/channels";
18
17
  import { RetryConfigSchema } from "@comis/core";
19
18
  import { shouldAutoTts, resolveOutputFormat, parseOutboundMedia, applyToolPolicy, } from "@comis/skills";
@@ -372,9 +371,10 @@ export async function setupChannels(deps) {
372
371
  logger.warn({ channelType: deliveryTarget.channelType, jobName, hint: "Ensure the target channel adapter is started", errorKind: "config" }, "No adapter found for job suspension notification");
373
372
  return;
374
373
  }
374
+ const classified = classifyError(lastError);
375
375
  const message = [
376
376
  `Scheduled task "${jobName}" was suspended after ${consecutiveErrors} consecutive failures.`,
377
- `Last error: ${lastError.slice(0, 200)}`,
377
+ `Reason: ${classified.userMessage}`,
378
378
  `Re-enable with /cron enable ${jobId}`,
379
379
  ].join("\n");
380
380
  try {
@@ -464,6 +464,7 @@ export async function setupChannels(deps) {
464
464
  voiceResponsePipeline,
465
465
  parseOutboundMedia,
466
466
  activeRunRegistry: deps.activeRunRegistry,
467
+ sessionResolver: deps.sessionResolver,
467
468
  queueConfig: deps.queueConfig,
468
469
  getElevatedReplyConfig: (agentId) => {
469
470
  const agentConfig = agents[agentId];
@@ -517,8 +518,13 @@ export async function setupChannels(deps) {
517
518
  return;
518
519
  }
519
520
  let graphDir;
521
+ // Two-step safePath composition matches the canonical daemon-wiring
522
+ // pattern in setup-output-retention.ts. Both safePath calls throw on
523
+ // traversal; the surrounding catch handles either failure with a
524
+ // single operator-facing WARN.
520
525
  try {
521
- graphDir = safePath(join(dataDir, "graph-runs"), graphId);
526
+ const graphRunsDir = safePath(dataDir, "graph-runs");
527
+ graphDir = safePath(graphRunsDir, graphId);
522
528
  }
523
529
  catch {
524
530
  channelsLogger.warn({ graphId, hint: "Path traversal attempt in graphId", errorKind: "validation" }, "Graph report request rejected");
@@ -529,7 +535,7 @@ export async function setupChannels(deps) {
529
535
  await stat(graphDir);
530
536
  }
531
537
  catch {
532
- channelsLogger.warn({ graphId, graphDir, hint: "Graph run directory not found", errorKind: "not_found" }, "Graph report directory missing");
538
+ channelsLogger.warn({ graphId, graphDir, hint: "Graph run directory not found", errorKind: "validation" }, "Graph report directory missing");
533
539
  await adapter.sendMessage(channelId, "Report not available \u2014 graph run data not found.", threadId ? { extra: { threadId } } : undefined);
534
540
  return;
535
541
  }
@@ -543,7 +549,7 @@ export async function setupChannels(deps) {
543
549
  // Try to identify leaf nodes from metadata
544
550
  let leafOutputFile;
545
551
  try {
546
- const metadataRaw = await readFile(join(graphDir, "_run-metadata.json"), "utf8");
552
+ const metadataRaw = await readFile(safePath(graphDir, "_run-metadata.json"), "utf8");
547
553
  const metadata = JSON.parse(metadataRaw);
548
554
  const completedNodes = Object.entries(metadata.nodes)
549
555
  .filter(([, v]) => v.status === "completed")
@@ -553,7 +559,7 @@ export async function setupChannels(deps) {
553
559
  for (const f of outputFiles) {
554
560
  const nodeId = f.replace(/-output\.md$/, "");
555
561
  if (completedNodes.includes(nodeId)) {
556
- const fileStat = await stat(join(graphDir, f));
562
+ const fileStat = await stat(safePath(graphDir, f));
557
563
  if (fileStat.size > maxSize) {
558
564
  maxSize = fileStat.size;
559
565
  leafOutputFile = f;
@@ -568,7 +574,7 @@ export async function setupChannels(deps) {
568
574
  // Fallback: pick largest output file
569
575
  let maxSize = 0;
570
576
  for (const f of outputFiles) {
571
- const fileStat = await stat(join(graphDir, f));
577
+ const fileStat = await stat(safePath(graphDir, f));
572
578
  if (fileStat.size > maxSize) {
573
579
  maxSize = fileStat.size;
574
580
  leafOutputFile = f;
@@ -579,7 +585,7 @@ export async function setupChannels(deps) {
579
585
  await adapter.sendMessage(channelId, "Report not available \u2014 could not identify output file.", threadId ? { extra: { threadId } } : undefined);
580
586
  return;
581
587
  }
582
- const filePath = join(graphDir, leafOutputFile);
588
+ const filePath = safePath(graphDir, leafOutputFile);
583
589
  const nodeId = leafOutputFile.replace(/-output\.md$/, "");
584
590
  const caption = `Full report \u2014 ${nodeId} (graph ${graphId.slice(0, 8)})`;
585
591
  await adapter.sendAttachment(channelId, {
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Cross-session messaging setup: cross-session sender and sub-agent runner.
3
- * Extracted from daemon.ts step 6.6.9 to isolate cross-session service
4
- * creation from the main wiring sequence. The three callback closures
5
- * (executeInSession, sendToChannel, executeSubAgent) are built internally
6
- * from injected dependencies (assembleToolsForAgent, getExecutor, adaptersByType).
3
+ * Isolates cross-session service creation from the main wiring sequence.
4
+ * The three callback closures (executeInSession, sendToChannel,
5
+ * executeSubAgent) are built internally from injected dependencies
6
+ * (assembleToolsForAgent, getExecutor, adaptersByType).
7
7
  * @module
8
8
  */
9
9
  import type { SessionKey } from "@comis/core";
@@ -14,7 +14,7 @@ import { createCrossSessionSender } from "../cross-session-sender.js";
14
14
  import { createSubAgentRunner } from "../sub-agent-runner.js";
15
15
  import { createAnnouncementBatcher } from "../announcement-batcher.js";
16
16
  import { createAnnouncementDeadLetterQueue } from "../announcement-dead-letter.js";
17
- /** All services produced by the cross-session messaging setup phase. */
17
+ /** All services produced by the cross-session messaging setup. */
18
18
  export interface CrossSessionResult {
19
19
  /** Cross-session message sender for agent-to-agent communication. */
20
20
  crossSessionSender: ReturnType<typeof createCrossSessionSender>;
@@ -103,6 +103,21 @@ export declare function setupCrossSession(deps: {
103
103
  abort(): Promise<void>;
104
104
  } | undefined;
105
105
  };
106
+ /**
107
+ * Optional composite-key resolver. Threaded into sub-agent-runner so abort
108
+ * paths use `(agentId, channelType, channelId)` instead of a single-arg
109
+ * formatted-key lookup. Daemon builds it via
110
+ * `createBackgroundSessionResolver({activeRunRegistry})`.
111
+ */
112
+ sessionResolver?: {
113
+ resolveActiveSession(key: {
114
+ agentId: string;
115
+ channelType: string;
116
+ channelId: string;
117
+ }): {
118
+ abort(): Promise<void>;
119
+ } | undefined;
120
+ };
106
121
  /** Delivery queue for crash-safe persistence */
107
122
  deliveryQueue?: import("@comis/core").DeliveryQueuePort;
108
123
  }): CrossSessionResult;