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
|
@@ -26,6 +26,8 @@ import type { SecretManager } from "@comis/core";
|
|
|
26
26
|
import type { ExecSandboxConfig } from "./sandbox/types.js";
|
|
27
27
|
import type { ProcessRegistry } from "./process-registry.js";
|
|
28
28
|
import type { TypedEventBus } from "@comis/core";
|
|
29
|
+
import type { ToolCapabilityPort, ApprovalGate } from "@comis/core";
|
|
30
|
+
import { type InstallDetourDecision } from "./install-detour.js";
|
|
29
31
|
declare const ExecParams: Type.TObject<{
|
|
30
32
|
command: Type.TString;
|
|
31
33
|
cwd: Type.TOptional<Type.TString>;
|
|
@@ -37,6 +39,7 @@ declare const ExecParams: Type.TObject<{
|
|
|
37
39
|
description: Type.TOptional<Type.TString>;
|
|
38
40
|
pty: Type.TOptional<Type.TBoolean>;
|
|
39
41
|
secretRefs: Type.TOptional<Type.TArray<Type.TString>>;
|
|
42
|
+
allowInstallDetour: Type.TOptional<Type.TBoolean>;
|
|
40
43
|
}>;
|
|
41
44
|
/** Minimal pino-compatible logger for structured tool logging. */
|
|
42
45
|
interface ToolLogger {
|
|
@@ -68,17 +71,60 @@ export declare function buildSpawnCommand(command: string, cwd: string, sandboxC
|
|
|
68
71
|
args: string[];
|
|
69
72
|
cwd: string | undefined;
|
|
70
73
|
};
|
|
74
|
+
/**
|
|
75
|
+
* Build the structured install-detour hint augmentation for advise mode.
|
|
76
|
+
* Returns BOTH a string (for `details.installDetourHint`) AND a sibling
|
|
77
|
+
* `[hint]` content block (for the `result.content` array). Primary content,
|
|
78
|
+
* stdout, stderr, exitCode, and status are NEVER mutated by callers.
|
|
79
|
+
*
|
|
80
|
+
* Hint is informational; primary signal stays on the
|
|
81
|
+
* exec result envelope.
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildInstallDetourHint(decision: InstallDetourDecision): {
|
|
84
|
+
installDetourHint: string;
|
|
85
|
+
hintContentBlock: {
|
|
86
|
+
type: "text";
|
|
87
|
+
text: string;
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Dependencies for the exec tool factory. Backward compatibility is NOT
|
|
92
|
+
* preserved (see CLAUDE.md user-memory `feedback_no_backward_compat`).
|
|
93
|
+
*
|
|
94
|
+
* `toolCapabilityPort` is REQUIRED — wires the install-detour policy gate
|
|
95
|
+
* inside `execute(...)` consuming `port.getInstallDetourMode()`. Daemon
|
|
96
|
+
* wiring may inject `createNoOpCapabilityPort()` at the construction site
|
|
97
|
+
* (`packages/daemon/src/wiring/setup-tools.ts`); the no-op returns empty
|
|
98
|
+
* connected-server and skill arrays so the parser sees no overlaps.
|
|
99
|
+
*
|
|
100
|
+
* `approvalGate` is OPTIONAL — only required by the `soft-stop` mode
|
|
101
|
+
* override path. Missing gate → `soft-stop` denies override
|
|
102
|
+
* (fail-closed per AGENTS.md §2.1).
|
|
103
|
+
*/
|
|
104
|
+
export interface ExecToolDeps {
|
|
105
|
+
readonly workspacePath: string;
|
|
106
|
+
readonly registry: ProcessRegistry;
|
|
107
|
+
readonly secretManager: SecretManager;
|
|
108
|
+
readonly platformSecretNames: ReadonlySet<string>;
|
|
109
|
+
readonly logger?: ToolLogger;
|
|
110
|
+
readonly subprocessEnv?: Record<string, string>;
|
|
111
|
+
readonly sandboxConfig?: ExecSandboxConfig;
|
|
112
|
+
readonly eventBus?: TypedEventBus;
|
|
113
|
+
readonly getToolResultsDir?: () => string | undefined;
|
|
114
|
+
/** REQUIRED for v1.1 capability layer. */
|
|
115
|
+
readonly toolCapabilityPort: ToolCapabilityPort;
|
|
116
|
+
/** Optional. Required only for soft-stop override path. */
|
|
117
|
+
readonly approvalGate?: ApprovalGate;
|
|
118
|
+
}
|
|
71
119
|
/**
|
|
72
120
|
* Create an exec tool for shell command execution.
|
|
73
121
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @param
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @
|
|
80
|
-
* @param getToolResultsDir - Optional getter for session tool-results directory
|
|
81
|
-
* @returns AgentTool implementing the exec interface
|
|
122
|
+
* Backward compat NOT preserved (memory `feedback_no_backward_compat`).
|
|
123
|
+
*
|
|
124
|
+
* @param deps - Dependencies bundle. See `ExecToolDeps` for field semantics.
|
|
125
|
+
* `toolCapabilityPort` is REQUIRED; `approvalGate` is optional but
|
|
126
|
+
* required for the soft-stop override path.
|
|
127
|
+
* @returns AgentTool implementing the exec interface.
|
|
82
128
|
*/
|
|
83
|
-
export declare function createExecTool(
|
|
129
|
+
export declare function createExecTool(deps: ExecToolDeps): AgentTool<typeof ExecParams>;
|
|
84
130
|
export {};
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { Type } from "typebox";
|
|
25
25
|
import { spawn } from "node:child_process";
|
|
26
|
-
import { createWriteStream, mkdirSync, writeFileSync, copyFileSync, statSync } from "node:fs";
|
|
26
|
+
import { createWriteStream, mkdirSync, writeFileSync, copyFileSync, statSync, existsSync } from "node:fs";
|
|
27
27
|
import { tmpdir } from "node:os";
|
|
28
28
|
import { randomBytes } from "node:crypto";
|
|
29
29
|
import { safePath, PathTraversalError } from "@comis/core";
|
|
@@ -35,6 +35,7 @@ import { createOutputCleaner } from "./output-cleaner.js";
|
|
|
35
35
|
import { extractHeredoc, validateExecCommand, interpretExitCode } from "./exec-security.js";
|
|
36
36
|
import { matchExecRecoveryHint } from "./exec-diagnostics.js";
|
|
37
37
|
import { tryGetContext } from "@comis/core";
|
|
38
|
+
import { parseInstallDetour, } from "./install-detour.js";
|
|
38
39
|
// ---------------------------------------------------------------------------
|
|
39
40
|
// Constants
|
|
40
41
|
// ---------------------------------------------------------------------------
|
|
@@ -75,6 +76,12 @@ const ExecParams = Type.Object({
|
|
|
75
76
|
maxItems: 8,
|
|
76
77
|
description: "Secret/credential NAMES (not values) to inject as env vars into the subprocess. Use this to pass API tokens to CLI tools like wrangler/gh/gcloud/kubectl without tripping the env-var allowlist. Names are resolved server-side via SecretManager; values never flow through agent context. Call env_list first to discover available names. Platform-managed secrets (referenced by the daemon config, e.g. ANTHROPIC_API_KEY) are rejected. Raw-interpreter commands (python -c, node -e, bash -c, etc.) are rejected with secretRefs to prevent trivial echo-to-stdout leaks. Example: {command: 'npx wrangler pages deploy ./dist', secretRefs: ['CLOUDFLARE_API_TOKEN', 'CLOUDFLARE_ACCOUNT_ID']}",
|
|
77
78
|
})),
|
|
79
|
+
allowInstallDetour: Type.Optional(Type.Boolean({
|
|
80
|
+
description: "Request an operator-approved override when the user explicitly needs " +
|
|
81
|
+
"package installation despite an equivalent connected MCP server or " +
|
|
82
|
+
"available skill. Submits an approval request — does NOT self-authorize. " +
|
|
83
|
+
"Per-commandDigest scoped (one approval does not cover a different install).",
|
|
84
|
+
})),
|
|
78
85
|
});
|
|
79
86
|
// ---------------------------------------------------------------------------
|
|
80
87
|
// cwd resolution
|
|
@@ -201,6 +208,39 @@ export function buildSpawnCommand(command, cwd, sandboxConfig, workspacePath, te
|
|
|
201
208
|
// ---------------------------------------------------------------------------
|
|
202
209
|
/** Valid secret env var name (same rule as env.set). */
|
|
203
210
|
const SECRET_REF_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/;
|
|
211
|
+
/**
|
|
212
|
+
* Workspace-internal data env resolver.
|
|
213
|
+
*
|
|
214
|
+
* Returns env vars (PATH, MPLCONFIGDIR, XDG_CACHE_HOME, MPLBACKEND,
|
|
215
|
+
* PIP_DISABLE_PIP_VERSION_CHECK) derived from `workspaceDir` so python /
|
|
216
|
+
* matplotlib subprocesses do NOT inherit the daemon's host PATH or
|
|
217
|
+
* cache-dir defaults. (15s pip-install hot path; matplotlib Fontconfig
|
|
218
|
+
* error from a non-writable default cache dir).
|
|
219
|
+
*
|
|
220
|
+
* Defined inline to avoid a cross-package import from `@comis/agent`
|
|
221
|
+
* (`@comis/skills` does not depend on `@comis/agent` -- they are siblings
|
|
222
|
+
* wired together at the daemon composition root, mirroring the
|
|
223
|
+
* `bridge-event-handlers.ts` "Defined inline to avoid cross-package
|
|
224
|
+
* import" precedent). The shape mirrors
|
|
225
|
+
* `packages/agent/src/workspace/data-env.ts` verbatim; if the contract
|
|
226
|
+
* diverges, lift this into `@comis/shared`. The agent-side `data-env.ts`
|
|
227
|
+
* remains the canonical owner of the no-host-env contract.
|
|
228
|
+
*
|
|
229
|
+
* This helper does NOT read host env. The values are pure derivations
|
|
230
|
+
* from workspaceDir + safePath (no path.join).
|
|
231
|
+
*/
|
|
232
|
+
function resolveDataEnv(opts) {
|
|
233
|
+
const venvBin = safePath(opts.workspaceDir, "venv", "bin");
|
|
234
|
+
const cacheDir = safePath(opts.workspaceDir, ".cache");
|
|
235
|
+
const mplDir = safePath(cacheDir, "matplotlib");
|
|
236
|
+
return {
|
|
237
|
+
PATH: venvBin,
|
|
238
|
+
MPLCONFIGDIR: mplDir,
|
|
239
|
+
XDG_CACHE_HOME: cacheDir,
|
|
240
|
+
MPLBACKEND: "Agg",
|
|
241
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1",
|
|
242
|
+
};
|
|
243
|
+
}
|
|
204
244
|
/**
|
|
205
245
|
* Detect raw-interpreter command shapes. When `secretRefs` is present,
|
|
206
246
|
* these are refused because they make `echo $TOKEN` / `print(os.environ)`
|
|
@@ -271,21 +311,116 @@ function resolveSecretRefs(refs, secretManager, platformSecretNames) {
|
|
|
271
311
|
return { ok: true, env };
|
|
272
312
|
}
|
|
273
313
|
// ---------------------------------------------------------------------------
|
|
274
|
-
//
|
|
314
|
+
// Install-detour helpers
|
|
275
315
|
// ---------------------------------------------------------------------------
|
|
316
|
+
/**
|
|
317
|
+
* Map package manager to the structured ecosystem field used by the
|
|
318
|
+
* `tool:install_detour_detected` event payload (closed shape).
|
|
319
|
+
*/
|
|
320
|
+
function ecosystemFor(pm) {
|
|
321
|
+
return pm === "pip" ? "python" : "node";
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Build the structured install-detour hint augmentation for advise mode.
|
|
325
|
+
* Returns BOTH a string (for `details.installDetourHint`) AND a sibling
|
|
326
|
+
* `[hint]` content block (for the `result.content` array). Primary content,
|
|
327
|
+
* stdout, stderr, exitCode, and status are NEVER mutated by callers.
|
|
328
|
+
*
|
|
329
|
+
* Hint is informational; primary signal stays on the
|
|
330
|
+
* exec result envelope.
|
|
331
|
+
*/
|
|
332
|
+
export function buildInstallDetourHint(decision) {
|
|
333
|
+
const lines = decision.overlaps.map((o) => {
|
|
334
|
+
const cluster = o.cluster ? ` (cluster: ${o.cluster})` : "";
|
|
335
|
+
return o.sourceType === "mcp"
|
|
336
|
+
? `- ${o.packageName} -> connected MCP server "${o.sourceName}"${cluster}`
|
|
337
|
+
: `- ${o.packageName} -> available skill "${o.sourceName}"${cluster}`;
|
|
338
|
+
});
|
|
339
|
+
const text = `[hint] Installed packages overlap available capabilities:\n${lines.join("\n")}\n` +
|
|
340
|
+
`If you can use these capabilities directly, do so before relying on the install.`;
|
|
341
|
+
return {
|
|
342
|
+
installDetourHint: text,
|
|
343
|
+
hintContentBlock: { type: "text", text },
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Build the verbatim soft-stop error template. Returns `null` when
|
|
348
|
+
* no overlap source remains connected/visible at error-build time
|
|
349
|
+
* (overlaps disappeared mid-call; refusal no longer justified).
|
|
350
|
+
* Caller falls through to spawn in that case.
|
|
351
|
+
*
|
|
352
|
+
* Bullet count == filtered-overlap count. Cluster shown in parens when defined.
|
|
353
|
+
* MCP overlaps say `connected MCP server`; skill overlaps say `available skill`.
|
|
354
|
+
* NO provider-specific tool names; NO self-authorizing override wording.
|
|
355
|
+
*/
|
|
356
|
+
function buildSoftStopErrorTemplate(decision, port) {
|
|
357
|
+
// Re-check connection/visibility at error time, not parser time.
|
|
358
|
+
// The connected-server set may have drifted since parse. Filter to overlaps
|
|
359
|
+
// whose source is currently reachable.
|
|
360
|
+
const connectedServers = new Set(port.getConnectedMcpServers());
|
|
361
|
+
const visibleSkills = new Set(port.getPromptSkillCapabilities().map((s) => s.name));
|
|
362
|
+
const filtered = decision.overlaps.filter((o) => o.sourceType === "mcp" ? connectedServers.has(o.sourceName) : visibleSkills.has(o.sourceName));
|
|
363
|
+
if (filtered.length === 0)
|
|
364
|
+
return null; // overlaps disappeared — fall through
|
|
365
|
+
const bullets = filtered.map((o) => {
|
|
366
|
+
const cluster = o.cluster ? ` (cluster: ${o.cluster})` : "";
|
|
367
|
+
return o.sourceType === "mcp"
|
|
368
|
+
? `- ${o.packageName} -> connected MCP server "${o.sourceName}"${cluster}`
|
|
369
|
+
: `- ${o.packageName} -> available skill "${o.sourceName}"${cluster}`;
|
|
370
|
+
});
|
|
371
|
+
return [
|
|
372
|
+
"Refused: install overlaps with available capability source(s).",
|
|
373
|
+
"",
|
|
374
|
+
"Overlapping packages:",
|
|
375
|
+
...bullets,
|
|
376
|
+
"",
|
|
377
|
+
"To proceed, choose one:",
|
|
378
|
+
"1. Use the connected tool(s) or available skill(s) listed above for the overlapping work.",
|
|
379
|
+
"2. If you only need the non-overlapping packages, rerun exec with the overlapping ones removed.",
|
|
380
|
+
"3. If you genuinely need the install despite the overlap, ask the user/operator to approve the install-detour override, then rerun this exact command with `allowInstallDetour: true`.",
|
|
381
|
+
].join("\n");
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Build the closed `tool:install_detour_detected` event payload from a
|
|
385
|
+
* decision + the action/mode tags. Sanitized only:
|
|
386
|
+
* NEVER includes `command`, `rawCommand`, `stdout`, `stderr`, raw shell
|
|
387
|
+
* fragments, URLs, paths, or credentials.
|
|
388
|
+
*/
|
|
389
|
+
function buildInstallDetourEventPayload(decision, mode, action) {
|
|
390
|
+
const ctx = tryGetContext();
|
|
391
|
+
return {
|
|
392
|
+
agentId: ctx?.sessionKey ?? "unknown", // sessionKey-as-agentId convention
|
|
393
|
+
sessionKey: ctx?.sessionKey ?? "unknown",
|
|
394
|
+
traceId: ctx?.traceId,
|
|
395
|
+
packageManager: decision.packageManager,
|
|
396
|
+
commandDigest: decision.commandDigest,
|
|
397
|
+
packages: decision.packages.map((p) => ({
|
|
398
|
+
normalizedName: p,
|
|
399
|
+
ecosystem: ecosystemFor(decision.packageManager),
|
|
400
|
+
})),
|
|
401
|
+
overlaps: decision.overlaps.map((o) => ({
|
|
402
|
+
packageName: o.packageName,
|
|
403
|
+
sourceType: o.sourceType,
|
|
404
|
+
sourceName: o.sourceName,
|
|
405
|
+
reason: o.reason,
|
|
406
|
+
})),
|
|
407
|
+
mode,
|
|
408
|
+
action,
|
|
409
|
+
timestamp: Date.now(),
|
|
410
|
+
};
|
|
411
|
+
}
|
|
276
412
|
/**
|
|
277
413
|
* Create an exec tool for shell command execution.
|
|
278
414
|
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* @param
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* @
|
|
285
|
-
* @param getToolResultsDir - Optional getter for session tool-results directory
|
|
286
|
-
* @returns AgentTool implementing the exec interface
|
|
415
|
+
* Backward compat NOT preserved (memory `feedback_no_backward_compat`).
|
|
416
|
+
*
|
|
417
|
+
* @param deps - Dependencies bundle. See `ExecToolDeps` for field semantics.
|
|
418
|
+
* `toolCapabilityPort` is REQUIRED; `approvalGate` is optional but
|
|
419
|
+
* required for the soft-stop override path.
|
|
420
|
+
* @returns AgentTool implementing the exec interface.
|
|
287
421
|
*/
|
|
288
|
-
export function createExecTool(
|
|
422
|
+
export function createExecTool(deps) {
|
|
423
|
+
const { workspacePath, registry, secretManager, platformSecretNames, logger, subprocessEnv, sandboxConfig, eventBus, getToolResultsDir, } = deps;
|
|
289
424
|
// Comis extension: promptGuidelines is not part of AgentTool type, use object
|
|
290
425
|
// spread to avoid excess property checks in the return statement.
|
|
291
426
|
const guidelines = {
|
|
@@ -365,9 +500,132 @@ export function createExecTool(workspacePath, registry, secretManager, platformS
|
|
|
365
500
|
});
|
|
366
501
|
throwToolError("permission_denied", validationError.message);
|
|
367
502
|
}
|
|
503
|
+
// ===================================================================
|
|
504
|
+
// Install-detour mode policy gate
|
|
505
|
+
//
|
|
506
|
+
// Runs synchronously BEFORE any subprocess spawn. soft-stop refuses
|
|
507
|
+
// pre-spawn; observe and advise fall through to spawn after emitting
|
|
508
|
+
// events.
|
|
509
|
+
//
|
|
510
|
+
// The decision is computed ONCE here and propagated to:
|
|
511
|
+
// - Event emission (this block)
|
|
512
|
+
// - ProcessSession population at all 3 spawn sites
|
|
513
|
+
// - Foreground / auto-bg / explicit-bg envelope augmentation (advise)
|
|
514
|
+
// - process.status retroactive augmentation (read back from session)
|
|
515
|
+
// ===================================================================
|
|
516
|
+
const installDetourMode = deps.toolCapabilityPort.getInstallDetourMode();
|
|
517
|
+
const installDetourDecision = parseInstallDetour(command, deps.toolCapabilityPort);
|
|
518
|
+
const allowInstallDetourOverride = readBooleanParam(p, "allowInstallDetour", false) ?? false;
|
|
519
|
+
// Track the terminal action emitted by the mode cascade for the per-call INFO log.
|
|
520
|
+
// Initialized to "no-decision" because the parser may return null (no install form /
|
|
521
|
+
// no overlap); when null, the mode cascade and the log block both short-circuit (the
|
|
522
|
+
// log lives inside `if (decision !== null)`).
|
|
523
|
+
let terminalAction = "no-decision";
|
|
524
|
+
if (installDetourDecision !== null) {
|
|
525
|
+
// Decision is non-null only when overlaps.length > 0 (parser contract).
|
|
526
|
+
if (installDetourMode === "observe") {
|
|
527
|
+
for (const overlap of installDetourDecision.overlaps) {
|
|
528
|
+
// Scope each emit to the single overlap so downstream
|
|
529
|
+
// consumers receive distinct per-overlap payloads.
|
|
530
|
+
// The spread keeps packageManager / commandDigest /
|
|
531
|
+
// packages identical across the N events for a single call —
|
|
532
|
+
// only `overlaps[]` differs.
|
|
533
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload({ ...installDetourDecision, overlaps: [overlap] }, "observe", "observed"));
|
|
534
|
+
terminalAction = "observed";
|
|
535
|
+
}
|
|
536
|
+
// Fall through to spawn (observe runs unchanged)
|
|
537
|
+
}
|
|
538
|
+
else if (installDetourMode === "advise") {
|
|
539
|
+
for (const overlap of installDetourDecision.overlaps) {
|
|
540
|
+
// Scope each emit to the single overlap.
|
|
541
|
+
// See observe-loop comment above for the rationale.
|
|
542
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload({ ...installDetourDecision, overlaps: [overlap] }, "advise", "hinted"));
|
|
543
|
+
terminalAction = "hinted";
|
|
544
|
+
}
|
|
545
|
+
// Fall through to spawn; envelope augmentation applied at completion sites
|
|
546
|
+
}
|
|
547
|
+
else if (installDetourMode === "soft-stop") {
|
|
548
|
+
if (!allowInstallDetourOverride) {
|
|
549
|
+
// Refuse pre-spawn, single soft_stopped event
|
|
550
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload(installDetourDecision, "soft-stop", "soft_stopped"));
|
|
551
|
+
terminalAction = "soft_stopped";
|
|
552
|
+
const errorMessage = buildSoftStopErrorTemplate(installDetourDecision, deps.toolCapabilityPort);
|
|
553
|
+
if (errorMessage === null) {
|
|
554
|
+
// Overlaps disappeared mid-call. Fall through to spawn.
|
|
555
|
+
logger?.debug({
|
|
556
|
+
toolName: "exec",
|
|
557
|
+
commandDigest: installDetourDecision.commandDigest,
|
|
558
|
+
mode: "soft-stop",
|
|
559
|
+
outcome: "overlaps-disappeared",
|
|
560
|
+
}, "install-detour overlap sources disappeared mid-call; falling through to spawn");
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
throwToolError("permission_denied", errorMessage);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// allowInstallDetour: true → submit approval request
|
|
568
|
+
const ctx = tryGetContext();
|
|
569
|
+
if (!deps.approvalGate || !ctx) {
|
|
570
|
+
// Fail-closed: missing gate or missing context → emit override_denied + throw
|
|
571
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload(installDetourDecision, "soft-stop", "override_denied"));
|
|
572
|
+
terminalAction = "override_denied";
|
|
573
|
+
const errorMessage = buildSoftStopErrorTemplate(installDetourDecision, deps.toolCapabilityPort);
|
|
574
|
+
throwToolError("permission_denied", errorMessage ?? "Install-detour override denied: missing approval gate or request context.");
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
// Emit override_requested BEFORE awaiting (event-pair contract)
|
|
578
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload(installDetourDecision, "soft-stop", "override_requested"));
|
|
579
|
+
terminalAction = "override_requested";
|
|
580
|
+
const resolution = await deps.approvalGate.requestApproval({
|
|
581
|
+
toolName: "exec",
|
|
582
|
+
action: `exec.install_detour.override:${installDetourDecision.commandDigest}`,
|
|
583
|
+
params: {
|
|
584
|
+
// sanitized only
|
|
585
|
+
packageManager: installDetourDecision.packageManager,
|
|
586
|
+
packages: installDetourDecision.packages,
|
|
587
|
+
overlaps: installDetourDecision.overlaps,
|
|
588
|
+
mode: "soft-stop",
|
|
589
|
+
commandDigest: installDetourDecision.commandDigest,
|
|
590
|
+
},
|
|
591
|
+
agentId: ctx.userId ?? "unknown",
|
|
592
|
+
sessionKey: ctx.sessionKey,
|
|
593
|
+
trustLevel: (ctx.trustLevel ?? "admin"),
|
|
594
|
+
channelType: ctx.channelType,
|
|
595
|
+
});
|
|
596
|
+
if (!resolution.approved) {
|
|
597
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload(installDetourDecision, "soft-stop", "override_denied"));
|
|
598
|
+
terminalAction = "override_denied";
|
|
599
|
+
throwToolError("permission_denied", `Install-detour override denied: ${resolution.reason ?? "no reason given"}`);
|
|
600
|
+
}
|
|
601
|
+
// Approved — emit overridden, fall through to spawn UNAUGMENTED.
|
|
602
|
+
eventBus?.emit("tool:install_detour_detected", buildInstallDetourEventPayload(installDetourDecision, "soft-stop", "overridden"));
|
|
603
|
+
terminalAction = "overridden";
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
// Per-call INFO log summarizing the policy-gate evaluation
|
|
608
|
+
// (AGENTS.md §2.7 object-first convention).
|
|
609
|
+
// Fires for every command where a decision was produced AND a mode branch ran.
|
|
610
|
+
// The `terminalAction` field reflects the LAST emitted action for this call.
|
|
611
|
+
// For multi-event paths (observe/advise: 1-per-overlap; soft-stop with override:
|
|
612
|
+
// 2-event pair), `terminalAction` is the last action in the cascade for the call.
|
|
613
|
+
logger?.info({
|
|
614
|
+
toolName: "exec",
|
|
615
|
+
commandDigest: installDetourDecision.commandDigest,
|
|
616
|
+
packageManager: installDetourDecision.packageManager,
|
|
617
|
+
packageCount: installDetourDecision.packages.length,
|
|
618
|
+
overlapCount: installDetourDecision.overlaps.length,
|
|
619
|
+
mode: installDetourMode,
|
|
620
|
+
action: terminalAction,
|
|
621
|
+
}, "install-detour policy gate evaluated");
|
|
622
|
+
}
|
|
623
|
+
// ===================================================================
|
|
624
|
+
// End install-detour mode block
|
|
625
|
+
// ===================================================================
|
|
368
626
|
// Detect --break-system-packages for post-execution warning
|
|
369
627
|
const breakSystemWarning = command.includes("--break-system-packages")
|
|
370
|
-
? "\u26a0\ufe0f WARNING: --break-system-packages modifies the system Python. Use a virtualenv
|
|
628
|
+
? "\u26a0\ufe0f WARNING: --break-system-packages modifies the system Python. Use a virtualenv: the workspace's pre-warmed venv (venv/bin/pip install ...) or a per-project one (python3 -m venv projects/<name>/.venv).\n\n"
|
|
371
629
|
: "";
|
|
372
630
|
// Log command start (truncate command to 200 chars for security)
|
|
373
631
|
logger?.debug({ toolName: "exec", command: command.slice(0, 200), background, pty, ...(description && { description }) }, "Exec command start");
|
|
@@ -415,10 +673,48 @@ export function createExecTool(workspacePath, registry, secretManager, platformS
|
|
|
415
673
|
}, "Exec resolved secretRefs for subprocess");
|
|
416
674
|
}
|
|
417
675
|
}
|
|
418
|
-
// Build environment (use filtered subprocess env instead of raw process.env)
|
|
676
|
+
// Build environment (use filtered subprocess env instead of raw process.env).
|
|
419
677
|
const baseEnv = subprocessEnv ?? process.env;
|
|
678
|
+
// Workspace-internal env split into two halves:
|
|
679
|
+
//
|
|
680
|
+
// 1. Always-on: MPLCONFIGDIR / XDG_CACHE_HOME / MPLBACKEND /
|
|
681
|
+
// PIP_DISABLE_PIP_VERSION_CHECK. Eliminates the matplotlib
|
|
682
|
+
// `Fontconfig error` regardless of which venv layout (workspace-
|
|
683
|
+
// root `venv/`, per-project `.venv/`, none) the agent uses --
|
|
684
|
+
// the host `/root/.cache/matplotlib` may be read-only under
|
|
685
|
+
// `ProtectSystem=strict`; the workspace cache dir is always
|
|
686
|
+
// writable. Also prevents host XDG_CACHE_HOME leakage.
|
|
687
|
+
//
|
|
688
|
+
// 2. Conditional: PATH gets `${workspacePath}/venv/bin` prepended
|
|
689
|
+
// only when that directory actually exists (i.e. the Dockerfile
|
|
690
|
+
// pre-warm landed and wasn't shadowed by a stale volume). This
|
|
691
|
+
// closes the 15s pip-install hot path for prewarm users without
|
|
692
|
+
// shadowing per-project `projects/<name>/.venv/bin/python`
|
|
693
|
+
// invocations the agent makes directly.
|
|
694
|
+
//
|
|
695
|
+
// Order: spread baseEnv -> override with dataEnv -> userEnv (so
|
|
696
|
+
// the agent can still pin a specific value via `env: {...}`) ->
|
|
697
|
+
// server-resolved secrets (always last; agent cannot override).
|
|
698
|
+
const dataEnv = resolveDataEnv({ workspaceDir: workspacePath });
|
|
699
|
+
const venvBin = safePath(workspacePath, "venv", "bin");
|
|
700
|
+
if (existsSync(venvBin)) {
|
|
701
|
+
logger?.debug({ toolName: "exec", workspaceDir: workspacePath, hint: "Prepending workspace-prewarmed venv/bin to PATH" }, "Exec workspace venv detected");
|
|
702
|
+
// Prepend venv bin so `python` / `pip` resolve to the prewarm
|
|
703
|
+
// while system binaries (bash, sh, node, git, curl) remain
|
|
704
|
+
// reachable via baseEnv.PATH.
|
|
705
|
+
if (dataEnv.PATH && baseEnv.PATH) {
|
|
706
|
+
dataEnv.PATH = `${dataEnv.PATH}:${baseEnv.PATH}`;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
// No prewarm on this workspace -- drop the venv-only PATH so
|
|
711
|
+
// baseEnv.PATH wins. The matplotlib + cache dir env vars survive
|
|
712
|
+
// and still kill the Fontconfig warning universally.
|
|
713
|
+
delete dataEnv.PATH;
|
|
714
|
+
}
|
|
420
715
|
const env = {
|
|
421
716
|
...baseEnv,
|
|
717
|
+
...dataEnv,
|
|
422
718
|
...(userEnv ?? {}),
|
|
423
719
|
...(resolvedSecretEnv ?? {}),
|
|
424
720
|
};
|
|
@@ -436,9 +732,9 @@ export function createExecTool(workspacePath, registry, secretManager, platformS
|
|
|
436
732
|
logger?.debug({ toolName: "exec", stdinLength: input.length }, "Exec stdin write");
|
|
437
733
|
}
|
|
438
734
|
if (background) {
|
|
439
|
-
return executeBackground(command, cwd, finalEnv, input, registry, logger, sandboxConfig, workspacePath, tempDir, description, pty);
|
|
735
|
+
return executeBackground(command, cwd, finalEnv, input, registry, logger, sandboxConfig, workspacePath, tempDir, description, pty, installDetourDecision ?? undefined, installDetourMode);
|
|
440
736
|
}
|
|
441
|
-
const result = await executeForeground(command, cwd, finalEnv, timeoutMs, input, signal, onUpdate, logger, sandboxConfig, workspacePath, tempDir, registry, autoBackgroundMs, pty, description, toolCallId, getToolResultsDir);
|
|
737
|
+
const result = await executeForeground(command, cwd, finalEnv, timeoutMs, input, signal, onUpdate, logger, sandboxConfig, workspacePath, tempDir, registry, autoBackgroundMs, pty, description, toolCallId, getToolResultsDir, installDetourDecision ?? undefined, installDetourMode);
|
|
442
738
|
// Prepend --break-system-packages warning to stdout so the LLM sees it
|
|
443
739
|
if (breakSystemWarning && result.details) {
|
|
444
740
|
const details = result.details;
|
|
@@ -470,7 +766,7 @@ function escalateToBackground(ctx) {
|
|
|
470
766
|
id: generateSessionId(),
|
|
471
767
|
command: ctx.command,
|
|
472
768
|
pid: ctx.child.pid,
|
|
473
|
-
startedAt:
|
|
769
|
+
startedAt: ctx.startTimeMs,
|
|
474
770
|
status: "running",
|
|
475
771
|
exitCode: undefined,
|
|
476
772
|
stdout: ctx.stdoutBuf,
|
|
@@ -480,6 +776,14 @@ function escalateToBackground(ctx) {
|
|
|
480
776
|
sandboxed: !!ctx.sandboxConfig,
|
|
481
777
|
autoBackgrounded: true,
|
|
482
778
|
...(ctx.description && { description: ctx.description }),
|
|
779
|
+
// Spawn-time decision capture (advise+overlap only).
|
|
780
|
+
// observe-mode runs unchanged with no retroactive hint; soft-stop refused calls
|
|
781
|
+
// never reach a session-creation site (refused pre-spawn).
|
|
782
|
+
...(ctx.installDetourDecision !== null
|
|
783
|
+
&& ctx.installDetourDecision !== undefined
|
|
784
|
+
&& ctx.installDetourDecision.overlaps.length > 0
|
|
785
|
+
&& ctx.installDetourMode === "advise"
|
|
786
|
+
&& { installDetourDecision: ctx.installDetourDecision }),
|
|
483
787
|
};
|
|
484
788
|
// Re-wire stdout/stderr from rolling buffer to session append
|
|
485
789
|
const bgStdoutCleaner = createOutputCleaner();
|
|
@@ -507,6 +811,27 @@ function escalateToBackground(ctx) {
|
|
|
507
811
|
ctx.logger?.info({ toolName: "exec", sessionId: session.id, pid: ctx.child.pid, durationMs: Math.round(performance.now() - ctx.startTime), ...(ctx.description && { description: ctx.description }) }, "Exec auto-backgrounded after threshold");
|
|
508
812
|
if (ctx.spillStream)
|
|
509
813
|
ctx.spillStream.end();
|
|
814
|
+
// Auto-bg envelope augmentation in advise mode
|
|
815
|
+
if (ctx.installDetourMode === "advise" &&
|
|
816
|
+
ctx.installDetourDecision !== null &&
|
|
817
|
+
ctx.installDetourDecision !== undefined &&
|
|
818
|
+
ctx.installDetourDecision.overlaps.length > 0) {
|
|
819
|
+
const hint = buildInstallDetourHint(ctx.installDetourDecision);
|
|
820
|
+
const augmented = jsonResult({
|
|
821
|
+
status: "backgrounded",
|
|
822
|
+
sessionId: session.id,
|
|
823
|
+
pid: ctx.child.pid,
|
|
824
|
+
stdoutSoFar: truncateTail(ctx.stdoutBuf).content,
|
|
825
|
+
stderrSoFar: truncateTail(ctx.stderrBuf).content,
|
|
826
|
+
...(ctx.description && { description: ctx.description }),
|
|
827
|
+
installDetourHint: hint.installDetourHint,
|
|
828
|
+
});
|
|
829
|
+
ctx.resolve({
|
|
830
|
+
content: [...augmented.content, hint.hintContentBlock],
|
|
831
|
+
details: augmented.details,
|
|
832
|
+
});
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
510
835
|
ctx.resolve(jsonResult({
|
|
511
836
|
status: "backgrounded",
|
|
512
837
|
sessionId: session.id,
|
|
@@ -519,8 +844,9 @@ function escalateToBackground(ctx) {
|
|
|
519
844
|
// ---------------------------------------------------------------------------
|
|
520
845
|
// Foreground execution
|
|
521
846
|
// ---------------------------------------------------------------------------
|
|
522
|
-
function executeForeground(command, cwd, env, timeoutMs, input, signal, onUpdate, logger, sandboxConfig, workspacePath, tempDir, registry, autoBackgroundMs, pty, description, toolCallId, getToolResultsDir) {
|
|
847
|
+
function executeForeground(command, cwd, env, timeoutMs, input, signal, onUpdate, logger, sandboxConfig, workspacePath, tempDir, registry, autoBackgroundMs, pty, description, toolCallId, getToolResultsDir, installDetourDecision, installDetourMode) {
|
|
523
848
|
const startTime = performance.now();
|
|
849
|
+
const startTimeMs = Date.now();
|
|
524
850
|
return new Promise((resolve) => {
|
|
525
851
|
const { bin, args, cwd: spawnCwd } = buildSpawnCommand(command, cwd, sandboxConfig, workspacePath ?? cwd, tempDir ?? tmpdir(), pty);
|
|
526
852
|
const child = spawn(bin, args, {
|
|
@@ -654,11 +980,14 @@ function executeForeground(command, cwd, env, timeoutMs, input, signal, onUpdate
|
|
|
654
980
|
if (resolved)
|
|
655
981
|
return;
|
|
656
982
|
escalateToBackground({
|
|
657
|
-
command, child, startTime, stdoutBuf, stderrBuf,
|
|
983
|
+
command, child, startTime, startTimeMs, stdoutBuf, stderrBuf,
|
|
658
984
|
registry, sandboxConfig, logger, spillStream,
|
|
659
985
|
signal, onAbort, timeoutTimer, resolve,
|
|
660
986
|
setResolved: () => { resolved = true; },
|
|
661
987
|
description,
|
|
988
|
+
// Forward install-detour spawn-time decision + mode
|
|
989
|
+
...(installDetourDecision !== undefined && { installDetourDecision }),
|
|
990
|
+
...(installDetourMode !== undefined && { installDetourMode }),
|
|
662
991
|
});
|
|
663
992
|
}, effectiveAutoMs)
|
|
664
993
|
: null;
|
|
@@ -791,6 +1120,19 @@ function executeForeground(command, cwd, env, timeoutMs, input, signal, onUpdate
|
|
|
791
1120
|
}
|
|
792
1121
|
}
|
|
793
1122
|
}
|
|
1123
|
+
// Foreground completion envelope augmentation
|
|
1124
|
+
if (installDetourMode === "advise" &&
|
|
1125
|
+
installDetourDecision !== undefined &&
|
|
1126
|
+
installDetourDecision.overlaps.length > 0) {
|
|
1127
|
+
const hint = buildInstallDetourHint(installDetourDecision);
|
|
1128
|
+
result.installDetourHint = hint.installDetourHint;
|
|
1129
|
+
const augmented = jsonResult(result);
|
|
1130
|
+
resolve({
|
|
1131
|
+
content: [...augmented.content, hint.hintContentBlock],
|
|
1132
|
+
details: augmented.details,
|
|
1133
|
+
});
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
794
1136
|
resolve(jsonResult(result));
|
|
795
1137
|
});
|
|
796
1138
|
// Handle spawn errors
|
|
@@ -818,7 +1160,7 @@ function executeForeground(command, cwd, env, timeoutMs, input, signal, onUpdate
|
|
|
818
1160
|
// ---------------------------------------------------------------------------
|
|
819
1161
|
// Background execution
|
|
820
1162
|
// ---------------------------------------------------------------------------
|
|
821
|
-
function executeBackground(command, cwd, env, input, registry, logger, sandboxConfig, workspacePath, tempDir, description, pty) {
|
|
1163
|
+
function executeBackground(command, cwd, env, input, registry, logger, sandboxConfig, workspacePath, tempDir, description, pty, installDetourDecision, installDetourMode) {
|
|
822
1164
|
const sessionId = generateSessionId();
|
|
823
1165
|
const { bin, args, cwd: spawnCwd } = buildSpawnCommand(command, cwd, sandboxConfig, workspacePath ?? cwd, tempDir ?? tmpdir(), pty);
|
|
824
1166
|
const child = spawn(bin, args, {
|
|
@@ -840,6 +1182,11 @@ function executeBackground(command, cwd, env, input, registry, logger, sandboxCo
|
|
|
840
1182
|
maxOutputChars: BACKGROUND_MAX_OUTPUT_CHARS,
|
|
841
1183
|
sandboxed: !!sandboxConfig,
|
|
842
1184
|
...(description && { description }),
|
|
1185
|
+
// Spawn-time decision capture (advise+overlap only).
|
|
1186
|
+
...(installDetourDecision !== undefined
|
|
1187
|
+
&& installDetourDecision.overlaps.length > 0
|
|
1188
|
+
&& installDetourMode === "advise"
|
|
1189
|
+
&& { installDetourDecision }),
|
|
843
1190
|
};
|
|
844
1191
|
// Output cleaners for stateful UTF-8 decode + ANSI strip + CR normalize + binary sanitize
|
|
845
1192
|
const stdoutCleaner = createOutputCleaner();
|
|
@@ -886,6 +1233,23 @@ function executeBackground(command, cwd, env, input, registry, logger, sandboxCo
|
|
|
886
1233
|
// Register in ProcessRegistry
|
|
887
1234
|
registry.add(session);
|
|
888
1235
|
logger?.debug({ toolName: "exec", sessionId, pid: child.pid }, "Background process spawned");
|
|
1236
|
+
// Explicit-bg envelope augmentation in advise mode
|
|
1237
|
+
if (installDetourMode === "advise" &&
|
|
1238
|
+
installDetourDecision !== undefined &&
|
|
1239
|
+
installDetourDecision.overlaps.length > 0) {
|
|
1240
|
+
const hint = buildInstallDetourHint(installDetourDecision);
|
|
1241
|
+
const augmented = jsonResult({
|
|
1242
|
+
status: "started",
|
|
1243
|
+
sessionId,
|
|
1244
|
+
pid: child.pid,
|
|
1245
|
+
...(description && { description }),
|
|
1246
|
+
installDetourHint: hint.installDetourHint,
|
|
1247
|
+
});
|
|
1248
|
+
return {
|
|
1249
|
+
content: [...augmented.content, hint.hintContentBlock],
|
|
1250
|
+
details: augmented.details,
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
889
1253
|
return jsonResult({
|
|
890
1254
|
status: "started",
|
|
891
1255
|
sessionId,
|