@sayknow-cli/coding-agent 0.4.7 → 0.5.1
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/CHANGELOG.md +122 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-contract.d.ts +40 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +156 -10
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +33 -2
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +5 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +13 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
- package/dist/types/runtime-mcp/manager.d.ts +9 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +52 -4
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/query/handlers.d.ts +14 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +9 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +83 -24
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
- package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +89 -18
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/skc-runtime/state-writer.d.ts +5 -1
- package/dist/types/skc-runtime/team-launch.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
- package/dist/types/skc-runtime/team-store.d.ts +12 -0
- package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +133 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +22 -9
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +7 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -12
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/compile-args.ts +0 -1
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-docs-index.ts +4 -1
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +58 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +16 -100
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +6 -38
- package/src/cli/read-cli.ts +2 -1
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +133 -11
- package/src/commands/daemon.ts +22 -5
- package/src/commands/deep-interview.ts +26 -1
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +134 -17
- package/src/commands/team.ts +1 -1
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-contract.ts +174 -0
- package/src/config/model-profiles.ts +51 -10
- package/src/config/model-registry.ts +24 -4
- package/src/config/model-resolver.ts +2 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +157 -10
- package/src/config/settings.ts +254 -108
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
- package/src/defaults/skc/skills/team/SKILL.md +1 -1
- package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/eval/py/executor.ts +37 -13
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/extensibility/shared-events.ts +5 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/internal-urls/docs-index.generated.ts +17 -16
- package/src/internal-urls/local-protocol.ts +87 -25
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +120 -42
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +52 -0
- package/src/modes/acp/acp-agent.ts +647 -83
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/sayknow-pet-widget.ts +20 -8
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +26 -8
- package/src/modes/components/welcome.ts +50 -23
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +101 -54
- package/src/modes/controllers/event-controller.ts +119 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +100 -8
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +4 -1
- package/src/modes/interactive-mode.ts +201 -60
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +99 -4
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +17 -3
- package/src/prompts/agents/critic.md +17 -2
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -3
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
- package/src/runtime-mcp/json-rpc.ts +28 -11
- package/src/runtime-mcp/manager.ts +77 -59
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +293 -43
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +22 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +10 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +793 -121
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +87 -25
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/prompt-reconciliation.ts +240 -0
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
- package/src/sdk/bus/telegram-daemon-control.ts +161 -40
- package/src/sdk/bus/telegram-daemon.ts +3649 -593
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +8 -2
- package/src/sdk/host/control/operations.ts +3 -2
- package/src/sdk/host/host.ts +27 -9
- package/src/sdk/host/query/handlers.ts +125 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +145 -0
- package/src/sdk/protocol/operation-registry.ts +24 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +69 -8
- package/src/sdk/startup-capability.ts +15 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +1112 -314
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +1426 -259
- package/src/session/internal/managed-session-storage.ts +414 -238
- package/src/session/internal/native-publish-outcome.ts +271 -0
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2079 -469
- package/src/session/session-storage.ts +151 -11
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +49 -5
- package/src/setup/provider-presets.json +2 -1
- package/src/skc-runtime/deep-interview-runtime.ts +303 -36
- package/src/skc-runtime/deep-interview-stage.ts +1016 -0
- package/src/skc-runtime/deep-interview-state.ts +8 -5
- package/src/skc-runtime/gc-render.ts +1 -0
- package/src/skc-runtime/gc-runtime.ts +11 -1
- package/src/skc-runtime/linux-proc.ts +70 -36
- package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/skc-runtime/ralplan-runtime.ts +1057 -75
- package/src/skc-runtime/repository-binding.ts +267 -0
- package/src/skc-runtime/session-state-sidecar.ts +25 -9
- package/src/skc-runtime/state-runtime.ts +50 -12
- package/src/skc-runtime/state-writer.ts +68 -56
- package/src/skc-runtime/team-launch.ts +34 -5
- package/src/skc-runtime/team-runtime.ts +41 -1
- package/src/skc-runtime/team-store.ts +32 -0
- package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/skc-runtime/team-workers.ts +1 -0
- package/src/skc-runtime/tmux-common.ts +13 -2
- package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
- package/src/skc-runtime/tmux-sessions.ts +45 -6
- package/src/skc-runtime/ultragoal-guard.ts +93 -15
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
- package/src/skc-runtime/ultragoal-runtime.ts +469 -38
- package/src/skc-runtime/workflow-manifest.generated.json +195 -5
- package/src/skc-runtime/workflow-manifest.ts +67 -3
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +104 -12
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/executor.ts +165 -47
- package/src/task/index.ts +123 -15
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/receipt.ts +3 -0
- package/src/task/render.ts +45 -26
- package/src/task/skc-command.ts +2 -2
- package/src/task/types.ts +55 -10
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +127 -154
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +7 -1
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1544 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path-scoped mutation coordinator for edit tools.
|
|
3
|
+
*
|
|
4
|
+
* Serializes concurrent read→compute→write windows against the same absolute
|
|
5
|
+
* path so independent sessions cannot silently overwrite each other's successful
|
|
6
|
+
* disjoint edits (https://github.com/jaybeyond/Sayknow_CLI/issues/2900).
|
|
7
|
+
*
|
|
8
|
+
* - Always: in-process async mutex keyed by resolved absolute path.
|
|
9
|
+
* - Optionally: durable cross-process `<path>.lock` via `withFileLock` for real
|
|
10
|
+
* filesystem mutations (subagents / separate processes).
|
|
11
|
+
*
|
|
12
|
+
* Nested acquisition of the same path is not supported; callers that already
|
|
13
|
+
* hold the lock must not re-enter.
|
|
14
|
+
*/
|
|
15
|
+
import * as path from "node:path";
|
|
16
|
+
import { type FileLockOptions, withFileLock } from "../config/file-lock";
|
|
17
|
+
|
|
18
|
+
type AsyncMutex = {
|
|
19
|
+
acquire(): Promise<() => void>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const pathMutexes = new Map<string, AsyncMutex>();
|
|
23
|
+
|
|
24
|
+
function createAsyncMutex(): AsyncMutex {
|
|
25
|
+
let locked = false;
|
|
26
|
+
const waiters: Array<() => void> = [];
|
|
27
|
+
return {
|
|
28
|
+
async acquire(): Promise<() => void> {
|
|
29
|
+
if (!locked) {
|
|
30
|
+
locked = true;
|
|
31
|
+
return () => release();
|
|
32
|
+
}
|
|
33
|
+
const { promise, resolve } = Promise.withResolvers<void>();
|
|
34
|
+
waiters.push(resolve);
|
|
35
|
+
await promise;
|
|
36
|
+
return () => release();
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function release(): void {
|
|
41
|
+
const next = waiters.shift();
|
|
42
|
+
if (next) {
|
|
43
|
+
next();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
locked = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mutexFor(absolutePath: string): AsyncMutex {
|
|
51
|
+
const key = path.resolve(absolutePath);
|
|
52
|
+
let mutex = pathMutexes.get(key);
|
|
53
|
+
if (!mutex) {
|
|
54
|
+
mutex = createAsyncMutex();
|
|
55
|
+
pathMutexes.set(key, mutex);
|
|
56
|
+
}
|
|
57
|
+
return mutex;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function withInProcessPathLock<T>(absolutePath: string, fn: () => Promise<T>): Promise<T> {
|
|
61
|
+
const release = await mutexFor(absolutePath).acquire();
|
|
62
|
+
try {
|
|
63
|
+
return await fn();
|
|
64
|
+
} finally {
|
|
65
|
+
release();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Default lock budget: long enough for large writes + formatter writeback. */
|
|
70
|
+
const DEFAULT_CROSS_PROCESS_LOCK: FileLockOptions = {
|
|
71
|
+
staleMs: 60_000,
|
|
72
|
+
retries: 600,
|
|
73
|
+
retryDelayMs: 50,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type EditPathMutationOptions = {
|
|
77
|
+
/**
|
|
78
|
+
* When true (default), also acquire a durable cross-process file lock.
|
|
79
|
+
* Disable for injectible/in-memory filesystem tests that do not touch disk.
|
|
80
|
+
*/
|
|
81
|
+
crossProcess?: boolean;
|
|
82
|
+
fileLock?: FileLockOptions;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Run `fn` while holding exclusive mutation rights for every absolute path.
|
|
87
|
+
* Paths are locked in lexicographic order to avoid deadlocks on multi-path ops
|
|
88
|
+
* (e.g. rename source + destination).
|
|
89
|
+
*/
|
|
90
|
+
export async function withEditPathMutation<T>(
|
|
91
|
+
absolutePaths: readonly string[],
|
|
92
|
+
fn: () => Promise<T>,
|
|
93
|
+
options: EditPathMutationOptions = {},
|
|
94
|
+
): Promise<T> {
|
|
95
|
+
const unique = [...new Set(absolutePaths.map(entry => path.resolve(entry)))].sort();
|
|
96
|
+
if (unique.length === 0) return fn();
|
|
97
|
+
|
|
98
|
+
const crossProcess = options.crossProcess !== false;
|
|
99
|
+
const fileLock = { ...DEFAULT_CROSS_PROCESS_LOCK, ...options.fileLock };
|
|
100
|
+
|
|
101
|
+
const runAt = async (index: number): Promise<T> => {
|
|
102
|
+
if (index >= unique.length) return fn();
|
|
103
|
+
const target = unique[index];
|
|
104
|
+
const next = () => runAt(index + 1);
|
|
105
|
+
return withInProcessPathLock(target, async () => {
|
|
106
|
+
if (!crossProcess) return next();
|
|
107
|
+
return withFileLock(target, next, fileLock);
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return runAt(0);
|
|
112
|
+
}
|
|
@@ -30,7 +30,7 @@ function toolResultHasError(result: AgentToolResult): boolean {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function getTool(session: ToolSession, name: string): AgentTool {
|
|
33
|
-
const tool = session.getToolByName?.(name);
|
|
33
|
+
const tool = session.getToolForExecution ? session.getToolForExecution(name) : session.getToolByName?.(name);
|
|
34
34
|
if (!tool) {
|
|
35
35
|
throw new ToolError(`Unknown tool from js runtime: ${name}`);
|
|
36
36
|
}
|
package/src/eval/py/executor.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface PythonExecutorOptions {
|
|
|
61
61
|
/** @internal Bridge session id, set by `executePython` before delegating. */
|
|
62
62
|
bridgeSessionId?: string;
|
|
63
63
|
/** @internal Bridge endpoint info, set by `executePython` before delegating. */
|
|
64
|
-
bridge?: { url: string;
|
|
64
|
+
bridge?: { url: string; capability: string };
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
export interface PythonKernelExecutor {
|
|
@@ -106,6 +106,7 @@ export interface PythonResult {
|
|
|
106
106
|
interface PythonSession {
|
|
107
107
|
sessionId: string;
|
|
108
108
|
kernel: PythonKernel;
|
|
109
|
+
bridgeCapability?: string;
|
|
109
110
|
ownerIds: Set<string>;
|
|
110
111
|
hasFallbackOwner: boolean;
|
|
111
112
|
queue: Promise<void>;
|
|
@@ -261,14 +262,14 @@ function buildKernelEnv(options: {
|
|
|
261
262
|
sessionFile?: string;
|
|
262
263
|
artifactsDir?: string;
|
|
263
264
|
bridgeSessionId?: string;
|
|
264
|
-
bridge?: { url: string;
|
|
265
|
+
bridge?: { url: string; capability: string };
|
|
265
266
|
}): Record<string, string> | undefined {
|
|
266
267
|
const env: Record<string, string> = {};
|
|
267
268
|
if (options.sessionFile) env.PI_SESSION_FILE = options.sessionFile;
|
|
268
269
|
if (options.artifactsDir) env.PI_ARTIFACTS_DIR = options.artifactsDir;
|
|
269
270
|
if (options.bridge && options.bridgeSessionId) {
|
|
270
271
|
env.PI_TOOL_BRIDGE_URL = options.bridge.url;
|
|
271
|
-
env.
|
|
272
|
+
env.PI_TOOL_BRIDGE_CAPABILITY = options.bridge.capability;
|
|
272
273
|
env.PI_TOOL_BRIDGE_SESSION = options.bridgeSessionId;
|
|
273
274
|
}
|
|
274
275
|
return Object.keys(env).length > 0 ? env : undefined;
|
|
@@ -328,6 +329,7 @@ async function acquireSession(sessionId: string, cwd: string, options: PythonExe
|
|
|
328
329
|
const session: PythonSession = {
|
|
329
330
|
sessionId,
|
|
330
331
|
kernel,
|
|
332
|
+
bridgeCapability: options.bridge?.capability,
|
|
331
333
|
ownerIds: new Set(),
|
|
332
334
|
hasFallbackOwner: false,
|
|
333
335
|
queue: Promise.resolve(),
|
|
@@ -361,12 +363,25 @@ async function replaceSessionKernel(
|
|
|
361
363
|
throw new PythonExecutionCancelledError(false);
|
|
362
364
|
}
|
|
363
365
|
requireRemainingTimeoutMs(options.deadlineMs);
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
366
|
+
const bridge = options.bridge;
|
|
367
|
+
const previousCapability = bridge?.capability;
|
|
368
|
+
const nextCapability = bridge ? crypto.randomUUID() : undefined;
|
|
369
|
+
if (bridge && nextCapability) bridge.capability = nextCapability;
|
|
370
|
+
let next: PythonKernel | undefined;
|
|
371
|
+
try {
|
|
372
|
+
next = await startKernel(cwd, options);
|
|
373
|
+
if (sessions.get(session.sessionId) !== session) {
|
|
374
|
+
throw new PythonExecutionCancelledError(false);
|
|
375
|
+
}
|
|
376
|
+
session.kernel = next;
|
|
377
|
+
session.bridgeCapability = nextCapability;
|
|
378
|
+
} catch (err) {
|
|
379
|
+
await next?.shutdown().catch(() => undefined);
|
|
380
|
+
if (bridge && previousCapability && bridge.capability === nextCapability) {
|
|
381
|
+
bridge.capability = previousCapability;
|
|
382
|
+
}
|
|
383
|
+
throw err;
|
|
368
384
|
}
|
|
369
|
-
session.kernel = next;
|
|
370
385
|
}
|
|
371
386
|
|
|
372
387
|
async function resetSession(sessionId: string): Promise<void> {
|
|
@@ -481,8 +496,8 @@ async function executeWithKernel(
|
|
|
481
496
|
displayOutputs.push({ type: "status", event });
|
|
482
497
|
});
|
|
483
498
|
const unregisterBridge =
|
|
484
|
-
options?.toolSession && options?.bridgeSessionId
|
|
485
|
-
? registerPyToolBridge(options.bridgeSessionId, {
|
|
499
|
+
options?.toolSession && options?.bridgeSessionId && options.bridge
|
|
500
|
+
? registerPyToolBridge(options.bridgeSessionId, options.bridge.capability, {
|
|
486
501
|
toolSession: options.toolSession,
|
|
487
502
|
signal: options.signal,
|
|
488
503
|
emitStatus,
|
|
@@ -499,8 +514,11 @@ async function executeWithKernel(
|
|
|
499
514
|
});
|
|
500
515
|
|
|
501
516
|
if (result.cancelled) {
|
|
517
|
+
// Prefer the caller-configured timeout for the user-facing annotation.
|
|
518
|
+
// Remaining wall-clock budget can shrink after async setup (Settings.init,
|
|
519
|
+
// kernel start) and would otherwise flake Math.round() second formatting.
|
|
502
520
|
const annotation = result.timedOut
|
|
503
|
-
? formatKernelTimeoutAnnotation(executionTimeoutMs, result.kernelKilled ?? false)
|
|
521
|
+
? formatKernelTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs, result.kernelKilled ?? false)
|
|
504
522
|
: undefined;
|
|
505
523
|
let crashNotice: string | null = null;
|
|
506
524
|
if (result.kernelKilled) {
|
|
@@ -554,7 +572,9 @@ async function executeWithKernel(
|
|
|
554
572
|
cancelled: true,
|
|
555
573
|
displayOutputs,
|
|
556
574
|
stdinRequested: false,
|
|
557
|
-
...(await sink.dump(
|
|
575
|
+
...(await sink.dump(
|
|
576
|
+
timedOut ? formatTimeoutAnnotation(options?.timeoutMs ?? executionTimeoutMs) : undefined,
|
|
577
|
+
)),
|
|
558
578
|
};
|
|
559
579
|
}
|
|
560
580
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -578,7 +598,8 @@ async function ensureKernelAvailable(cwd: string, options: PythonExecutorOptions
|
|
|
578
598
|
async function ensureToolBridge(options: PythonExecutorOptions): Promise<void> {
|
|
579
599
|
if (!options.toolSession || options.bridge) return;
|
|
580
600
|
try {
|
|
581
|
-
|
|
601
|
+
const bridge = await ensurePyToolBridge();
|
|
602
|
+
options.bridge = { ...bridge, capability: crypto.randomUUID() };
|
|
582
603
|
} catch (err) {
|
|
583
604
|
logger.warn("Failed to start Python tool bridge", {
|
|
584
605
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -607,6 +628,9 @@ async function executeOnSession(code: string, cwd: string, options: PythonExecut
|
|
|
607
628
|
await resetSession(sessionId);
|
|
608
629
|
}
|
|
609
630
|
const session = await acquireSession(sessionId, cwd, options);
|
|
631
|
+
if (options.bridge && session.bridgeCapability) {
|
|
632
|
+
options.bridge.capability = session.bridgeCapability;
|
|
633
|
+
}
|
|
610
634
|
return await runQueued(session, options, async () => {
|
|
611
635
|
if (options.signal?.aborted) {
|
|
612
636
|
throw new PythonExecutionCancelledError(isTimedOutCancellation(options.signal.reason, options.signal));
|
package/src/eval/py/kernel.ts
CHANGED
|
@@ -8,9 +8,6 @@
|
|
|
8
8
|
* Shutdown writes `{"type":"exit"}` and escalates to SIGTERM/SIGKILL on
|
|
9
9
|
* timeout.
|
|
10
10
|
*/
|
|
11
|
-
import * as fs from "node:fs";
|
|
12
|
-
import * as os from "node:os";
|
|
13
|
-
import * as path from "node:path";
|
|
14
11
|
import { $env, isBunTestRuntime, logger, Snowflake } from "@sayknow-cli/utils";
|
|
15
12
|
import type { Subprocess } from "bun";
|
|
16
13
|
import { $ } from "bun";
|
|
@@ -18,7 +15,7 @@ import { Settings } from "../../config/settings";
|
|
|
18
15
|
import { type KernelDisplayOutput, renderKernelDisplay } from "./display";
|
|
19
16
|
import { resolvePythonIpcTrace, resolvePythonSkipCheck } from "./env";
|
|
20
17
|
import { PYTHON_PRELUDE } from "./prelude";
|
|
21
|
-
import
|
|
18
|
+
import { ensureRunnerScript } from "./runner-artifact";
|
|
22
19
|
import { ensurePythonRuntime, filterEnv, type PythonRuntimeOptions } from "./runtime";
|
|
23
20
|
|
|
24
21
|
export type { KernelDisplayOutput, PythonStatusEvent } from "./display";
|
|
@@ -27,23 +24,6 @@ export { renderKernelDisplay } from "./display";
|
|
|
27
24
|
// Dual-read: `SKC_PYTHON_IPC_TRACE` is preferred, then legacy `PI_PYTHON_IPC_TRACE`.
|
|
28
25
|
const TRACE_IPC = resolvePythonIpcTrace($env);
|
|
29
26
|
|
|
30
|
-
// Cache the runner script on disk so the subprocess loads it normally. Cached
|
|
31
|
-
// per script hash so installs don't race across versions.
|
|
32
|
-
const RUNNER_CACHE_DIR = path.join(os.tmpdir(), "skc-python-runner");
|
|
33
|
-
let RUNNER_SCRIPT_PATH: string | null = null;
|
|
34
|
-
|
|
35
|
-
async function ensureRunnerScript(): Promise<string> {
|
|
36
|
-
if (RUNNER_SCRIPT_PATH) return RUNNER_SCRIPT_PATH;
|
|
37
|
-
await fs.promises.mkdir(RUNNER_CACHE_DIR, { recursive: true });
|
|
38
|
-
const hash = Bun.hash(RUNNER_SCRIPT).toString(36);
|
|
39
|
-
const target = path.join(RUNNER_CACHE_DIR, `runner-${hash}.py`);
|
|
40
|
-
if (!fs.existsSync(target)) {
|
|
41
|
-
await Bun.write(target, RUNNER_SCRIPT);
|
|
42
|
-
}
|
|
43
|
-
RUNNER_SCRIPT_PATH = target;
|
|
44
|
-
return target;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
27
|
const SHUTDOWN_GRACE_MS = 1_000;
|
|
48
28
|
const STARTUP_TIMEOUT_MS = 10_000;
|
|
49
29
|
// How long to wait after SIGINT for the runner to emit `done`. If the cell is
|
package/src/eval/py/prelude.py
CHANGED
|
@@ -453,10 +453,10 @@ if "__skc_prelude_loaded__" not in globals():
|
|
|
453
453
|
|
|
454
454
|
if all(
|
|
455
455
|
_k in os.environ
|
|
456
|
-
for _k in ("PI_TOOL_BRIDGE_URL", "
|
|
456
|
+
for _k in ("PI_TOOL_BRIDGE_URL", "PI_TOOL_BRIDGE_CAPABILITY", "PI_TOOL_BRIDGE_SESSION")
|
|
457
457
|
):
|
|
458
458
|
tool = _ToolProxy(
|
|
459
459
|
os.environ["PI_TOOL_BRIDGE_URL"],
|
|
460
|
-
os.environ["
|
|
460
|
+
os.environ["PI_TOOL_BRIDGE_CAPABILITY"],
|
|
461
461
|
os.environ["PI_TOOL_BRIDGE_SESSION"],
|
|
462
462
|
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { postmortem } from "@sayknow-cli/utils";
|
|
5
|
+
import RUNNER_SCRIPT from "./runner.py" with { type: "text" };
|
|
6
|
+
|
|
7
|
+
const DIRECTORY_MODE = 0o700;
|
|
8
|
+
const FILE_MODE = 0o600;
|
|
9
|
+
const DIRECTORY_PREFIX = "skc-python-runner-";
|
|
10
|
+
|
|
11
|
+
interface RunnerScriptArtifact {
|
|
12
|
+
directory: string;
|
|
13
|
+
scriptPath: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function initializeRunnerScript(tempRoot: string): Promise<RunnerScriptArtifact> {
|
|
17
|
+
const directory = await fs.mkdtemp(path.join(tempRoot, DIRECTORY_PREFIX));
|
|
18
|
+
try {
|
|
19
|
+
if (process.platform !== "win32") await fs.chmod(directory, DIRECTORY_MODE);
|
|
20
|
+
const scriptPath = path.join(directory, "runner.py");
|
|
21
|
+
const handle = await fs.open(scriptPath, "wx", FILE_MODE);
|
|
22
|
+
try {
|
|
23
|
+
await handle.writeFile(RUNNER_SCRIPT, { encoding: "utf8" });
|
|
24
|
+
if (process.platform !== "win32") await handle.chmod(FILE_MODE);
|
|
25
|
+
} finally {
|
|
26
|
+
await handle.close();
|
|
27
|
+
}
|
|
28
|
+
return { directory, scriptPath };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
await fs.rm(directory, { recursive: true, force: true }).catch(() => {});
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface RunnerScriptCache {
|
|
36
|
+
ensureRunnerScript(): Promise<string>;
|
|
37
|
+
cleanup(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createRunnerScriptCache(tempRoot: string): RunnerScriptCache {
|
|
41
|
+
let initialization: Promise<RunnerScriptArtifact> | null = null;
|
|
42
|
+
let artifact: RunnerScriptArtifact | null = null;
|
|
43
|
+
let cleanupAttempt: Promise<void> | null = null;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
async ensureRunnerScript() {
|
|
47
|
+
if (artifact) return artifact.scriptPath;
|
|
48
|
+
if (!initialization) initialization = initializeRunnerScript(tempRoot);
|
|
49
|
+
try {
|
|
50
|
+
artifact = await initialization;
|
|
51
|
+
return artifact.scriptPath;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (initialization) initialization = null;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
async cleanup() {
|
|
58
|
+
if (cleanupAttempt) return await cleanupAttempt;
|
|
59
|
+
cleanupAttempt = (async () => {
|
|
60
|
+
const initialized = await initialization?.catch(() => null);
|
|
61
|
+
const directory = artifact?.directory ?? initialized?.directory;
|
|
62
|
+
artifact = null;
|
|
63
|
+
initialization = null;
|
|
64
|
+
if (directory) await fs.rm(directory, { recursive: true, force: true });
|
|
65
|
+
})();
|
|
66
|
+
try {
|
|
67
|
+
await cleanupAttempt;
|
|
68
|
+
} finally {
|
|
69
|
+
cleanupAttempt = null;
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** @internal Test-only initializer for isolated artifact assertions. */
|
|
76
|
+
export function createRunnerScriptInitializer(tempRoot: string): () => Promise<string> {
|
|
77
|
+
return createRunnerScriptCache(tempRoot).ensureRunnerScript;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const processRunnerScriptCache = createRunnerScriptCache(os.tmpdir());
|
|
81
|
+
|
|
82
|
+
export const ensureRunnerScript = () => processRunnerScriptCache.ensureRunnerScript();
|
|
83
|
+
|
|
84
|
+
postmortem.register("python-runner-artifact", () => processRunnerScriptCache.cleanup());
|
|
@@ -19,7 +19,10 @@ export interface PyToolBridgeEntry {
|
|
|
19
19
|
|
|
20
20
|
export interface PyToolBridgeInfo {
|
|
21
21
|
url: string;
|
|
22
|
-
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface PyToolBridgeRegistration extends PyToolBridgeEntry {
|
|
25
|
+
sessionId: string;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
interface BridgeServer {
|
|
@@ -27,11 +30,20 @@ interface BridgeServer {
|
|
|
27
30
|
stop: () => Promise<void>;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
const registrations = new Map<string,
|
|
33
|
+
const registrations = new Map<string, PyToolBridgeRegistration>();
|
|
31
34
|
let serverPromise: Promise<BridgeServer> | null = null;
|
|
32
35
|
|
|
36
|
+
function isCanonicalCapability(capability: string): boolean {
|
|
37
|
+
return capability.length > 0 && !/\s/.test(capability);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseBearerCapability(authorization: string | null): string | null {
|
|
41
|
+
if (!authorization?.startsWith("Bearer ")) return null;
|
|
42
|
+
const capability = authorization.slice("Bearer ".length);
|
|
43
|
+
return isCanonicalCapability(capability) ? capability : null;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
async function startServer(): Promise<BridgeServer> {
|
|
34
|
-
const token = crypto.randomUUID();
|
|
35
47
|
const server = Bun.serve({
|
|
36
48
|
hostname: "127.0.0.1",
|
|
37
49
|
port: 0,
|
|
@@ -40,7 +52,12 @@ async function startServer(): Promise<BridgeServer> {
|
|
|
40
52
|
if (req.method !== "POST" || url.pathname !== "/v1/tool") {
|
|
41
53
|
return new Response("Not Found", { status: 404 });
|
|
42
54
|
}
|
|
43
|
-
|
|
55
|
+
const capability = parseBearerCapability(req.headers.get("authorization"));
|
|
56
|
+
if (!capability) {
|
|
57
|
+
return new Response("Forbidden", { status: 403 });
|
|
58
|
+
}
|
|
59
|
+
const registration = registrations.get(capability);
|
|
60
|
+
if (!registration) {
|
|
44
61
|
return new Response("Forbidden", { status: 403 });
|
|
45
62
|
}
|
|
46
63
|
|
|
@@ -55,19 +72,15 @@ async function startServer(): Promise<BridgeServer> {
|
|
|
55
72
|
if (!sessionId || !name) {
|
|
56
73
|
return Response.json({ ok: false, error: "Missing session/name" }, { status: 400 });
|
|
57
74
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return Response.json(
|
|
61
|
-
{ ok: false, error: `No active Python tool bridge session: ${sessionId}` },
|
|
62
|
-
{ status: 200 },
|
|
63
|
-
);
|
|
75
|
+
if (sessionId !== registration.sessionId) {
|
|
76
|
+
return new Response("Forbidden", { status: 403 });
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
try {
|
|
67
80
|
const value = await callSessionTool(name, body.args, {
|
|
68
|
-
session:
|
|
69
|
-
signal:
|
|
70
|
-
emitStatus:
|
|
81
|
+
session: registration.toolSession,
|
|
82
|
+
signal: registration.signal,
|
|
83
|
+
emitStatus: registration.emitStatus,
|
|
71
84
|
});
|
|
72
85
|
return Response.json({ ok: true, value });
|
|
73
86
|
} catch (err) {
|
|
@@ -81,7 +94,6 @@ async function startServer(): Promise<BridgeServer> {
|
|
|
81
94
|
|
|
82
95
|
const info: PyToolBridgeInfo = {
|
|
83
96
|
url: `http://${server.hostname}:${server.port}`,
|
|
84
|
-
token,
|
|
85
97
|
};
|
|
86
98
|
logger.debug("Python tool bridge listening", { url: info.url });
|
|
87
99
|
|
|
@@ -111,11 +123,15 @@ export async function ensurePyToolBridge(): Promise<PyToolBridgeInfo> {
|
|
|
111
123
|
* Register a tool session for the duration of one execution. The returned
|
|
112
124
|
* function MUST be called to remove the entry once execution finishes.
|
|
113
125
|
*/
|
|
114
|
-
export function registerPyToolBridge(sessionId: string, entry: PyToolBridgeEntry): () => void {
|
|
115
|
-
|
|
126
|
+
export function registerPyToolBridge(sessionId: string, capability: string, entry: PyToolBridgeEntry): () => void {
|
|
127
|
+
if (!isCanonicalCapability(capability)) {
|
|
128
|
+
throw new Error("Python tool bridge capability must be a non-empty canonical bearer token");
|
|
129
|
+
}
|
|
130
|
+
const registration = { ...entry, sessionId };
|
|
131
|
+
registrations.set(capability, registration);
|
|
116
132
|
return () => {
|
|
117
|
-
if (registrations.get(
|
|
118
|
-
registrations.delete(
|
|
133
|
+
if (registrations.get(capability) === registration) {
|
|
134
|
+
registrations.delete(capability);
|
|
119
135
|
}
|
|
120
136
|
};
|
|
121
137
|
}
|
|
@@ -201,6 +201,7 @@ export class ExtensionRunner {
|
|
|
201
201
|
#getAllToolsFn: ExtensionContext["getAllTools"] = () => [];
|
|
202
202
|
#getResolveToolFn: ExtensionContext["resolveTool"] = () => undefined;
|
|
203
203
|
#cycleModelFn: ExtensionContextActions["cycleModel"] = undefined;
|
|
204
|
+
#setModelProfileFn: ExtensionContextActions["setModelProfile"] = undefined;
|
|
204
205
|
#cycleThinkingLevelFn: ExtensionContextActions["cycleThinkingLevel"] = undefined;
|
|
205
206
|
#setQueueModeFn: ExtensionContextActions["setQueueMode"] = undefined;
|
|
206
207
|
#getSkillStateFn: ExtensionContextActions["getSkillState"] = undefined;
|
|
@@ -313,6 +314,7 @@ export class ExtensionRunner {
|
|
|
313
314
|
this.#getAllToolsFn = contextActions.getAllTools ?? (() => []);
|
|
314
315
|
this.#getResolveToolFn = contextActions.resolveTool ?? (() => undefined);
|
|
315
316
|
this.#cycleModelFn = contextActions.cycleModel;
|
|
317
|
+
this.#setModelProfileFn = contextActions.setModelProfile;
|
|
316
318
|
this.#cycleThinkingLevelFn = contextActions.cycleThinkingLevel;
|
|
317
319
|
this.#setQueueModeFn = contextActions.setQueueMode;
|
|
318
320
|
this.#getSkillStateFn = contextActions.getSkillState;
|
|
@@ -562,6 +564,7 @@ export class ExtensionRunner {
|
|
|
562
564
|
getAllTools: () => this.#getAllToolsFn(),
|
|
563
565
|
resolveTool: name => this.#getResolveToolFn(name),
|
|
564
566
|
cycleModel: async () => await this.#cycleModelFn?.(),
|
|
567
|
+
setModelProfile: async name => (await this.#setModelProfileFn?.(name)) ?? false,
|
|
565
568
|
cycleThinkingLevel: () => this.#cycleThinkingLevelFn?.(),
|
|
566
569
|
setQueueMode: (kind, mode) => this.#setQueueModeFn?.(kind, mode) ?? false,
|
|
567
570
|
invokeSkill: async (name, args) => await this.#invokeSkillFn?.(name, args),
|
|
@@ -581,6 +584,7 @@ export class ExtensionRunner {
|
|
|
581
584
|
setSdkPermissionProvider: provider => this.#setSdkPermissionProviderFn?.(provider),
|
|
582
585
|
sdkBindings: () => [
|
|
583
586
|
...(this.#cycleModelFn ? ["cycleModel"] : []),
|
|
587
|
+
...(this.#setModelProfileFn ? ["setModelProfile"] : []),
|
|
584
588
|
...(this.#cycleThinkingLevelFn ? ["cycleThinkingLevel"] : []),
|
|
585
589
|
...(this.#setQueueModeFn ? ["setQueueMode"] : []),
|
|
586
590
|
...(this.#getSkillStateFn ? ["getSkillState"] : []),
|
|
@@ -290,6 +290,18 @@ export interface ExtensionTranscriptEntry {
|
|
|
290
290
|
textSummary: string;
|
|
291
291
|
ts: string;
|
|
292
292
|
body?: string;
|
|
293
|
+
/**
|
|
294
|
+
* Durable, image-free message blocks used by rich transcript consumers such
|
|
295
|
+
* as ACP replay. Binary image payloads intentionally remain unavailable.
|
|
296
|
+
*/
|
|
297
|
+
content?: Array<
|
|
298
|
+
| { type: "text"; text: string }
|
|
299
|
+
| { type: "thinking"; thinking: string }
|
|
300
|
+
| { type: "toolCall"; id: string; name: string; arguments: unknown }
|
|
301
|
+
>;
|
|
302
|
+
toolCallId?: string;
|
|
303
|
+
toolName?: string;
|
|
304
|
+
isError?: boolean;
|
|
293
305
|
}
|
|
294
306
|
|
|
295
307
|
export interface ContextUsage {
|
|
@@ -361,6 +373,7 @@ export interface ExtensionContext {
|
|
|
361
373
|
resolveTool(name: string): Pick<Tool, "safeSummary" | "safeSummaryFields"> | undefined;
|
|
362
374
|
/** Session control seams used by the SDK host. */
|
|
363
375
|
cycleModel(): Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
|
|
376
|
+
setModelProfile?(name: string): Promise<boolean>;
|
|
364
377
|
cycleThinkingLevel(): ThinkingLevel | undefined;
|
|
365
378
|
setQueueMode(kind: "steering" | "follow_up" | "interrupt", mode: unknown): boolean;
|
|
366
379
|
getSkillState(): unknown;
|
|
@@ -379,7 +392,15 @@ export interface ExtensionContext {
|
|
|
379
392
|
|
|
380
393
|
getJobs(): unknown;
|
|
381
394
|
/** Typed skill and mode controls exposed to the SDK host. */
|
|
382
|
-
invokeSkill?(
|
|
395
|
+
invokeSkill?(
|
|
396
|
+
name: string,
|
|
397
|
+
args?: string,
|
|
398
|
+
options?: {
|
|
399
|
+
onPreflightAccepted?: () => void;
|
|
400
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
401
|
+
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
402
|
+
},
|
|
403
|
+
): Promise<unknown>;
|
|
383
404
|
setPlanMode?(on: boolean): unknown;
|
|
384
405
|
operateGoal?(op: "create" | "get" | "resume" | "pause" | "complete" | "drop", objective?: string): Promise<unknown>;
|
|
385
406
|
|
|
@@ -1122,7 +1143,11 @@ export interface ExtensionAPI {
|
|
|
1122
1143
|
/** Send a user message to the agent, or queue it when deliverAs is set. */
|
|
1123
1144
|
sendUserMessage(
|
|
1124
1145
|
content: string | (TextContent | ImageContent)[],
|
|
1125
|
-
options?: {
|
|
1146
|
+
options?: {
|
|
1147
|
+
deliverAs?: "steer" | "followUp";
|
|
1148
|
+
onPreflightAccepted?: () => void;
|
|
1149
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1150
|
+
},
|
|
1126
1151
|
): Promise<void>;
|
|
1127
1152
|
|
|
1128
1153
|
/** Append a custom entry to the session for state persistence (not sent to LLM). */
|
|
@@ -1331,7 +1356,11 @@ export type SendMessageHandler = <T = unknown>(
|
|
|
1331
1356
|
|
|
1332
1357
|
export type SendUserMessageHandler = (
|
|
1333
1358
|
content: string | (TextContent | ImageContent)[],
|
|
1334
|
-
options?: {
|
|
1359
|
+
options?: {
|
|
1360
|
+
deliverAs?: "steer" | "followUp";
|
|
1361
|
+
onPreflightAccepted?: () => void;
|
|
1362
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1363
|
+
},
|
|
1335
1364
|
) => void | Promise<void>;
|
|
1336
1365
|
|
|
1337
1366
|
export type AppendEntryHandler = <T = unknown>(customType: string, data?: T) => void;
|
|
@@ -1429,6 +1458,7 @@ export interface ExtensionContextActions {
|
|
|
1429
1458
|
clearContext?: () => Promise<boolean>;
|
|
1430
1459
|
/** Session control and query seams exposed to the per-session SDK host. */
|
|
1431
1460
|
cycleModel?: () => Promise<{ model: Model; thinkingLevel: ThinkingLevel | undefined } | undefined>;
|
|
1461
|
+
setModelProfile?: (name: string) => Promise<boolean>;
|
|
1432
1462
|
cycleThinkingLevel?: () => ThinkingLevel | undefined;
|
|
1433
1463
|
setQueueMode?: (kind: "steering" | "follow_up" | "interrupt", mode: unknown) => boolean;
|
|
1434
1464
|
getSkillState?: () => unknown;
|
|
@@ -1455,7 +1485,15 @@ export interface ExtensionContextActions {
|
|
|
1455
1485
|
| undefined,
|
|
1456
1486
|
) => void;
|
|
1457
1487
|
sdkControl?: (operation: string, input: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
1458
|
-
invokeSkill?: (
|
|
1488
|
+
invokeSkill?: (
|
|
1489
|
+
name: string,
|
|
1490
|
+
args?: string,
|
|
1491
|
+
options?: {
|
|
1492
|
+
onPreflightAccepted?: () => void;
|
|
1493
|
+
onPreflightAcceptCommit?: () => void | Promise<void>;
|
|
1494
|
+
onSkillPrepared?: (meta: { name: string; path: string; lineCount?: number; cleanedArgs?: string }) => void;
|
|
1495
|
+
},
|
|
1496
|
+
) => Promise<unknown>;
|
|
1459
1497
|
setPlanMode?: (on: boolean) => unknown;
|
|
1460
1498
|
operateGoal?: (
|
|
1461
1499
|
op: "create" | "get" | "resume" | "pause" | "complete" | "drop",
|