@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
|
@@ -149,6 +149,19 @@ async function assertDirectoryNotSymlink(directoryPath: string): Promise<void> {
|
|
|
149
149
|
const LEGACY_MIGRATION_MARKER = ".skc-local-legacy-migrated-v1";
|
|
150
150
|
const MAX_LEGACY_LOCAL_BYTES = 64 * 1024 * 1024;
|
|
151
151
|
|
|
152
|
+
type LegacyMigrationState = "complete" | "cleanup_pending";
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Marker values that mean legacy migration has settled for this root and the
|
|
156
|
+
* synchronous resolver may proceed. `cleanup_pending` counts: the entries are
|
|
157
|
+
* installed and content-verified, and only retirement of the legacy source is
|
|
158
|
+
* outstanding. Must stay in sync with {@link readMigrationMarker}, which the
|
|
159
|
+
* async gate uses to decide the same question.
|
|
160
|
+
*/
|
|
161
|
+
function isSettledMigrationMarkerValue(value: string): boolean {
|
|
162
|
+
return value === "verified\n" || value === "absent\n" || value === "cleanup_pending\n";
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
interface LegacyEntrySnapshot {
|
|
153
166
|
readonly relativePath: string;
|
|
154
167
|
readonly dev: bigint;
|
|
@@ -173,6 +186,26 @@ async function snapshotDirectory(directoryPath: string, relativePath: string): P
|
|
|
173
186
|
return { relativePath, dev: stat.dev, ino: stat.ino, size: stat.size };
|
|
174
187
|
}
|
|
175
188
|
|
|
189
|
+
interface CanonicalLegacyRoot {
|
|
190
|
+
readonly root: string;
|
|
191
|
+
readonly identity: LegacyEntrySnapshot;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function canonicalLegacyRoot(artifactsDir: string): Promise<CanonicalLegacyRoot> {
|
|
195
|
+
const lexicalRoot = path.resolve(artifactsDir, "local");
|
|
196
|
+
const lexicalIdentity = await snapshotDirectory(lexicalRoot, "");
|
|
197
|
+
const [canonicalArtifactsDir, canonicalRoot] = await Promise.all([
|
|
198
|
+
fs.realpath(path.resolve(artifactsDir)),
|
|
199
|
+
fs.realpath(lexicalRoot),
|
|
200
|
+
]);
|
|
201
|
+
if (canonicalRoot === canonicalArtifactsDir) throw new Error("Unsafe legacy local:// migration source");
|
|
202
|
+
ensureWithinRoot(canonicalRoot, canonicalArtifactsDir);
|
|
203
|
+
const canonicalIdentity = await snapshotDirectory(canonicalRoot, "");
|
|
204
|
+
if (!matchesSnapshot(canonicalIdentity, lexicalIdentity))
|
|
205
|
+
throw new Error("Legacy local:// migration source changed during capture");
|
|
206
|
+
return { root: canonicalRoot, identity: lexicalIdentity };
|
|
207
|
+
}
|
|
208
|
+
|
|
176
209
|
async function snapshotRegularFile(filePath: string, relativePath: string): Promise<LegacyEntrySnapshot> {
|
|
177
210
|
const before = await fs.lstat(filePath, { bigint: true });
|
|
178
211
|
if (!before.isFile() || before.isSymbolicLink()) throw new Error("Unsafe legacy local:// migration source");
|
|
@@ -206,11 +239,16 @@ function manifestsMatch(left: readonly LegacyEntrySnapshot[], right: readonly Le
|
|
|
206
239
|
);
|
|
207
240
|
}
|
|
208
241
|
|
|
209
|
-
async function captureLegacyManifest(
|
|
242
|
+
async function captureLegacyManifest(
|
|
243
|
+
root: string,
|
|
244
|
+
expectedRootIdentity: LegacyEntrySnapshot,
|
|
245
|
+
): Promise<readonly LegacyEntrySnapshot[]> {
|
|
210
246
|
const manifest: LegacyEntrySnapshot[] = [];
|
|
211
247
|
let copiedBytes = 0n;
|
|
212
248
|
const captureDirectory = async (directoryPath: string, relativePath: string): Promise<void> => {
|
|
213
249
|
const directory = await snapshotDirectory(directoryPath, relativePath);
|
|
250
|
+
if (relativePath === "" && !matchesSnapshot(directory, expectedRootIdentity))
|
|
251
|
+
throw new Error("Legacy local:// migration source changed during capture");
|
|
214
252
|
manifest.push(directory);
|
|
215
253
|
const entries = (await fs.readdir(directoryPath, { withFileTypes: true })).sort((a, b) =>
|
|
216
254
|
a.name.localeCompare(b.name),
|
|
@@ -313,7 +351,7 @@ async function migrateManagedLegacyLocal(
|
|
|
313
351
|
}
|
|
314
352
|
const staging = path.join(scratchParent, `.skc-local-migration-${randomUUID()}`);
|
|
315
353
|
const installed: Array<{ readonly path: string; readonly dev: bigint; readonly ino: bigint }> = [];
|
|
316
|
-
let
|
|
354
|
+
let sourceRetired = false;
|
|
317
355
|
try {
|
|
318
356
|
await fs.mkdir(staging, { mode: 0o700 });
|
|
319
357
|
for (const entry of captured.entries) {
|
|
@@ -327,7 +365,7 @@ async function migrateManagedLegacyLocal(
|
|
|
327
365
|
throw new Error("Legacy local:// migration destination verification failed");
|
|
328
366
|
}
|
|
329
367
|
}
|
|
330
|
-
for (const entry of await fs.readdir(staging)) {
|
|
368
|
+
for (const entry of (await fs.readdir(staging)).sort()) {
|
|
331
369
|
const destination = path.join(localRoot, entry);
|
|
332
370
|
try {
|
|
333
371
|
await fs.lstat(destination);
|
|
@@ -339,18 +377,22 @@ async function migrateManagedLegacyLocal(
|
|
|
339
377
|
const identity = await fs.lstat(destination, { bigint: true });
|
|
340
378
|
installed.push({ path: destination, dev: identity.dev, ino: identity.ino });
|
|
341
379
|
}
|
|
380
|
+
try {
|
|
381
|
+
source.retire(captured.snapshot);
|
|
382
|
+
sourceRetired = true;
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (!(error instanceof Error) || error.message !== "cleanup_pending") throw error;
|
|
385
|
+
await fs.writeFile(marker, "cleanup_pending\n", { mode: 0o600, flag: "wx" });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
342
388
|
await fs.writeFile(marker, "verified\n", { mode: 0o600, flag: "wx" });
|
|
343
|
-
const markerIdentity = await fs.lstat(marker, { bigint: true });
|
|
344
|
-
installedMarker = { dev: markerIdentity.dev, ino: markerIdentity.ino };
|
|
345
|
-
source.retire(captured.snapshot);
|
|
346
389
|
} catch (error) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
await fs.rm(destination.path, { recursive: true, force: true });
|
|
390
|
+
if (!sourceRetired) {
|
|
391
|
+
for (const destination of installed.reverse()) {
|
|
392
|
+
const current = await fs.lstat(destination.path, { bigint: true }).catch(() => null);
|
|
393
|
+
if (current?.dev === destination.dev && current.ino === destination.ino)
|
|
394
|
+
await fs.rm(destination.path, { recursive: true, force: true });
|
|
395
|
+
}
|
|
354
396
|
}
|
|
355
397
|
throw error;
|
|
356
398
|
} finally {
|
|
@@ -358,15 +400,16 @@ async function migrateManagedLegacyLocal(
|
|
|
358
400
|
}
|
|
359
401
|
}
|
|
360
402
|
|
|
361
|
-
async function readMigrationMarker(marker: string): Promise<
|
|
403
|
+
async function readMigrationMarker(marker: string): Promise<LegacyMigrationState | null> {
|
|
362
404
|
try {
|
|
363
405
|
const snapshot = await snapshotRegularFile(marker, LEGACY_MIGRATION_MARKER);
|
|
364
406
|
if (snapshot.size > 32n || snapshot.bytes === undefined) throw new Error("Unsafe local:// migration marker");
|
|
365
407
|
const value = snapshot.bytes.toString("utf8");
|
|
366
|
-
if (value
|
|
367
|
-
return
|
|
408
|
+
if (value === "cleanup_pending\n") return "cleanup_pending";
|
|
409
|
+
if (value === "verified\n" || value === "absent\n") return "complete";
|
|
410
|
+
throw new Error("Unsafe local:// migration marker");
|
|
368
411
|
} catch (error) {
|
|
369
|
-
if (isEnoent(error)) return
|
|
412
|
+
if (isEnoent(error)) return null;
|
|
370
413
|
throw error;
|
|
371
414
|
}
|
|
372
415
|
}
|
|
@@ -388,10 +431,11 @@ async function migrateLegacyLocal(
|
|
|
388
431
|
await fs.writeFile(marker, "absent\n", { mode: 0o600, flag: "wx" });
|
|
389
432
|
return;
|
|
390
433
|
}
|
|
391
|
-
|
|
434
|
+
let legacySource: CanonicalLegacyRoot;
|
|
392
435
|
let manifest: readonly LegacyEntrySnapshot[];
|
|
393
436
|
try {
|
|
394
|
-
|
|
437
|
+
legacySource = await canonicalLegacyRoot(artifactsDir);
|
|
438
|
+
manifest = await captureLegacyManifest(legacySource.root, legacySource.identity);
|
|
395
439
|
} catch (error) {
|
|
396
440
|
if (isEnoent(error)) {
|
|
397
441
|
await fs.writeFile(marker, "absent\n", { mode: 0o600, flag: "wx" });
|
|
@@ -401,8 +445,8 @@ async function migrateLegacyLocal(
|
|
|
401
445
|
}
|
|
402
446
|
const staging = path.join(scratchParent, `.skc-local-migration-${randomUUID()}`);
|
|
403
447
|
try {
|
|
404
|
-
await copyLegacyManifest(
|
|
405
|
-
const verifiedManifest = await captureLegacyManifest(
|
|
448
|
+
await copyLegacyManifest(legacySource.root, staging, manifest);
|
|
449
|
+
const verifiedManifest = await captureLegacyManifest(legacySource.root, legacySource.identity);
|
|
406
450
|
if (!manifestsMatch(verifiedManifest, manifest))
|
|
407
451
|
throw new Error("Legacy local:// migration source changed during capture");
|
|
408
452
|
for (const entry of await fs.readdir(staging)) {
|
|
@@ -414,7 +458,17 @@ async function migrateLegacyLocal(
|
|
|
414
458
|
}
|
|
415
459
|
await fs.rename(path.join(staging, entry), path.join(localRoot, entry));
|
|
416
460
|
}
|
|
417
|
-
|
|
461
|
+
try {
|
|
462
|
+
await retireLegacyTree(legacySource.root, manifest);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
if (
|
|
465
|
+
!(error instanceof Error) ||
|
|
466
|
+
error.message !== "Legacy local:// migration retirement failed: cleanup_pending"
|
|
467
|
+
)
|
|
468
|
+
throw error;
|
|
469
|
+
await fs.writeFile(marker, "cleanup_pending\n", { mode: 0o600, flag: "wx" });
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
418
472
|
await fs.writeFile(marker, "verified\n", { mode: 0o600, flag: "wx" });
|
|
419
473
|
} finally {
|
|
420
474
|
await fs.rm(staging, { recursive: true, force: true });
|
|
@@ -501,8 +555,13 @@ function initializeLocalRootSyncWhenLegacyAbsent(options: LocalProtocolOptions,
|
|
|
501
555
|
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) throw new Error("Unsafe local:// root");
|
|
502
556
|
const marker = path.join(localRoot, LEGACY_MIGRATION_MARKER);
|
|
503
557
|
try {
|
|
504
|
-
|
|
505
|
-
|
|
558
|
+
// Accept exactly the marker states the async gate treats as settled. A
|
|
559
|
+
// `cleanup_pending` marker means the entries are fully installed and verified
|
|
560
|
+
// and only retirement of the legacy source is outstanding, so resolution is
|
|
561
|
+
// safe; rejecting it here made the sync resolver fail closed on a root the
|
|
562
|
+
// async gate had already completed.
|
|
563
|
+
if (!isSettledMigrationMarkerValue(fsSync.readFileSync(marker, "utf8")))
|
|
564
|
+
throw new Error("Unsafe local:// migration marker");
|
|
506
565
|
initializedLocalRoots.add(localRoot);
|
|
507
566
|
return;
|
|
508
567
|
} catch (error) {
|
|
@@ -519,7 +578,10 @@ function initializeLocalRootSyncWhenLegacyAbsent(options: LocalProtocolOptions,
|
|
|
519
578
|
fsSync.writeFileSync(marker, "absent\n", { mode: 0o600, flag: "wx" });
|
|
520
579
|
} catch (error) {
|
|
521
580
|
if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
|
|
522
|
-
|
|
581
|
+
// A concurrent initializer won the exclusive create. Any settled marker state
|
|
582
|
+
// it wrote is authoritative; only an unrecognized value is unsafe.
|
|
583
|
+
if (!isSettledMigrationMarkerValue(fsSync.readFileSync(marker, "utf8")))
|
|
584
|
+
throw new Error("Unsafe local:// migration marker");
|
|
523
585
|
}
|
|
524
586
|
initializedLocalRoots.add(localRoot);
|
|
525
587
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { isEnoent } from "@sayknow-cli/utils";
|
|
5
|
+
import type { GcCollectResult, GcContext, GcError, GcRecord, GcStoreAdapter } from "../skc-runtime/gc-runtime";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GC for `local://` session roots.
|
|
9
|
+
*
|
|
10
|
+
* `resolveLocalRoot` gives every session its own `<tmp>/skc-local/<session-id>`
|
|
11
|
+
* directory, and the first `local://` resolution seeds it with a migration
|
|
12
|
+
* marker. Nothing ever removes those directories, so a machine accumulates one
|
|
13
|
+
* per session indefinitely — and the overwhelming majority only ever hold the
|
|
14
|
+
* marker, because most sessions never write a `local://` file.
|
|
15
|
+
*
|
|
16
|
+
* Only marker-only directories are eligible, and only after a grace period.
|
|
17
|
+
* That keeps the rule provably safe without needing a session-liveness oracle:
|
|
18
|
+
* a directory holding real content is never touched, and a session that just
|
|
19
|
+
* started is still inside its grace window.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const LOCAL_ROOT_PARENT = "skc-local";
|
|
23
|
+
const LEGACY_MIGRATION_MARKER = ".skc-local-legacy-migrated-v1";
|
|
24
|
+
|
|
25
|
+
/** Directories younger than this are left alone so a live session is never raced. */
|
|
26
|
+
const GRACE_MS = 24 * 60 * 60 * 1000;
|
|
27
|
+
|
|
28
|
+
/** Bound the scan so a pathological directory cannot stall `skc gc`. */
|
|
29
|
+
const MAX_ENTRIES = 20_000;
|
|
30
|
+
|
|
31
|
+
function localRootParent(env: NodeJS.ProcessEnv): string {
|
|
32
|
+
return path.join(env.TMPDIR?.trim() || os.tmpdir(), LOCAL_ROOT_PARENT);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A root is prunable only when the marker is the single thing inside it. */
|
|
36
|
+
async function classify(dir: string, now: number): Promise<{ status: string; stale: boolean; reason: string } | null> {
|
|
37
|
+
let entries: string[];
|
|
38
|
+
try {
|
|
39
|
+
entries = await fs.readdir(dir);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (isEnoent(error)) return null;
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const extra = entries.filter(name => name !== LEGACY_MIGRATION_MARKER);
|
|
46
|
+
if (extra.length > 0)
|
|
47
|
+
return {
|
|
48
|
+
status: "in_use",
|
|
49
|
+
stale: false,
|
|
50
|
+
reason: `Holds ${extra.length} entr${extra.length === 1 ? "y" : "ies"} beyond the marker`,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const stat = await fs.stat(dir);
|
|
54
|
+
if (now - stat.mtimeMs < GRACE_MS) return { status: "recent", stale: false, reason: "Inside the grace window" };
|
|
55
|
+
return { status: "marker_only", stale: true, reason: "Only the migration marker, past the grace window" };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const localRootsGcAdapter: GcStoreAdapter = {
|
|
59
|
+
store: "local_roots",
|
|
60
|
+
async collect(ctx: GcContext): Promise<GcCollectResult> {
|
|
61
|
+
const records: GcRecord[] = [];
|
|
62
|
+
const errors: GcError[] = [];
|
|
63
|
+
const parent = localRootParent(ctx.env);
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
|
|
66
|
+
let dirents: string[];
|
|
67
|
+
try {
|
|
68
|
+
dirents = await fs.readdir(parent);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (isEnoent(error)) return { records, errors };
|
|
71
|
+
errors.push({ store: "local_roots", scope: parent, message: (error as Error).message });
|
|
72
|
+
return { records, errors };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const name of dirents.slice(0, MAX_ENTRIES)) {
|
|
76
|
+
const dir = path.join(parent, name);
|
|
77
|
+
try {
|
|
78
|
+
const classified = await classify(dir, now);
|
|
79
|
+
if (!classified) continue;
|
|
80
|
+
records.push({
|
|
81
|
+
store: "local_roots",
|
|
82
|
+
id: name,
|
|
83
|
+
path: dir,
|
|
84
|
+
status: classified.status,
|
|
85
|
+
stale: classified.stale,
|
|
86
|
+
removable: classified.stale,
|
|
87
|
+
action: classified.stale ? "would_remove" : "none",
|
|
88
|
+
reason: classified.reason,
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
errors.push({ store: "local_roots", scope: dir, message: (error as Error).message });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (dirents.length > MAX_ENTRIES) {
|
|
96
|
+
errors.push({
|
|
97
|
+
store: "local_roots",
|
|
98
|
+
scope: parent,
|
|
99
|
+
message: `Scan truncated at ${MAX_ENTRIES} entries`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { records, errors };
|
|
104
|
+
},
|
|
105
|
+
async prune(record: GcRecord) {
|
|
106
|
+
const dir = record.path;
|
|
107
|
+
if (!dir) return { removed: false, error: "Missing local root path" };
|
|
108
|
+
|
|
109
|
+
// Re-validate immediately before removing: the session may have written a
|
|
110
|
+
// file between collect and prune.
|
|
111
|
+
let classified: Awaited<ReturnType<typeof classify>>;
|
|
112
|
+
try {
|
|
113
|
+
classified = await classify(dir, Date.now());
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return { removed: false, error: (error as Error).message };
|
|
116
|
+
}
|
|
117
|
+
if (!classified) return { removed: false, skipped: "Local root disappeared" };
|
|
118
|
+
if (!classified.stale) return { removed: false, skipped: `Local root no longer eligible (${classified.status})` };
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
122
|
+
return { removed: true };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return { removed: false, error: (error as Error).message };
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
};
|
|
@@ -2,29 +2,91 @@ import { MCPManager } from "../runtime-mcp/manager";
|
|
|
2
2
|
import type { MCPResourceReadResult } from "../runtime-mcp/types";
|
|
3
3
|
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const MAX_MCP_RESOURCE_URI_CHARS = 16_384;
|
|
6
|
+
const MAX_MCP_URI_TEMPLATE_CHARS = 8_192;
|
|
7
|
+
const MAX_MCP_URI_TEMPLATE_EXPRESSIONS = 32;
|
|
8
|
+
|
|
9
|
+
interface CompiledUriTemplate {
|
|
10
|
+
literalSegments: string[];
|
|
11
|
+
literalChars: number;
|
|
12
|
+
expressionCount: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function containsExpressionLineTerminator(text: string, start: number, end: number): boolean {
|
|
16
|
+
for (let index = start; index < end; index++) {
|
|
17
|
+
const code = text.charCodeAt(index);
|
|
18
|
+
if (code === 0x0a || code === 0x0d || code === 0x2028 || code === 0x2029) return true;
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function compileUriTemplate(uriTemplate: string): CompiledUriTemplate | undefined {
|
|
24
|
+
if (uriTemplate.length > MAX_MCP_URI_TEMPLATE_CHARS) return undefined;
|
|
25
|
+
|
|
26
|
+
const literalSegments: string[] = [];
|
|
27
|
+
let expressionCount = 0;
|
|
28
|
+
let literalChars = 0;
|
|
29
|
+
let segmentStart = 0;
|
|
30
|
+
let scanPosition = 0;
|
|
31
|
+
while (scanPosition < uriTemplate.length) {
|
|
32
|
+
const expressionStart = uriTemplate.indexOf("{", scanPosition);
|
|
33
|
+
if (expressionStart < 0) break;
|
|
34
|
+
const expressionEnd = uriTemplate.indexOf("}", expressionStart + 1);
|
|
35
|
+
if (expressionEnd < 0) break;
|
|
36
|
+
if (expressionEnd === expressionStart + 1) {
|
|
37
|
+
scanPosition = expressionStart + 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
expressionCount++;
|
|
41
|
+
if (expressionCount > MAX_MCP_URI_TEMPLATE_EXPRESSIONS) return undefined;
|
|
42
|
+
const segment = uriTemplate.slice(segmentStart, expressionStart);
|
|
43
|
+
literalSegments.push(segment);
|
|
44
|
+
literalChars += segment.length;
|
|
45
|
+
segmentStart = expressionEnd + 1;
|
|
46
|
+
scanPosition = segmentStart;
|
|
47
|
+
}
|
|
48
|
+
const finalSegment = uriTemplate.slice(segmentStart);
|
|
49
|
+
literalSegments.push(finalSegment);
|
|
50
|
+
literalChars += finalSegment.length;
|
|
51
|
+
return { literalSegments, literalChars, expressionCount };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function matchesUriTemplate(uri: string, template: CompiledUriTemplate): boolean {
|
|
55
|
+
const { literalSegments, expressionCount } = template;
|
|
56
|
+
if (expressionCount === 0) return uri === literalSegments[0];
|
|
57
|
+
if (!uri.startsWith(literalSegments[0])) return false;
|
|
58
|
+
|
|
59
|
+
let cursor = literalSegments[0].length;
|
|
60
|
+
for (let index = 1; index < literalSegments.length - 1; index++) {
|
|
61
|
+
const segment = literalSegments[index];
|
|
62
|
+
const matchIndex = uri.indexOf(segment, cursor);
|
|
63
|
+
if (matchIndex < 0 || containsExpressionLineTerminator(uri, cursor, matchIndex)) return false;
|
|
64
|
+
cursor = matchIndex + segment.length;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const suffix = literalSegments.at(-1) ?? "";
|
|
68
|
+
const suffixStart = uri.length - suffix.length;
|
|
69
|
+
return suffixStart >= cursor && uri.endsWith(suffix) && !containsExpressionLineTerminator(uri, cursor, suffixStart);
|
|
7
70
|
}
|
|
8
71
|
|
|
9
72
|
function getUriTemplateMatchScore(
|
|
10
73
|
uri: string,
|
|
11
74
|
uriTemplate: string,
|
|
12
75
|
): { literalChars: number; expressionCount: number } | undefined {
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const pattern = literalSegments.map(escapeRegex).join("(.*?)");
|
|
17
|
-
const regex = new RegExp(`^${pattern}$`);
|
|
18
|
-
if (!regex.test(uri)) return undefined;
|
|
19
|
-
const literalChars = literalSegments.reduce((total, segment) => total + segment.length, 0);
|
|
20
|
-
return { literalChars, expressionCount };
|
|
76
|
+
const template = compileUriTemplate(uriTemplate);
|
|
77
|
+
if (!template || !matchesUriTemplate(uri, template)) return undefined;
|
|
78
|
+
return { literalChars: template.literalChars, expressionCount: template.expressionCount };
|
|
21
79
|
}
|
|
22
80
|
|
|
23
81
|
function extractResourceUri(url: InternalUrl): string {
|
|
24
82
|
const host = url.rawHost || url.hostname;
|
|
25
83
|
const rawPathname = url.rawPathname ?? url.pathname;
|
|
26
84
|
const hasPath = rawPathname && rawPathname !== "/";
|
|
27
|
-
const
|
|
85
|
+
const rawUri = `${host}${hasPath ? rawPathname : ""}${url.search}${url.hash}`;
|
|
86
|
+
if (rawUri.length > MAX_MCP_RESOURCE_URI_CHARS) {
|
|
87
|
+
throw new Error("MCP resource URI exceeds the 16384-character limit.");
|
|
88
|
+
}
|
|
89
|
+
const uri = rawUri.trim();
|
|
28
90
|
if (!uri) {
|
|
29
91
|
throw new Error("mcp:// URL requires a resource URI: mcp://<resource-uri>");
|
|
30
92
|
}
|