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.
- package/node_modules/@comis/agent/dist/background/auto-background-middleware.js +9 -0
- package/node_modules/@comis/agent/dist/background/background-task-manager.d.ts +22 -2
- package/node_modules/@comis/agent/dist/background/background-task-manager.js +48 -41
- package/node_modules/@comis/agent/dist/background/background-task-persistence.js +28 -5
- package/node_modules/@comis/agent/dist/background/background-task-types.d.ts +49 -0
- package/node_modules/@comis/agent/dist/background/completion-dispatcher.d.ts +130 -0
- package/node_modules/@comis/agent/dist/background/completion-dispatcher.js +215 -0
- package/node_modules/@comis/agent/dist/background/completion-runner.d.ts +10 -1
- package/node_modules/@comis/agent/dist/background/completion-runner.js +98 -15
- package/node_modules/@comis/agent/dist/background/index.d.ts +6 -1
- package/node_modules/@comis/agent/dist/background/index.js +2 -0
- package/node_modules/@comis/agent/dist/background/session-resolver.d.ts +85 -0
- package/node_modules/@comis/agent/dist/background/session-resolver.js +78 -0
- package/node_modules/@comis/agent/dist/bootstrap/sections/messaging-sections.js +1 -0
- package/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +3 -3
- package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.d.ts +30 -2
- package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +51 -2
- package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.d.ts +22 -0
- package/node_modules/@comis/agent/dist/bootstrap/system-prompt-assembler.js +2 -2
- package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.d.ts +1 -5
- package/node_modules/@comis/agent/dist/bridge/bridge-event-handlers.js +2 -14
- package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +26 -0
- package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +3 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +9 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +73 -2
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.d.ts +10 -10
- package/node_modules/@comis/agent/dist/context-engine/signature-surrogate-guard.js +14 -14
- package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.d.ts +11 -13
- package/node_modules/@comis/agent/dist/context-engine/thinking-block-cleaner.js +14 -15
- package/node_modules/@comis/agent/dist/executor/capability-index-context.d.ts +72 -0
- package/node_modules/@comis/agent/dist/executor/capability-index-context.js +329 -0
- package/node_modules/@comis/agent/dist/executor/drain-helper.d.ts +122 -0
- package/node_modules/@comis/agent/dist/executor/drain-helper.js +173 -0
- package/node_modules/@comis/agent/dist/executor/error-classifier.js +2 -2
- package/node_modules/@comis/agent/dist/executor/executor-post-execution.d.ts +48 -4
- package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +134 -31
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +7 -0
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +25 -4
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.d.ts +18 -1
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +19 -16
- package/node_modules/@comis/agent/dist/executor/jit-guide-injector.d.ts +11 -2
- package/node_modules/@comis/agent/dist/executor/jit-guide-injector.js +16 -2
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +8 -2
- package/node_modules/@comis/agent/dist/executor/pi-executor.js +25 -12
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.d.ts +9 -1
- package/node_modules/@comis/agent/dist/executor/prompt-assembly.js +15 -1
- package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +18 -27
- package/node_modules/@comis/agent/dist/executor/tool-deferral.js +29 -38
- package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +1 -1
- package/node_modules/@comis/agent/dist/model/model-scanner.js +1 -1
- package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.d.ts +11 -1
- package/node_modules/@comis/agent/dist/safety/tool-retry-breaker.js +19 -22
- package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +16 -2
- package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.d.ts +1 -1
- package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.js +5 -5
- package/node_modules/@comis/agent/dist/workspace/data-env.d.ts +38 -0
- package/node_modules/@comis/agent/dist/workspace/data-env.js +56 -0
- package/node_modules/@comis/agent/dist/workspace/index.d.ts +1 -0
- package/node_modules/@comis/agent/dist/workspace/index.js +1 -0
- package/node_modules/@comis/agent/dist/workspace/templates.js +5 -1
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/dist/index.d.ts +1 -1
- package/node_modules/@comis/channels/dist/index.js +1 -1
- package/node_modules/@comis/channels/dist/shared/channel-manager.d.ts +9 -3
- package/node_modules/@comis/channels/dist/shared/inbound-gate.d.ts +1 -1
- package/node_modules/@comis/channels/dist/shared/inbound-gate.js +22 -7
- package/node_modules/@comis/channels/dist/shared/inbound-pipeline.d.ts +10 -3
- package/node_modules/@comis/channels/dist/shared/inbound-route.d.ts +1 -1
- package/node_modules/@comis/channels/dist/shared/inbound-route.js +13 -2
- package/node_modules/@comis/channels/dist/shared/response-filter.d.ts +11 -24
- package/node_modules/@comis/channels/dist/shared/response-filter.js +25 -53
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/commands/providers.d.ts +1 -2
- package/node_modules/@comis/cli/dist/commands/providers.js +5 -6
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/config/field-metadata.js +2 -0
- package/node_modules/@comis/core/dist/config/immutable-keys.js +4 -1
- package/node_modules/@comis/core/dist/config/index.d.ts +4 -0
- package/node_modules/@comis/core/dist/config/index.js +2 -0
- package/node_modules/@comis/core/dist/config/schema-agent.d.ts +0 -792
- package/node_modules/@comis/core/dist/config/schema-approvals.d.ts +0 -14
- package/node_modules/@comis/core/dist/config/schema-auto-reply-engine.d.ts +0 -6
- package/node_modules/@comis/core/dist/config/schema-background-tasks.d.ts +0 -12
- package/node_modules/@comis/core/dist/config/schema-browser.d.ts +0 -18
- package/node_modules/@comis/core/dist/config/schema-channel.d.ts +0 -158
- package/node_modules/@comis/core/dist/config/schema-coalescer.d.ts +0 -5
- package/node_modules/@comis/core/dist/config/schema-daemon.d.ts +0 -32
- package/node_modules/@comis/core/dist/config/schema-delivery.d.ts +0 -18
- package/node_modules/@comis/core/dist/config/schema-documentation.d.ts +0 -12
- package/node_modules/@comis/core/dist/config/schema-embedding.d.ts +0 -20
- package/node_modules/@comis/core/dist/config/schema-envelope.d.ts +0 -15
- package/node_modules/@comis/core/dist/config/schema-gateway.d.ts +0 -37
- package/node_modules/@comis/core/dist/config/schema-gemini-cache.d.ts +0 -2
- package/node_modules/@comis/core/dist/config/schema-integrations.d.ts +0 -318
- package/node_modules/@comis/core/dist/config/schema-lifecycle-reactions.d.ts +0 -18
- package/node_modules/@comis/core/dist/config/schema-memory-review.d.ts +0 -7
- package/node_modules/@comis/core/dist/config/schema-memory.d.ts +0 -16
- package/node_modules/@comis/core/dist/config/schema-messages.d.ts +0 -8
- package/node_modules/@comis/core/dist/config/schema-models.d.ts +0 -15
- package/node_modules/@comis/core/dist/config/schema-notification.d.ts +0 -5
- package/node_modules/@comis/core/dist/config/schema-oauth.d.ts +0 -5
- package/node_modules/@comis/core/dist/config/schema-observability.d.ts +0 -38
- package/node_modules/@comis/core/dist/config/schema-output-retention.d.ts +34 -0
- package/node_modules/@comis/core/dist/config/schema-output-retention.js +48 -0
- package/node_modules/@comis/core/dist/config/schema-plugins.d.ts +0 -8
- package/node_modules/@comis/core/dist/config/schema-providers.d.ts +0 -64
- package/node_modules/@comis/core/dist/config/schema-queue.d.ts +0 -58
- package/node_modules/@comis/core/dist/config/schema-response-prefix.d.ts +0 -2
- package/node_modules/@comis/core/dist/config/schema-retry.d.ts +0 -6
- package/node_modules/@comis/core/dist/config/schema-scheduler.d.ts +0 -39
- package/node_modules/@comis/core/dist/config/schema-secrets.d.ts +0 -3
- package/node_modules/@comis/core/dist/config/schema-security.d.ts +0 -18
- package/node_modules/@comis/core/dist/config/schema-send-policy.d.ts +0 -13
- package/node_modules/@comis/core/dist/config/schema-sender-trust-display.d.ts +0 -5
- package/node_modules/@comis/core/dist/config/schema-serializer.js +2 -0
- package/node_modules/@comis/core/dist/config/schema-skills.d.ts +0 -61
- package/node_modules/@comis/core/dist/config/schema-streaming.d.ts +0 -38
- package/node_modules/@comis/core/dist/config/schema-telegram-file-guard.d.ts +0 -3
- package/node_modules/@comis/core/dist/config/schema-tooling.d.ts +87 -0
- package/node_modules/@comis/core/dist/config/schema-tooling.js +152 -0
- package/node_modules/@comis/core/dist/config/schema-verbosity.d.ts +0 -12
- package/node_modules/@comis/core/dist/config/schema-webhooks.d.ts +0 -40
- package/node_modules/@comis/core/dist/config/schema.d.ts +41 -38
- package/node_modules/@comis/core/dist/config/schema.js +6 -0
- package/node_modules/@comis/core/dist/context/context.d.ts +0 -4
- package/node_modules/@comis/core/dist/domain/approval-request.d.ts +0 -17
- package/node_modules/@comis/core/dist/domain/background-task-origin.d.ts +0 -10
- package/node_modules/@comis/core/dist/domain/delivery-origin.d.ts +0 -5
- package/node_modules/@comis/core/dist/domain/execution-graph.d.ts +0 -48
- package/node_modules/@comis/core/dist/domain/memory-entry.d.ts +0 -3
- package/node_modules/@comis/core/dist/domain/model-compat.d.ts +0 -4
- package/node_modules/@comis/core/dist/domain/normalized-message.d.ts +0 -15
- package/node_modules/@comis/core/dist/domain/provider-capabilities.d.ts +0 -6
- package/node_modules/@comis/core/dist/domain/rich-message.d.ts +0 -14
- package/node_modules/@comis/core/dist/domain/subagent-context-config.d.ts +0 -22
- package/node_modules/@comis/core/dist/domain/subagent-context-types.d.ts +0 -8
- package/node_modules/@comis/core/dist/event-bus/events-agent.d.ts +31 -0
- package/node_modules/@comis/core/dist/event-bus/events-infra.d.ts +5 -0
- package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
- package/node_modules/@comis/core/dist/exports/config.js +3 -1
- package/node_modules/@comis/core/dist/exports/hooks.d.ts +1 -1
- package/node_modules/@comis/core/dist/exports/ports.d.ts +2 -2
- package/node_modules/@comis/core/dist/exports/ports.js +1 -1
- package/node_modules/@comis/core/dist/ports/channel-plugin.d.ts +0 -13
- package/node_modules/@comis/core/dist/ports/index.d.ts +2 -0
- package/node_modules/@comis/core/dist/ports/index.js +4 -0
- package/node_modules/@comis/core/dist/ports/no-op-tool-capability.d.ts +30 -0
- package/node_modules/@comis/core/dist/ports/no-op-tool-capability.js +47 -0
- package/node_modules/@comis/core/dist/ports/tool-capability.d.ts +165 -0
- package/node_modules/@comis/core/dist/ports/tool-capability.js +15 -0
- package/node_modules/@comis/core/dist/security/audit.d.ts +0 -11
- package/node_modules/@comis/core/dist/tool-metadata.d.ts +21 -1
- package/node_modules/@comis/core/dist/tool-metadata.js +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/bundled-skills/skill-creator/scripts/validate-skill.py +1 -1
- package/node_modules/@comis/daemon/dist/daemon.js +89 -14
- package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.d.ts +1 -1
- package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +1 -1
- package/node_modules/@comis/daemon/dist/rpc/builtin-provider-guard.js +2 -2
- package/node_modules/@comis/daemon/dist/rpc/credential-resolver.js +1 -1
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +1 -1
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +2 -2
- package/node_modules/@comis/daemon/dist/sub-agent-runner.d.ts +18 -0
- package/node_modules/@comis/daemon/dist/sub-agent-runner.js +41 -9
- package/node_modules/@comis/daemon/dist/wiring/index.d.ts +2 -0
- package/node_modules/@comis/daemon/dist/wiring/index.js +1 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +36 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +45 -8
- package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.d.ts +28 -9
- package/node_modules/@comis/daemon/dist/wiring/setup-background-completion-runner.js +36 -9
- package/node_modules/@comis/daemon/dist/wiring/setup-background-tasks.js +2 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.d.ts +9 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-channels.js +15 -9
- package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.d.ts +20 -5
- package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +20 -15
- package/node_modules/@comis/daemon/dist/wiring/setup-delivery.js +14 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-gateway.d.ts +4 -6
- package/node_modules/@comis/daemon/dist/wiring/setup-gateway.js +3 -5
- package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.d.ts +20 -5
- package/node_modules/@comis/daemon/dist/wiring/setup-heartbeat.js +11 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.d.ts +89 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-output-retention.js +212 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-tools.d.ts +18 -4
- package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +29 -10
- package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.d.ts +75 -0
- package/node_modules/@comis/daemon/dist/wiring/tool-capability-adapter.js +253 -0
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/dist/webhook/webhook-endpoint.d.ts +0 -4
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/dist/cron/cron-types.d.ts +0 -42
- package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.d.ts +29 -8
- package/node_modules/@comis/scheduler/dist/heartbeat/agent-heartbeat-source.js +19 -7
- package/node_modules/@comis/scheduler/dist/system-events/system-event-types.d.ts +0 -3
- package/node_modules/@comis/scheduler/dist/tasks/task-types.d.ts +0 -17
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/dist/index.d.ts +3 -0
- package/node_modules/@comis/shared/dist/index.js +4 -0
- package/node_modules/@comis/shared/dist/mcp-tool-name.d.ts +78 -0
- package/node_modules/@comis/shared/dist/mcp-tool-name.js +92 -0
- package/node_modules/@comis/shared/dist/silent-tokens.d.ts +38 -0
- package/node_modules/@comis/shared/dist/silent-tokens.js +51 -0
- package/node_modules/@comis/shared/dist/visible-delivery.d.ts +28 -0
- package/node_modules/@comis/shared/dist/visible-delivery.js +16 -0
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.d.ts +2 -13
- package/node_modules/@comis/skills/dist/bridge/mcp-tool-bridge.js +3 -21
- package/node_modules/@comis/skills/dist/bridge/tool-metadata-enforcement.js +1 -1
- package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +4 -4
- package/node_modules/@comis/skills/dist/builtin/exec-tool.d.ts +55 -9
- package/node_modules/@comis/skills/dist/builtin/exec-tool.js +383 -19
- package/node_modules/@comis/skills/dist/builtin/install-detour.d.ts +67 -0
- package/node_modules/@comis/skills/dist/builtin/install-detour.js +342 -0
- package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.js +5 -5
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +2 -2
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +2 -2
- package/node_modules/@comis/skills/dist/builtin/platform/message-tool.js +18 -0
- package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.d.ts +18 -1
- package/node_modules/@comis/skills/dist/builtin/platform/messaging-factory.js +18 -2
- package/node_modules/@comis/skills/dist/builtin/platform/models-manage-tool.js +3 -3
- package/node_modules/@comis/skills/dist/builtin/process-registry.d.ts +14 -0
- package/node_modules/@comis/skills/dist/builtin/process-tool.d.ts +24 -4
- package/node_modules/@comis/skills/dist/builtin/process-tool.js +25 -7
- package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/sandbox/bwrap-provider.js +9 -0
- package/node_modules/@comis/skills/dist/index.d.ts +4 -1
- package/node_modules/@comis/skills/dist/index.js +3 -1
- package/node_modules/@comis/skills/dist/manifest/capability-parser.d.ts +44 -0
- package/node_modules/@comis/skills/dist/manifest/capability-parser.js +68 -0
- package/node_modules/@comis/skills/dist/manifest/schema.d.ts +44 -37
- package/node_modules/@comis/skills/dist/manifest/schema.js +35 -0
- package/node_modules/@comis/skills/dist/registry/discovery.d.ts +8 -0
- package/node_modules/@comis/skills/dist/registry/discovery.js +10 -3
- package/node_modules/@comis/skills/dist/registry/skill-registry.d.ts +45 -1
- package/node_modules/@comis/skills/dist/registry/skill-registry.js +70 -7
- package/node_modules/@comis/skills/package.json +1 -1
- package/node_modules/@comis/web/package.json +1 -1
- 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`
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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 (
|
|
67
|
-
*
|
|
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 (
|
|
154
|
-
*
|
|
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
|
-
|
|
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
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
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
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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(
|
|
55
|
+
export declare function createProcessTool(deps: ProcessToolDeps): AgentTool<typeof ProcessParams>;
|
|
36
56
|
export {};
|