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
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Install-detour parser: detect pip/npm/pnpm/yarn install commands that
3
+ * overlap connected MCP servers or visible prompt skills.
4
+ *
5
+ * Pure parser — no IO, no per-call state beyond inputs. Returns `null` on
6
+ * parser-bail (unbalanced quotes, no install form found, no overlap detected)
7
+ * to signal "let the command run unchanged."
8
+ *
9
+ * Consumed by:
10
+ * - packages/skills/src/builtin/exec-tool.ts (mode policy gate)
11
+ * - packages/skills/src/builtin/process-tool.ts (advise-mode retroactive hint)
12
+ * - packages/skills/src/builtin/process-registry.ts (InstallDetourDecision type for ProcessSession field)
13
+ *
14
+ * Privacy invariant: the parser produces only sanitized identifiers
15
+ * (`commandDigest`, `packages[].normalizedName`, `overlaps[].sourceName`).
16
+ * Raw command text NEVER leaves this module.
17
+ *
18
+ * @module
19
+ */
20
+ import type { ToolCapabilityPort } from "@comis/core";
21
+ /**
22
+ * One overlap entry — links a parsed install package back to the connected
23
+ * MCP server or visible prompt skill that should be used instead.
24
+ *
25
+ * The 4 `reason` literals are pinned to match the closed event shape at
26
+ * `packages/core/src/event-bus/events-agent.ts:120-159`. Do NOT add new
27
+ * literals here without updating the event type in core.
28
+ */
29
+ export interface DetourOverlap {
30
+ /** Normalized package name (PEP-503 for python, lowercase + scope-preserving for npm). */
31
+ readonly packageName: string;
32
+ readonly sourceType: "mcp" | "skill";
33
+ /** Connected MCP server name OR visible prompt-skill name. Never a tool name. */
34
+ readonly sourceName: string;
35
+ /** Cluster (used by soft-stop error template; from getMcpServerHint/getSkillHint/getClusterConfig). */
36
+ readonly cluster?: string;
37
+ readonly reason: "direct-server-name" | "mcp-operator-alias" | "skill-comis-alias" | "skill-operator-alias";
38
+ }
39
+ /**
40
+ * Parser decision — returned non-null only when an install form was matched
41
+ * AND at least one overlap was detected. Returns `null` on every other path
42
+ * (parser-bail, no install form, no overlap), so callers branch via
43
+ * `if (decision !== null)` rather than checking `overlaps.length`.
44
+ */
45
+ export interface InstallDetourDecision {
46
+ readonly packageManager: "pip" | "npm" | "pnpm" | "yarn";
47
+ /** Sorted alphabetically — required for `commandDigest` stability. */
48
+ readonly packages: readonly string[];
49
+ readonly overlaps: readonly DetourOverlap[];
50
+ /** SHA-256 of `${packageManager}:${sortedPackages.join(",")}` truncated to 16 hex chars. */
51
+ readonly commandDigest: string;
52
+ }
53
+ /**
54
+ * Parse a shell command for install-detour overlap. Pure function — no IO,
55
+ * no module state, no memoization. Built fresh per call from the port's
56
+ * runtime state to avoid the skill-visibility race.
57
+ *
58
+ * Returns `null` when:
59
+ * - `splitTopLevelSegments` bails (unbalanced quotes anywhere)
60
+ * - No top-level segment matches the leading-token rule
61
+ * - The matched segment yields no parsed packages after token classification
62
+ * - `overlaps.length === 0` (no detected overlap)
63
+ *
64
+ * @param command - Raw shell command (caller passes verbatim — parser does not sanitize)
65
+ * @param port - ToolCapabilityPort (runtime view of connected servers + visible skills)
66
+ */
67
+ export declare function parseInstallDetour(command: string, port: ToolCapabilityPort): InstallDetourDecision | null;
@@ -0,0 +1,342 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Install-detour parser: detect pip/npm/pnpm/yarn install commands that
4
+ * overlap connected MCP servers or visible prompt skills.
5
+ *
6
+ * Pure parser — no IO, no per-call state beyond inputs. Returns `null` on
7
+ * parser-bail (unbalanced quotes, no install form found, no overlap detected)
8
+ * to signal "let the command run unchanged."
9
+ *
10
+ * Consumed by:
11
+ * - packages/skills/src/builtin/exec-tool.ts (mode policy gate)
12
+ * - packages/skills/src/builtin/process-tool.ts (advise-mode retroactive hint)
13
+ * - packages/skills/src/builtin/process-registry.ts (InstallDetourDecision type for ProcessSession field)
14
+ *
15
+ * Privacy invariant: the parser produces only sanitized identifiers
16
+ * (`commandDigest`, `packages[].normalizedName`, `overlaps[].sourceName`).
17
+ * Raw command text NEVER leaves this module.
18
+ *
19
+ * @module
20
+ */
21
+ import { createHash } from "node:crypto";
22
+ import { ShellQuoteTracker } from "./exec-security.js";
23
+ // --------------------------------------------------------------------------
24
+ // Public entry point
25
+ // --------------------------------------------------------------------------
26
+ /**
27
+ * Parse a shell command for install-detour overlap. Pure function — no IO,
28
+ * no module state, no memoization. Built fresh per call from the port's
29
+ * runtime state to avoid the skill-visibility race.
30
+ *
31
+ * Returns `null` when:
32
+ * - `splitTopLevelSegments` bails (unbalanced quotes anywhere)
33
+ * - No top-level segment matches the leading-token rule
34
+ * - The matched segment yields no parsed packages after token classification
35
+ * - `overlaps.length === 0` (no detected overlap)
36
+ *
37
+ * @param command - Raw shell command (caller passes verbatim — parser does not sanitize)
38
+ * @param port - ToolCapabilityPort (runtime view of connected servers + visible skills)
39
+ */
40
+ export function parseInstallDetour(command, port) {
41
+ const segments = splitTopLevelSegments(command);
42
+ if (segments === null)
43
+ return null; // unbalanced quotes
44
+ // Try each top-level segment until one matches an install form
45
+ for (const segment of segments) {
46
+ const parsed = parseInstallSegment(segment);
47
+ if (parsed === null)
48
+ continue;
49
+ const { packageManager, rawPackages } = parsed;
50
+ // Token classification: drop URL/VCS/path/file specs; strip versions; keep registry names
51
+ const ecosystem = packageManager === "pip" ? "python" : "node";
52
+ const cleanedNames = [];
53
+ for (const tok of rawPackages) {
54
+ const cleaned = classifyPackageToken(tok, ecosystem);
55
+ if (cleaned !== null)
56
+ cleanedNames.push(cleaned);
57
+ }
58
+ if (cleanedNames.length === 0)
59
+ continue; // no install-target tokens
60
+ // Normalize names per ecosystem
61
+ const normalize = ecosystem === "python" ? normalizePythonName : normalizeNpmName;
62
+ const packages = [...new Set(cleanedNames.map(normalize))].sort();
63
+ if (packages.length === 0)
64
+ continue;
65
+ // Build alias map FRESH per call — no memoization (avoids skill-visibility race)
66
+ const { pythonAliases, npmAliases } = buildPackageAliasMap(port);
67
+ const aliasMap = ecosystem === "python" ? pythonAliases : npmAliases;
68
+ const connectedServersNorm = new Set(port.getConnectedMcpServers().map(normalize));
69
+ // Detect overlaps for each package: direct-server first, then alias-map
70
+ const overlaps = [];
71
+ for (const pkgN of packages) {
72
+ if (connectedServersNorm.has(pkgN)) {
73
+ // Find the original (un-normalized) server name for the sourceName field
74
+ const serverName = port.getConnectedMcpServers().find((s) => normalize(s) === pkgN);
75
+ if (serverName) {
76
+ const hint = port.getMcpServerHint(serverName);
77
+ overlaps.push({
78
+ packageName: pkgN,
79
+ sourceType: "mcp",
80
+ sourceName: serverName,
81
+ cluster: hint?.cluster,
82
+ reason: "direct-server-name",
83
+ });
84
+ continue;
85
+ }
86
+ }
87
+ const aliasHit = aliasMap.get(pkgN);
88
+ if (aliasHit)
89
+ overlaps.push(aliasHit);
90
+ }
91
+ if (overlaps.length === 0)
92
+ return null; // no overlap → no event → run unchanged
93
+ // First-match-wins on segments: return on the first segment producing overlap
94
+ return {
95
+ packageManager,
96
+ packages,
97
+ overlaps,
98
+ commandDigest: buildCommandDigest(packageManager, packages),
99
+ };
100
+ }
101
+ return null; // no segment matched an install form
102
+ }
103
+ // --------------------------------------------------------------------------
104
+ // Private helpers (NOT exported — internal to install-detour module)
105
+ // --------------------------------------------------------------------------
106
+ /**
107
+ * Split a command on top-level `;` `&&` `||` `|` `&` (outside quotes). Reuses
108
+ * `ShellQuoteTracker` from `exec-security.ts:21-74`. Returns `null` on
109
+ * unbalanced quotes anywhere — the parser-bail signal.
110
+ *
111
+ * Deliberately separate from `exec-security.ts:splitCommandSegments`:
112
+ * - Splits on `&` AS WELL — POSIX background-and-continue is a command terminator
113
+ * (same operator class as `;`). See exec-security.ts:184 for the canonical reference.
114
+ * - Returns `null` on unbalanced quotes (vs returning collected-so-far).
115
+ * Two helpers, no shared abstraction (KISS-consistent).
116
+ */
117
+ function splitTopLevelSegments(command) {
118
+ const tracker = new ShellQuoteTracker();
119
+ const segments = [];
120
+ let current = "";
121
+ for (let i = 0; i < command.length; i++) {
122
+ const ch = command[i];
123
+ if (!tracker.escaped && tracker.state === "NORMAL") {
124
+ // Two-char operators first
125
+ if (i + 1 < command.length) {
126
+ const two = ch + command[i + 1];
127
+ if (two === "&&" || two === "||") {
128
+ segments.push(current.trim());
129
+ current = "";
130
+ i++; // skip second char
131
+ continue;
132
+ }
133
+ }
134
+ // Single-char operators. `&` is a POSIX command terminator (background-and-continue),
135
+ // same class as `;` — see exec-security.ts:184. The two-char `&&` lookahead above
136
+ // runs first, so `&&` is never reached here.
137
+ if (ch === ";" || ch === "|" || ch === "&") {
138
+ segments.push(current.trim());
139
+ current = "";
140
+ continue;
141
+ }
142
+ }
143
+ tracker.feed(ch);
144
+ current += ch;
145
+ }
146
+ // Bail on unbalanced quotes
147
+ if (tracker.state !== "NORMAL" || tracker.escaped) {
148
+ return null;
149
+ }
150
+ segments.push(current.trim());
151
+ return segments.filter((s) => s.length > 0);
152
+ }
153
+ /**
154
+ * Identify the install form (if any) of a single top-level segment by its
155
+ * leading token. Returns the matched package manager + the raw remaining
156
+ * tokens (after the install verb) when a form is found.
157
+ *
158
+ * Leading-token rule — ONLY these forms are recognized:
159
+ * pip install …
160
+ * pip3 install …
161
+ * python -m pip install …
162
+ * python3 -m pip install …
163
+ * npm install … | npm i … | npm add …
164
+ * pnpm install … | pnpm add …
165
+ * yarn add …
166
+ *
167
+ * Quoted strings, command substitution, heredocs, and `npx`/`pwsh -c`
168
+ * fall through to the `null` branch.
169
+ */
170
+ function parseInstallSegment(segment) {
171
+ // Whitespace-tokenize the segment. We deliberately do NOT re-tokenize
172
+ // with quote-awareness here — the leading-token rule only inspects the
173
+ // first whitespace-delimited token, and shell quoting at this layer is
174
+ // already consistent (parent splitTopLevelSegments left us a balanced
175
+ // segment). This is the design's "no recursive descent" position.
176
+ const tokens = segment.split(/\s+/).filter((t) => t.length > 0);
177
+ if (tokens.length === 0)
178
+ return null;
179
+ const lead = tokens[0];
180
+ if (lead === "pip" || lead === "pip3") {
181
+ if (tokens[1] !== "install")
182
+ return null;
183
+ return { packageManager: "pip", rawPackages: tokens.slice(2) };
184
+ }
185
+ if (lead === "python" || lead === "python3") {
186
+ // python -m pip install …
187
+ if (tokens[1] !== "-m" || tokens[2] !== "pip" || tokens[3] !== "install")
188
+ return null;
189
+ return { packageManager: "pip", rawPackages: tokens.slice(4) };
190
+ }
191
+ if (lead === "npm") {
192
+ const verb = tokens[1];
193
+ if (verb !== "install" && verb !== "i" && verb !== "add")
194
+ return null;
195
+ return { packageManager: "npm", rawPackages: tokens.slice(2) };
196
+ }
197
+ if (lead === "pnpm") {
198
+ const verb = tokens[1];
199
+ if (verb !== "install" && verb !== "add")
200
+ return null;
201
+ return { packageManager: "pnpm", rawPackages: tokens.slice(2) };
202
+ }
203
+ if (lead === "yarn") {
204
+ if (tokens[1] !== "add")
205
+ return null;
206
+ return { packageManager: "yarn", rawPackages: tokens.slice(2) };
207
+ }
208
+ return null;
209
+ }
210
+ /**
211
+ * Classify one raw token from the install args:
212
+ * - Returns `null` if it's a flag (starts with `-`).
213
+ * - Returns `null` if it's URL/VCS/local-path/file spec.
214
+ * - Returns the package name with version stripped for unscoped names.
215
+ * - Preserves `@scope/name` and strips `@version` only at the SECOND `@`.
216
+ */
217
+ function classifyPackageToken(token, ecosystem) {
218
+ if (token.startsWith("-"))
219
+ return null; // flag — skip
220
+ // URL / VCS / local-path / file spec rejection
221
+ if (token.includes("://") ||
222
+ token.startsWith("git+") ||
223
+ token.startsWith("file:") ||
224
+ token.startsWith("./") ||
225
+ token.startsWith("../") ||
226
+ token.endsWith(".tar.gz") ||
227
+ token.endsWith(".whl") ||
228
+ token.endsWith(".zip")) {
229
+ return null;
230
+ }
231
+ // Scoped npm preservation: @scope/name or @scope/name@1.2.3
232
+ if (ecosystem === "node" && token.startsWith("@")) {
233
+ const slashIdx = token.indexOf("/");
234
+ if (slashIdx <= 0)
235
+ return null; // malformed @scope without name
236
+ const afterSlash = token.slice(slashIdx + 1);
237
+ const versionAt = afterSlash.indexOf("@");
238
+ const namePart = versionAt >= 0
239
+ ? afterSlash.slice(0, versionAt)
240
+ : afterSlash;
241
+ if (namePart.length === 0)
242
+ return null;
243
+ return token.slice(0, slashIdx + 1) + namePart; // "@scope/name"
244
+ }
245
+ // Unscoped: strip version specifiers from the FIRST occurrence of
246
+ // any of `==`, `>=`, `<=`, `<`, `>`, `!=`, `~=`, or `@`.
247
+ const versionRegex = /(==|>=|<=|!=|~=|<|>|@)/;
248
+ const match = token.search(versionRegex);
249
+ const namePart = match >= 0 ? token.slice(0, match) : token;
250
+ if (namePart.length === 0)
251
+ return null;
252
+ return namePart;
253
+ }
254
+ /** PEP 503 strict normalization: lowercase + collapse `-`/`_`/`.` runs to single `-`. */
255
+ function normalizePythonName(name) {
256
+ return name.toLowerCase().replace(/[-_.]+/g, "-");
257
+ }
258
+ /** npm: lowercase only; `@scope/name` preserved exactly. */
259
+ function normalizeNpmName(name) {
260
+ return name.toLowerCase();
261
+ }
262
+ /**
263
+ * SHA-256 of `${pm}:${sortedPackages.join(",")}` truncated to 16 hex chars.
264
+ * Stable, order-insensitive, distinct for distinct PMs/packages.
265
+ * Mirrors `file-state-tracker.ts:160-163` crypto pattern + the
266
+ * `SYSTEM_PROMPT_HASH_LENGTH = 16` truncation convention from
267
+ * `packages/agent/src/context-engine/constants.ts:163`.
268
+ */
269
+ function buildCommandDigest(pm, packages) {
270
+ const sorted = [...packages].sort();
271
+ return createHash("sha256")
272
+ .update(`${pm}:${sorted.join(",")}`)
273
+ .digest("hex")
274
+ .slice(0, 16);
275
+ }
276
+ /**
277
+ * Build per-call alias maps from the port's runtime view. NEVER memoize
278
+ * across calls — avoids the skill-visibility race.
279
+ *
280
+ * Two SEPARATE maps keyed by ecosystem to avoid cross-ecosystem aliasing
281
+ * collisions when an operator declares `replacesPackages: ["foo_bar"]`
282
+ * (Python normalizes to `foo-bar`; npm preserves `foo_bar`). The parser
283
+ * picks the appropriate map based on detected `packageManager`.
284
+ */
285
+ function buildPackageAliasMap(port) {
286
+ const pythonAliases = new Map();
287
+ const npmAliases = new Map();
288
+ // 1. MCP operator hints (tooling.mcp.capabilityHints[*].replacesPackages)
289
+ for (const server of port.getConnectedMcpServers()) {
290
+ const hint = port.getMcpServerHint(server);
291
+ if (!hint)
292
+ continue;
293
+ for (const pkg of hint.replacesPackages) {
294
+ const overlap = {
295
+ packageName: pkg.toLowerCase(),
296
+ sourceType: "mcp",
297
+ sourceName: server,
298
+ cluster: hint.cluster,
299
+ reason: "mcp-operator-alias",
300
+ };
301
+ const pyKey = normalizePythonName(pkg);
302
+ const npmKey = normalizeNpmName(pkg);
303
+ if (!pythonAliases.has(pyKey)) {
304
+ pythonAliases.set(pyKey, { ...overlap, packageName: pyKey });
305
+ }
306
+ if (!npmAliases.has(npmKey)) {
307
+ npmAliases.set(npmKey, { ...overlap, packageName: npmKey });
308
+ }
309
+ }
310
+ }
311
+ // 2. Skill aliases (operator hints + comis.capability — both pre-merged
312
+ // by port.getPromptSkillCapabilities()).
313
+ // Visibility filter (allowed/denied/eligibility/disableModelInvocation)
314
+ // is already applied at the port level — no re-filter here.
315
+ for (const skill of port.getPromptSkillCapabilities()) {
316
+ // Discriminate operator-alias vs comis-alias: if port.getSkillHint
317
+ // returns truthy for this skill, the operator hint is the source.
318
+ const operatorHint = port.getSkillHint(skill.name, skill.skillKey);
319
+ const reason = operatorHint
320
+ ? "skill-operator-alias"
321
+ : "skill-comis-alias";
322
+ for (const pkg of skill.replacesPackages) {
323
+ const overlap = {
324
+ packageName: pkg.toLowerCase(),
325
+ sourceType: "skill",
326
+ sourceName: skill.name,
327
+ cluster: skill.cluster,
328
+ reason,
329
+ };
330
+ const pyKey = normalizePythonName(pkg);
331
+ const npmKey = normalizeNpmName(pkg);
332
+ // first-source-wins precedence: MCP entries already in the map are not overwritten
333
+ if (!pythonAliases.has(pyKey)) {
334
+ pythonAliases.set(pyKey, { ...overlap, packageName: pyKey });
335
+ }
336
+ if (!npmAliases.has(npmKey)) {
337
+ npmAliases.set(npmKey, { ...overlap, packageName: npmKey });
338
+ }
339
+ }
340
+ }
341
+ return { pythonAliases, npmAliases };
342
+ }
@@ -19,11 +19,11 @@ import { jsonResult, readEnumParam, throwToolError, createTrustGuard, } from "./
19
19
  * `AgentToolResult` (multi-block content + typed `details`), so the factory
20
20
  * passes it through verbatim instead of re-wrapping via `jsonResult`.
21
21
  *
22
- * Used by `agents_manage.create` (260428-sw2 Layer 1) to emit a 2-text-block
23
- * tool_result: a high-attention next-step contract first, the JSON-rendered
24
- * RPC fields second. The 7 sibling admin manage tools (cron/heartbeat/
25
- * sessions/tokens/etc.) keep returning plain objects from their overrides --
26
- * they hit the `jsonResult` branch unchanged. Additive, zero-impact change.
22
+ * Used by `agents_manage.create` to emit a 2-text-block tool_result: a
23
+ * high-attention next-step contract first, the JSON-rendered RPC fields
24
+ * second. The 7 sibling admin manage tools (cron/heartbeat/sessions/tokens/
25
+ * etc.) keep returning plain objects from their overrides -- they hit the
26
+ * `jsonResult` branch unchanged. Additive, zero-impact change.
27
27
  */
28
28
  function isAgentToolResult(value) {
29
29
  return (typeof value === "object" &&
@@ -63,8 +63,8 @@ export interface AgentInlineWritesError {
63
63
  * of the `agents_manage.create` tool_result. The freshest, uncached surface
64
64
  * the LLM reads on every turn -- pinned here to fix the silent-termination
65
65
  * bug where TOOL_GUIDE prescriptive text gets crowded out under high
66
- * parallel-tool-call load (production session 1a8b0d91 turn 13: 9 sub-agents
67
- * created in parallel, then a 0-text 0-thinking 0-tool turn).
66
+ * parallel-tool-call load (e.g. 9 sub-agents created in parallel followed
67
+ * by a 0-text 0-thinking 0-tool turn).
68
68
  *
69
69
  * Pure string composition. No I/O, no Result<T,E> needed (per AGENTS.md
70
70
  * §2.1: Result is for fallible paths only; this is infallible).
@@ -150,8 +150,8 @@ function mapWorkspaceProfile(config) {
150
150
  * of the `agents_manage.create` tool_result. The freshest, uncached surface
151
151
  * the LLM reads on every turn -- pinned here to fix the silent-termination
152
152
  * bug where TOOL_GUIDE prescriptive text gets crowded out under high
153
- * parallel-tool-call load (production session 1a8b0d91 turn 13: 9 sub-agents
154
- * created in parallel, then a 0-text 0-thinking 0-tool turn).
153
+ * parallel-tool-call load (e.g. 9 sub-agents created in parallel followed
154
+ * by a 0-text 0-thinking 0-tool turn).
155
155
  *
156
156
  * Pure string composition. No I/O, no Result<T,E> needed (per AGENTS.md
157
157
  * §2.1: Result is for fallible paths only; this is infallible).
@@ -186,5 +186,23 @@ export function createMessageTool(rpcCall) {
186
186
  }
187
187
  }
188
188
  },
189
+ // Capture visibleDelivery on attach.
190
+ // The augmenter writes to JSONL `details` only — never to `content` —
191
+ // so the OpenAI Responses converter strips it before re-injection.
192
+ augmentDetails: {
193
+ attach: (params, _result) => {
194
+ const channelType = typeof params.channel_type === "string" ? params.channel_type : "";
195
+ const channelId = typeof params.channel_id === "string" ? params.channel_id : "";
196
+ const caption = typeof params.caption === "string" ? params.caption : undefined;
197
+ const record = {
198
+ kind: "attachment",
199
+ channelType,
200
+ channelId,
201
+ ...(caption !== undefined && { caption }),
202
+ deliveredAt: Date.now(),
203
+ };
204
+ return { visibleDelivery: record };
205
+ },
206
+ },
189
207
  }, rpcCall);
190
208
  }
@@ -60,13 +60,30 @@ export interface MultiActionDispatchConfig<T extends TSchema> {
60
60
  /** Per-action handler. Called with validated action, raw params, and rpcCall.
61
61
  * Must return the result to be wrapped in jsonResult. */
62
62
  actionHandler: (action: string, params: Record<string, unknown>, rpcCall: RpcCall) => Promise<unknown>;
63
+ /**
64
+ * Optional per-action callback to AUGMENT the `details` field of the returned
65
+ * AgentToolResult. Called AFTER the actionHandler resolves; receives the raw
66
+ * params, the actionHandler's result, and returns an object whose entries are
67
+ * merged into `jsonResult(result).details`.
68
+ *
69
+ * Used by `message(action='attach')` to capture `visibleDelivery` for JSONL
70
+ * persistence. Caller-driven — the factory itself does not assume any
71
+ * specific augmentation shape.
72
+ *
73
+ * The augmenter MUST NOT touch `wrapped.content` — the OpenAI Responses
74
+ * converter strips `msg.content` only, so anything in `details` is
75
+ * JSONL-only and never re-enters the prompt context.
76
+ */
77
+ augmentDetails?: Partial<Record<string, (params: Record<string, unknown>, result: unknown) => Record<string, unknown>>>;
63
78
  }
64
79
  /**
65
80
  * Create a multi-action tool that validates the action parameter then
66
81
  * delegates to an action handler.
67
82
  *
68
83
  * Handles the common pattern: action validation via readEnumParam +
69
- * action handler dispatch + jsonResult + error handling.
84
+ * action handler dispatch + jsonResult + error handling. Optionally
85
+ * augments the returned `details` field via `config.augmentDetails[action]`
86
+ * (see `MultiActionDispatchConfig.augmentDetails`).
70
87
  *
71
88
  * @param config - Tool configuration
72
89
  * @param rpcCall - RPC call function
@@ -54,7 +54,9 @@ export function createRpcDispatchTool(config, rpcCall) {
54
54
  * delegates to an action handler.
55
55
  *
56
56
  * Handles the common pattern: action validation via readEnumParam +
57
- * action handler dispatch + jsonResult + error handling.
57
+ * action handler dispatch + jsonResult + error handling. Optionally
58
+ * augments the returned `details` field via `config.augmentDetails[action]`
59
+ * (see `MultiActionDispatchConfig.augmentDetails`).
58
60
  *
59
61
  * @param config - Tool configuration
60
62
  * @param rpcCall - RPC call function
@@ -71,7 +73,21 @@ export function createMultiActionDispatchTool(config, rpcCall) {
71
73
  const p = params;
72
74
  const action = readEnumParam(p, "action", config.validActions);
73
75
  const result = await config.actionHandler(action, p, rpcCall);
74
- return jsonResult(result);
76
+ const wrapped = jsonResult(result);
77
+ // Opt-in details augmentation. Caller registers a per-action
78
+ // augmenter; the factory merges its output into details.
79
+ // The augmenter MUST NOT touch `wrapped.content` — the OpenAI Responses
80
+ // converter strips msg.content only, so anything in details is
81
+ // JSONL-only and never re-enters the prompt context.
82
+ const augmenter = config.augmentDetails?.[action];
83
+ if (augmenter) {
84
+ const augmented = augmenter(p, result);
85
+ return {
86
+ ...wrapped,
87
+ details: { ...(wrapped.details ?? {}), ...augmented },
88
+ };
89
+ }
90
+ return wrapped;
75
91
  }
76
92
  catch (err) {
77
93
  if (err instanceof Error && err.message.startsWith("["))
@@ -72,9 +72,9 @@ export function createModelsManageTool(rpcCall) {
72
72
  const provider = readStringParam(p, "provider");
73
73
  return rpcCall("models.test", { provider, _trustLevel: ctx.trustLevel });
74
74
  },
75
- // Layer 1F (260430-vwt): live native-catalog provider list for
76
- // agent self-discovery. Pairs with the tool-guide pointer so the
77
- // agent can confirm which names auto-promote in providers.create.
75
+ // Live native-catalog provider list for agent self-discovery.
76
+ // Pairs with the tool-guide pointer so the agent can confirm which
77
+ // names auto-promote in providers.create.
78
78
  async list_providers(_p, rpcCall, ctx) {
79
79
  return rpcCall("models.list_providers", { _trustLevel: ctx.trustLevel });
80
80
  },
@@ -7,6 +7,7 @@
7
7
  * @module
8
8
  */
9
9
  import type { ChildProcess } from "node:child_process";
10
+ import type { InstallDetourDecision } from "./install-detour.js";
10
11
  export interface ProcessSession {
11
12
  readonly id: string;
12
13
  readonly command: string;
@@ -21,6 +22,19 @@ export interface ProcessSession {
21
22
  readonly sandboxed: boolean;
22
23
  readonly autoBackgrounded?: boolean;
23
24
  readonly description?: string;
25
+ /**
26
+ * Install-detour decision captured at spawn time. Populated at the three
27
+ * exec-tool spawn sites (auto-bg, explicit-bg, foreground escalating to
28
+ * background) when the spawn-time parser detected overlap AND the mode was
29
+ * `advise`. Soft-stop refuses pre-spawn — no session exists for those
30
+ * calls; this field is never populated for soft-stop refusals.
31
+ *
32
+ * `process.status` reads this back on retroactive hint augmentation rather
33
+ * than re-deriving from current connected-server state — the connected
34
+ * server set may have drifted since spawn, which would produce an
35
+ * inconsistent hint vs the spawn-time event.
36
+ */
37
+ readonly installDetourDecision?: InstallDetourDecision;
24
38
  }
25
39
  export interface ProcessRegistry {
26
40
  add(session: ProcessSession): void;
@@ -14,6 +14,7 @@
14
14
  import type { AgentTool } from "@mariozechner/pi-agent-core";
15
15
  import { Type } from "typebox";
16
16
  import type { ProcessRegistry } from "./process-registry.js";
17
+ import type { ToolCapabilityPort } from "@comis/core";
17
18
  declare const ProcessParams: Type.TObject<{
18
19
  action: Type.TUnion<[Type.TLiteral<"list">, Type.TLiteral<"kill">, Type.TLiteral<"status">, Type.TLiteral<"log">]>;
19
20
  sessionId: Type.TOptional<Type.TString>;
@@ -25,12 +26,31 @@ interface ToolLogger {
25
26
  debug(obj: Record<string, unknown>, msg: string): void;
26
27
  info(obj: Record<string, unknown>, msg: string): void;
27
28
  }
29
+ /**
30
+ * Dependencies for the process tool factory. Backward compatibility is NOT
31
+ * preserved (see CLAUDE.md user-memory `feedback_no_backward_compat`).
32
+ *
33
+ * `toolCapabilityPort` is REQUIRED — read inside the `case "status":` branch
34
+ * to decide whether to augment the result envelope with the retroactive
35
+ * install-detour hint (read the spawn-time `session.installDetourDecision`
36
+ * rather than re-deriving from current connected-server state, since the
37
+ * connected set may have drifted since spawn). Daemon wiring injects
38
+ * `createNoOpCapabilityPort()` until the real adapter is available.
39
+ */
40
+ export interface ProcessToolDeps {
41
+ readonly registry: ProcessRegistry;
42
+ readonly logger?: ToolLogger;
43
+ /** REQUIRED for the v1.1 capability layer — used by `process.status` augmentation. */
44
+ readonly toolCapabilityPort: ToolCapabilityPort;
45
+ }
28
46
  /**
29
47
  * Create a process management tool that delegates to a ProcessRegistry.
30
48
  *
31
- * @param registry - ProcessRegistry for session CRUD operations
32
- * @param logger - Optional structured logger for DEBUG-level operation logging
33
- * @returns AgentTool implementing the process management interface
49
+ * Uses a deps-object signature; backward compat with the prior positional
50
+ * `(registry, logger?)` shape is NOT preserved.
51
+ *
52
+ * @param deps - Dependencies bundle. See `ProcessToolDeps` for field semantics.
53
+ * @returns AgentTool implementing the process management interface.
34
54
  */
35
- export declare function createProcessTool(registry: ProcessRegistry, logger?: ToolLogger): AgentTool<typeof ProcessParams>;
55
+ export declare function createProcessTool(deps: ProcessToolDeps): AgentTool<typeof ProcessParams>;
36
56
  export {};