@sayknow-cli/coding-agent 0.4.6 → 0.5.0
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 +112 -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 +27 -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
package/src/task/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ import * as os from "node:os";
|
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@sayknow-cli/agent-core";
|
|
19
19
|
import type { Model, Usage } from "@sayknow-cli/ai";
|
|
20
|
-
import { $
|
|
20
|
+
import { $pickenv, prompt, Snowflake } from "@sayknow-cli/utils";
|
|
21
21
|
import type { ToolSession } from "..";
|
|
22
22
|
import { AsyncJobManager, OwnerSubagentShutdownError, type ResumeRunner } from "../async";
|
|
23
23
|
import { resolveAgentModelPatterns } from "../config/model-resolver";
|
|
@@ -44,6 +44,15 @@ import {
|
|
|
44
44
|
// Import review tools for side effects (registers subagent tool handlers)
|
|
45
45
|
import "../tools/review";
|
|
46
46
|
import { initializeLocalRoot, type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
47
|
+
import {
|
|
48
|
+
assertExecutionRootMatchesRepositoryBinding,
|
|
49
|
+
assertPathUnderRepositoryBinding,
|
|
50
|
+
captureRepositoryBinding,
|
|
51
|
+
publicRepositoryBinding,
|
|
52
|
+
type RepositoryBinding,
|
|
53
|
+
RepositoryBindingError,
|
|
54
|
+
resolveTaskRepositoryBinding,
|
|
55
|
+
} from "../skc-runtime/repository-binding";
|
|
47
56
|
import { generateCommitMessage } from "../utils/commit-message-generator";
|
|
48
57
|
import * as git from "../utils/git";
|
|
49
58
|
import { discoverAgents, filterVisibleAgents, getAgent } from "./discovery";
|
|
@@ -128,6 +137,43 @@ function addUsageTotals(target: Usage, usage: Partial<Usage>): void {
|
|
|
128
137
|
target.cost.total += cost.total;
|
|
129
138
|
}
|
|
130
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Stamp/resolve repository authority for every task before agent discovery or spawn.
|
|
142
|
+
* Omitted bindings inherit the session worktree; declared ones must match it.
|
|
143
|
+
*/
|
|
144
|
+
async function resolveTaskItemsWithRepositoryBindings(
|
|
145
|
+
cwd: string,
|
|
146
|
+
tasks: readonly TaskItem[],
|
|
147
|
+
): Promise<{ tasks: TaskItem[]; error?: string }> {
|
|
148
|
+
const resolved: TaskItem[] = [];
|
|
149
|
+
for (const task of tasks) {
|
|
150
|
+
try {
|
|
151
|
+
const binding = await resolveTaskRepositoryBinding(cwd, task.repositoryBinding);
|
|
152
|
+
if (binding.relativeSubdir) {
|
|
153
|
+
assertPathUnderRepositoryBinding(binding, ".");
|
|
154
|
+
}
|
|
155
|
+
resolved.push({
|
|
156
|
+
...task,
|
|
157
|
+
repositoryBinding: binding,
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
const id = task.id?.trim() ? task.id : "(missing-id)";
|
|
161
|
+
if (error instanceof RepositoryBindingError) {
|
|
162
|
+
return { tasks: [], error: `Task "${id}" repository binding rejected: ${error.message}` };
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
tasks: [],
|
|
166
|
+
error: `Task "${id}" repository binding rejected: ${error instanceof Error ? error.message : String(error)}`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return { tasks: resolved };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function repositoryBindingFromTask(task: TaskItem): RepositoryBinding | undefined {
|
|
174
|
+
return task.repositoryBinding ? publicRepositoryBinding(task.repositoryBinding as RepositoryBinding) : undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
131
177
|
function validateTaskIdsForScheduling(tasks: readonly TaskItem[]): string | undefined {
|
|
132
178
|
const invalid: string[] = [];
|
|
133
179
|
for (let i = 0; i < tasks.length; i++) {
|
|
@@ -399,12 +445,16 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
399
445
|
this.session.getSessionSpawns() ?? "*",
|
|
400
446
|
);
|
|
401
447
|
}
|
|
448
|
+
readonly #sessionRepositoryBinding: RepositoryBinding;
|
|
449
|
+
|
|
402
450
|
private constructor(
|
|
403
451
|
private readonly session: ToolSession,
|
|
404
452
|
discoveredAgents: AgentDefinition[],
|
|
453
|
+
sessionRepositoryBinding: RepositoryBinding,
|
|
405
454
|
) {
|
|
406
|
-
this.#blockedAgent = $
|
|
455
|
+
this.#blockedAgent = $pickenv("SKC_BLOCKED_AGENT", "PI_BLOCKED_AGENT");
|
|
407
456
|
this.#discoveredAgents = discoveredAgents;
|
|
457
|
+
this.#sessionRepositoryBinding = sessionRepositoryBinding;
|
|
408
458
|
}
|
|
409
459
|
|
|
410
460
|
#getTaskSimpleMode(): TaskSimpleMode {
|
|
@@ -412,11 +462,16 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
412
462
|
}
|
|
413
463
|
|
|
414
464
|
/**
|
|
415
|
-
* Create a TaskTool instance
|
|
465
|
+
* Create a TaskTool instance.
|
|
466
|
+
* Repository authority is captured from session cwd *before* agent discovery so
|
|
467
|
+
* multi-repo workspaces fail closed prior to context/discovery (#2901).
|
|
416
468
|
*/
|
|
417
469
|
static async create(session: ToolSession): Promise<TaskTool> {
|
|
470
|
+
const sessionRepositoryBinding = await captureRepositoryBinding(session.cwd, { displayPath: session.cwd });
|
|
471
|
+
// Authority check before discovery: session cwd must resolve to a stable binding.
|
|
472
|
+
await assertExecutionRootMatchesRepositoryBinding(session.cwd, sessionRepositoryBinding);
|
|
418
473
|
const { agents } = await discoverAgents(session.cwd);
|
|
419
|
-
return new TaskTool(session, agents);
|
|
474
|
+
return new TaskTool(session, agents, publicRepositoryBinding(sessionRepositoryBinding));
|
|
420
475
|
}
|
|
421
476
|
|
|
422
477
|
async execute(
|
|
@@ -432,13 +487,32 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
432
487
|
return createTaskModeError(validationError);
|
|
433
488
|
}
|
|
434
489
|
|
|
435
|
-
|
|
436
|
-
|
|
490
|
+
// Re-verify session authority before using any discovered agents or scheduling work.
|
|
491
|
+
try {
|
|
492
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, this.#sessionRepositoryBinding);
|
|
493
|
+
} catch (error) {
|
|
494
|
+
const message =
|
|
495
|
+
error instanceof RepositoryBindingError
|
|
496
|
+
? error.message
|
|
497
|
+
: error instanceof Error
|
|
498
|
+
? error.message
|
|
499
|
+
: String(error);
|
|
500
|
+
return createTaskModeError(`Session repository binding rejected before task discovery: ${message}`);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const rawTaskItems = params.tasks ?? [];
|
|
504
|
+
const taskIdValidationError = validateTaskIdsForScheduling(rawTaskItems);
|
|
437
505
|
if (taskIdValidationError) {
|
|
438
506
|
return createTaskModeError(taskIdValidationError);
|
|
439
507
|
}
|
|
508
|
+
const bindingResolution = await resolveTaskItemsWithRepositoryBindings(this.session.cwd, rawTaskItems);
|
|
509
|
+
if (bindingResolution.error) {
|
|
510
|
+
return createTaskModeError(bindingResolution.error);
|
|
511
|
+
}
|
|
512
|
+
const taskItems = bindingResolution.tasks;
|
|
513
|
+
const paramsWithBindings: TaskParams = { ...params, tasks: taskItems };
|
|
440
514
|
if (taskItems.length === 0) {
|
|
441
|
-
return this.#executeSync(_toolCallId,
|
|
515
|
+
return this.#executeSync(_toolCallId, paramsWithBindings, signal, onUpdate);
|
|
442
516
|
}
|
|
443
517
|
const agent = getAgent(this.#discoveredAgents, params.agent);
|
|
444
518
|
if (!agent) {
|
|
@@ -926,13 +1000,31 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
926
1000
|
},
|
|
927
1001
|
): Promise<AgentToolResult<TaskToolDetails>> {
|
|
928
1002
|
const startTime = Date.now();
|
|
1003
|
+
// Pre-discovery authority: session cwd must still match the capture from create().
|
|
1004
|
+
try {
|
|
1005
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, this.#sessionRepositoryBinding);
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
const message =
|
|
1008
|
+
error instanceof RepositoryBindingError
|
|
1009
|
+
? error.message
|
|
1010
|
+
: error instanceof Error
|
|
1011
|
+
? error.message
|
|
1012
|
+
: String(error);
|
|
1013
|
+
return createTaskModeError(`Session repository binding rejected before task discovery: ${message}`);
|
|
1014
|
+
}
|
|
1015
|
+
const bindingResolution = await resolveTaskItemsWithRepositoryBindings(this.session.cwd, params.tasks ?? []);
|
|
1016
|
+
if (bindingResolution.error) {
|
|
1017
|
+
return createTaskModeError(bindingResolution.error);
|
|
1018
|
+
}
|
|
1019
|
+
const boundParams: TaskParams = { ...params, tasks: bindingResolution.tasks };
|
|
1020
|
+
|
|
929
1021
|
const { agents, projectAgentsDir } = await discoverAgents(this.session.cwd);
|
|
930
|
-
const { agent: agentName, context, schema: outputSchema } =
|
|
1022
|
+
const { agent: agentName, context, schema: outputSchema } = boundParams;
|
|
931
1023
|
const simpleMode = this.#getTaskSimpleMode();
|
|
932
1024
|
const { contextEnabled, customSchemaEnabled } = getTaskSimpleModeCapabilities(simpleMode);
|
|
933
1025
|
const sharedContext = contextEnabled ? context?.trim() : undefined;
|
|
934
1026
|
const isolationMode = this.session.settings.get("task.isolation.mode");
|
|
935
|
-
const isolationRequested = "isolated" in
|
|
1027
|
+
const isolationRequested = "isolated" in boundParams ? boundParams.isolated === true : false;
|
|
936
1028
|
const isIsolated = isolationMode !== "none" && isolationRequested;
|
|
937
1029
|
const mergeMode = this.session.settings.get("task.isolation.merge");
|
|
938
1030
|
const commitStyle = this.session.settings.get("task.isolation.commits");
|
|
@@ -940,7 +1032,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
940
1032
|
const taskDepth = this.session.taskDepth ?? 0;
|
|
941
1033
|
const subagentLspEnabled = (this.session.enableLsp ?? true) && this.session.settings.get("task.enableLsp");
|
|
942
1034
|
|
|
943
|
-
if (isolationMode === "none" && "isolated" in
|
|
1035
|
+
if (isolationMode === "none" && "isolated" in boundParams) {
|
|
944
1036
|
return {
|
|
945
1037
|
content: [
|
|
946
1038
|
{
|
|
@@ -1000,7 +1092,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1000
1092
|
}
|
|
1001
1093
|
|
|
1002
1094
|
const forkContextValidationError = validateForkContextRequests(
|
|
1003
|
-
|
|
1095
|
+
boundParams.tasks ?? [],
|
|
1004
1096
|
agent,
|
|
1005
1097
|
this.session.settings.get("task.forkContext.enabled"),
|
|
1006
1098
|
);
|
|
@@ -1039,7 +1131,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1039
1131
|
: (effectiveAgent.output ?? this.session.outputSchema);
|
|
1040
1132
|
|
|
1041
1133
|
// Handle empty or missing tasks
|
|
1042
|
-
if (!
|
|
1134
|
+
if (!boundParams.tasks || boundParams.tasks.length === 0) {
|
|
1043
1135
|
return {
|
|
1044
1136
|
content: [
|
|
1045
1137
|
{
|
|
@@ -1057,7 +1149,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1057
1149
|
};
|
|
1058
1150
|
}
|
|
1059
1151
|
|
|
1060
|
-
const tasks =
|
|
1152
|
+
const tasks = boundParams.tasks;
|
|
1061
1153
|
const missingTaskIndexes: number[] = [];
|
|
1062
1154
|
const idIndexes = new Map<string, number[]>();
|
|
1063
1155
|
|
|
@@ -1173,7 +1265,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1173
1265
|
const emitProgress = () => {
|
|
1174
1266
|
const progress = Array.from(progressMap.values()).sort((a, b) => a.index - b.index);
|
|
1175
1267
|
onUpdate?.({
|
|
1176
|
-
content: [{ type: "text", text: `Running ${
|
|
1268
|
+
content: [{ type: "text", text: `Running ${tasks.length} agents...` }],
|
|
1177
1269
|
details: {
|
|
1178
1270
|
projectAgentsDir,
|
|
1179
1271
|
results: [],
|
|
@@ -1342,12 +1434,19 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1342
1434
|
const taskSessionFile = managedPersistence
|
|
1343
1435
|
? null
|
|
1344
1436
|
: (overrides?.sessionFile ?? executionOverrides?.sessionFiles?.get(task.id) ?? null);
|
|
1437
|
+
const taskRepositoryBinding = repositoryBindingFromTask(task) ?? this.#sessionRepositoryBinding;
|
|
1438
|
+
// Declared paths (relativeSubdir) must stay under the bound root before spawn.
|
|
1439
|
+
if (taskRepositoryBinding.relativeSubdir) {
|
|
1440
|
+
assertPathUnderRepositoryBinding(taskRepositoryBinding, ".");
|
|
1441
|
+
}
|
|
1345
1442
|
if (!isIsolated) {
|
|
1443
|
+
await assertExecutionRootMatchesRepositoryBinding(this.session.cwd, taskRepositoryBinding);
|
|
1346
1444
|
const result = await runSubprocess({
|
|
1347
1445
|
cwd: this.session.cwd,
|
|
1348
1446
|
agent: effectiveAgent,
|
|
1349
1447
|
task: renderTaskAssignment(task.assignment, simpleMode),
|
|
1350
1448
|
assignment: task.assignment.trim(),
|
|
1449
|
+
executionMode: task.executionMode,
|
|
1351
1450
|
context: sharedContext,
|
|
1352
1451
|
description: task.description,
|
|
1353
1452
|
index,
|
|
@@ -1393,7 +1492,12 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1393
1492
|
parentTelemetry: this.session.getTelemetry?.(),
|
|
1394
1493
|
forkContextSeed,
|
|
1395
1494
|
});
|
|
1396
|
-
return {
|
|
1495
|
+
return {
|
|
1496
|
+
...result,
|
|
1497
|
+
...(forkContext ? { forkContext } : {}),
|
|
1498
|
+
forkContextAdvisory,
|
|
1499
|
+
repositoryBinding: publicRepositoryBinding(taskRepositoryBinding),
|
|
1500
|
+
};
|
|
1397
1501
|
}
|
|
1398
1502
|
|
|
1399
1503
|
const taskStart = Date.now();
|
|
@@ -1406,6 +1510,8 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1406
1510
|
|
|
1407
1511
|
isolationHandle = await ensureIsolation(repoRoot, task.id, preferredIsolationBackend);
|
|
1408
1512
|
const isolationDir = isolationHandle.mergedDir;
|
|
1513
|
+
// Isolated worktrees must preserve the source repository identity (#2901).
|
|
1514
|
+
await assertExecutionRootMatchesRepositoryBinding(isolationDir, taskRepositoryBinding);
|
|
1409
1515
|
|
|
1410
1516
|
const result = await runSubprocess({
|
|
1411
1517
|
cwd: this.session.cwd,
|
|
@@ -1413,6 +1519,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1413
1519
|
agent: effectiveAgent,
|
|
1414
1520
|
task: renderTaskAssignment(task.assignment, simpleMode),
|
|
1415
1521
|
assignment: task.assignment.trim(),
|
|
1522
|
+
executionMode: task.executionMode,
|
|
1416
1523
|
context: sharedContext,
|
|
1417
1524
|
description: task.description,
|
|
1418
1525
|
index,
|
|
@@ -1462,6 +1569,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1462
1569
|
...result,
|
|
1463
1570
|
...(forkContext ? { forkContext } : {}),
|
|
1464
1571
|
forkContextAdvisory,
|
|
1572
|
+
repositoryBinding: publicRepositoryBinding(taskRepositoryBinding),
|
|
1465
1573
|
};
|
|
1466
1574
|
if (mergeMode === "branch" && resultWithForkContext.exitCode === 0) {
|
|
1467
1575
|
try {
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { AgentProgress, TaskToolDetails } from "./types";
|
|
2
|
+
|
|
3
|
+
export type ProviderRetryKind = NonNullable<AgentProgress["retryState"]>["kind"];
|
|
4
|
+
|
|
5
|
+
const FIRST_EVENT_TIMEOUT_PATTERN = /stream timed out while waiting for the first event/i;
|
|
6
|
+
const IDLE_STREAM_STALL_PATTERN = /stream stalled while waiting for the next event/i;
|
|
7
|
+
|
|
8
|
+
export function classifyProviderRetry(errorMessage: string): ProviderRetryKind {
|
|
9
|
+
if (FIRST_EVENT_TIMEOUT_PATTERN.test(errorMessage)) return "first_event_timeout";
|
|
10
|
+
if (IDLE_STREAM_STALL_PATTERN.test(errorMessage)) return "idle_stream_stall";
|
|
11
|
+
return "provider_error";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function providerNameFromModel(model: string | undefined): string | undefined {
|
|
15
|
+
const provider = model?.split("/", 1)[0]?.trim();
|
|
16
|
+
return provider || undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function providerRetryPhaseLabel(kind: ProviderRetryKind): string {
|
|
20
|
+
switch (kind) {
|
|
21
|
+
case "first_event_timeout":
|
|
22
|
+
return "first event timeout";
|
|
23
|
+
case "idle_stream_stall":
|
|
24
|
+
return "stream stalled";
|
|
25
|
+
case "provider_error":
|
|
26
|
+
return "provider error";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function providerProgressAgeLabel(retryState: NonNullable<AgentProgress["retryState"]>, nowMs: number): string {
|
|
31
|
+
if (retryState.lastProviderProgressAtMs === undefined) return "no provider events yet";
|
|
32
|
+
const ageSeconds = Math.max(0, Math.floor((nowMs - retryState.lastProviderProgressAtMs) / 1000));
|
|
33
|
+
return `last provider progress ${ageSeconds}s ago`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
37
|
+
return Boolean(value) && typeof value === "object";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isTaskToolDetails(value: unknown): value is TaskToolDetails {
|
|
41
|
+
return (
|
|
42
|
+
isRecord(value) && Array.isArray(value.results) && (value.progress === undefined || Array.isArray(value.progress))
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isAgentProgress(value: unknown): value is AgentProgress {
|
|
47
|
+
return isRecord(value) && typeof value.status === "string";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function hasActiveProviderRetryInTaskDetails(details: TaskToolDetails, seen = new WeakSet<object>()): boolean {
|
|
51
|
+
if (!isTaskToolDetails(details) || seen.has(details)) return false;
|
|
52
|
+
seen.add(details);
|
|
53
|
+
for (const progress of details.progress ?? []) {
|
|
54
|
+
if (isAgentProgress(progress) && hasActiveProviderRetryInProgress(progress, seen)) return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function hasActiveProviderRetryInProgress(progress: AgentProgress, seen = new WeakSet<object>()): boolean {
|
|
60
|
+
if (!isAgentProgress(progress) || seen.has(progress)) return false;
|
|
61
|
+
seen.add(progress);
|
|
62
|
+
if (progress.status === "running" && progress.retryState) return true;
|
|
63
|
+
|
|
64
|
+
const nestedTaskData = isRecord(progress.extractedToolData) ? progress.extractedToolData.task : undefined;
|
|
65
|
+
if (Array.isArray(nestedTaskData)) {
|
|
66
|
+
for (const nestedDetails of nestedTaskData) {
|
|
67
|
+
if (hasActiveProviderRetryInTaskDetails(nestedDetails as TaskToolDetails, seen)) return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return hasActiveProviderRetryInTaskDetails(progress.inflightTaskDetails as TaskToolDetails, seen);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ProviderDegradationGroup {
|
|
74
|
+
provider: string;
|
|
75
|
+
count: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function collectProviderDegradationGroups(progress: readonly AgentProgress[]): ProviderDegradationGroup[] {
|
|
79
|
+
const counts = new Map<string, number>();
|
|
80
|
+
for (const item of progress) {
|
|
81
|
+
if (!isAgentProgress(item) || item.status !== "running" || !item.retryState) continue;
|
|
82
|
+
const provider = item.retryState.provider ?? "provider";
|
|
83
|
+
counts.set(provider, (counts.get(provider) ?? 0) + 1);
|
|
84
|
+
}
|
|
85
|
+
return Array.from(counts, ([provider, count]) => ({ provider, count }))
|
|
86
|
+
.filter(group => group.count > 1)
|
|
87
|
+
.sort((a, b) => b.count - a.count || a.provider.localeCompare(b.provider));
|
|
88
|
+
}
|
package/src/task/receipt.ts
CHANGED
|
@@ -48,6 +48,8 @@ export interface TaskResultReceipt {
|
|
|
48
48
|
extractedToolCounts?: Record<string, number>;
|
|
49
49
|
forkContext?: SingleResult["forkContext"];
|
|
50
50
|
forkContextAdvisory?: SingleResult["forkContextAdvisory"];
|
|
51
|
+
/** Resolved repository identity for this delegated lane (#2901). */
|
|
52
|
+
repositoryBinding?: SingleResult["repositoryBinding"];
|
|
51
53
|
roi?: TaskRoi;
|
|
52
54
|
}
|
|
53
55
|
|
|
@@ -262,6 +264,7 @@ export function buildTaskReceipt(raw: SingleResult): TaskResultReceipt {
|
|
|
262
264
|
extractedToolCounts,
|
|
263
265
|
forkContext: raw.forkContext,
|
|
264
266
|
forkContextAdvisory: raw.forkContextAdvisory,
|
|
267
|
+
repositoryBinding: raw.repositoryBinding,
|
|
265
268
|
roi: buildTaskRoi(raw),
|
|
266
269
|
};
|
|
267
270
|
}
|
package/src/task/render.ts
CHANGED
|
@@ -28,6 +28,12 @@ import {
|
|
|
28
28
|
type SubmitReviewDetails,
|
|
29
29
|
} from "../tools/review";
|
|
30
30
|
import { Ellipsis, Hasher, type RenderCache, renderStatusLine } from "../tui";
|
|
31
|
+
import {
|
|
32
|
+
collectProviderDegradationGroups,
|
|
33
|
+
hasActiveProviderRetryInTaskDetails,
|
|
34
|
+
providerProgressAgeLabel,
|
|
35
|
+
providerRetryPhaseLabel,
|
|
36
|
+
} from "./provider-retry-status";
|
|
31
37
|
import type { TaskResultReceipt } from "./receipt";
|
|
32
38
|
import { subprocessToolRegistry } from "./subprocess-tool-registry";
|
|
33
39
|
import type { AgentProgress, TaskParams, TaskToolDetails } from "./types";
|
|
@@ -123,6 +129,15 @@ function formatModelSubstitutionWarning(warning: { requested: string; effective:
|
|
|
123
129
|
return `Requested model substituted: ${warning.requested} -> ${warning.effective}`;
|
|
124
130
|
}
|
|
125
131
|
|
|
132
|
+
function renderProviderDegradationNotices(progress: readonly AgentProgress[], theme: Theme): string[] {
|
|
133
|
+
return collectProviderDegradationGroups(progress).map(group =>
|
|
134
|
+
theme.fg(
|
|
135
|
+
"warning",
|
|
136
|
+
truncateToWidth(replaceTabs(`provider degraded: ${group.count} subagents retrying on ${group.provider}`), 100),
|
|
137
|
+
),
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
126
141
|
function formatJsonScalar(value: unknown, _theme: Theme): string {
|
|
127
142
|
if (value === null) return "null";
|
|
128
143
|
if (typeof value === "string") {
|
|
@@ -548,14 +563,11 @@ function renderAgentProgress(
|
|
|
548
563
|
const titlePart = description ? `${theme.bold(displayId)}: ${description}` : displayId;
|
|
549
564
|
let statusLine = `${prefix} ${theme.fg(iconColor, icon)} ${theme.fg("accent", titlePart)}`;
|
|
550
565
|
|
|
551
|
-
//
|
|
552
|
-
// is sleeping on a provider 429, not silently progressing. Wins over the
|
|
553
|
-
// generic running spinner because "we're waiting on a quota window" is
|
|
554
|
-
// the operationally meaningful state.
|
|
566
|
+
// A provider recovery loop is operationally distinct from normal agent work.
|
|
555
567
|
if (progress.retryState && progress.status === "running") {
|
|
556
|
-
statusLine += ` ${formatBadge("
|
|
568
|
+
statusLine += ` ${formatBadge("provider degraded", "warning", theme)}`;
|
|
557
569
|
} else if (progress.retryFailure && (progress.status === "failed" || progress.status === "aborted")) {
|
|
558
|
-
statusLine += ` ${formatBadge("
|
|
570
|
+
statusLine += ` ${formatBadge("provider failed", "error", theme)}`;
|
|
559
571
|
} else if (progress.status === "failed" || progress.status === "aborted") {
|
|
560
572
|
const statusLabel = progress.status === "failed" ? "failed" : "aborted";
|
|
561
573
|
statusLine += ` ${formatBadge(statusLabel, iconColor, theme)}`;
|
|
@@ -615,8 +627,10 @@ function renderAgentProgress(
|
|
|
615
627
|
// keep spinning while a child sleeps on a 3-hour provider rate-limit.
|
|
616
628
|
if (progress.retryState && progress.status === "running") {
|
|
617
629
|
const attemptLabel = progress.retryState.unbounded
|
|
618
|
-
? `attempt ${progress.retryState.attempt}`
|
|
619
|
-
:
|
|
630
|
+
? `attempt ${progress.retryState.attempt}, unbounded`
|
|
631
|
+
: `attempt ${progress.retryState.attempt} of ${progress.retryState.maxAttempts}, bounded`;
|
|
632
|
+
const phaseLabel = providerRetryPhaseLabel(progress.retryState.kind);
|
|
633
|
+
const progressAge = staticTime ? "" : ` · ${providerProgressAgeLabel(progress.retryState, Date.now())}`;
|
|
620
634
|
// `staticTime` omits the wall-clock countdown so a cached await body stays a
|
|
621
635
|
// pure function of its key (the producer already drops time-only churn).
|
|
622
636
|
let waitLabel = "";
|
|
@@ -624,7 +638,7 @@ function renderAgentProgress(
|
|
|
624
638
|
const remainingMs = Math.max(0, progress.retryState.startedAtMs + progress.retryState.delayMs - Date.now());
|
|
625
639
|
waitLabel = remainingMs > 0 ? ` in ${formatDuration(remainingMs)}` : " now";
|
|
626
640
|
}
|
|
627
|
-
const summary =
|
|
641
|
+
const summary = `${phaseLabel} · retrying ${attemptLabel}${waitLabel}${progressAge}: ${truncateToWidth(replaceTabs(progress.retryState.errorMessage), 60)}`;
|
|
628
642
|
lines.push(`${continuePrefix}${theme.tree.hook} ${theme.fg("warning", summary)}`);
|
|
629
643
|
} else if (progress.retryFailure && progress.status !== "running") {
|
|
630
644
|
const summary = `auto-retry gave up after ${progress.retryFailure.attempt} attempt${
|
|
@@ -953,23 +967,28 @@ export function renderResult(
|
|
|
953
967
|
.u32(spinnerFrame ?? 0)
|
|
954
968
|
.u32(width)
|
|
955
969
|
.digest();
|
|
956
|
-
|
|
970
|
+
const hasDynamicRetry = hasActiveProviderRetryInTaskDetails(details);
|
|
971
|
+
if (!hasDynamicRetry && cached?.key === key) return cached.lines;
|
|
957
972
|
|
|
958
973
|
const lines: string[] = [];
|
|
959
974
|
|
|
960
|
-
const
|
|
961
|
-
|
|
975
|
+
const hasProgress = Boolean(details.progress && details.progress.length > 0);
|
|
976
|
+
const shouldRenderProgress = hasProgress && (isPartial || details.results.length === 0);
|
|
977
|
+
const hasResults = details.results.length > 0;
|
|
978
|
+
if (hasResults) {
|
|
979
|
+
details.results.forEach((res, i) => {
|
|
980
|
+
const isLast = !shouldRenderProgress && i === details.results.length - 1;
|
|
981
|
+
lines.push(...renderAgentResult(res, isLast, expanded, theme));
|
|
982
|
+
});
|
|
983
|
+
}
|
|
962
984
|
if (shouldRenderProgress && details.progress) {
|
|
985
|
+
lines.push(...renderProviderDegradationNotices(details.progress, theme));
|
|
963
986
|
details.progress.forEach((progress, i) => {
|
|
964
987
|
const isLast = i === details.progress!.length - 1;
|
|
965
988
|
lines.push(...renderAgentProgress(progress, isLast, expanded, theme, spinnerFrame));
|
|
966
989
|
});
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const isLast = i === details.results.length - 1;
|
|
970
|
-
lines.push(...renderAgentResult(res, isLast, expanded, theme));
|
|
971
|
-
});
|
|
972
|
-
|
|
990
|
+
}
|
|
991
|
+
if (hasResults && !shouldRenderProgress) {
|
|
973
992
|
const abortedCount = details.results.filter(r => r.status === "aborted").length;
|
|
974
993
|
const mergeFailedCount = details.results.filter(r => r.status === "merge_failed").length;
|
|
975
994
|
const successCount = details.results.filter(r => r.status === "completed").length;
|
|
@@ -997,7 +1016,7 @@ export function renderResult(
|
|
|
997
1016
|
if (lines.length === 0) {
|
|
998
1017
|
const text = fallbackText.trim() ? fallbackText : "No results";
|
|
999
1018
|
const result = [theme.fg("dim", truncateToWidth(text, width))];
|
|
1000
|
-
cached = { key, lines: result };
|
|
1019
|
+
if (!hasDynamicRetry) cached = { key, lines: result };
|
|
1001
1020
|
return result;
|
|
1002
1021
|
}
|
|
1003
1022
|
|
|
@@ -1019,9 +1038,9 @@ export function renderResult(
|
|
|
1019
1038
|
}
|
|
1020
1039
|
|
|
1021
1040
|
const indented = lines.map(line =>
|
|
1022
|
-
line.length > 0 ? truncateToWidth(` ${line}`, width, Ellipsis.Omit) : "",
|
|
1041
|
+
line.length > 0 ? truncateToWidth(` ${replaceTabs(line)}`, width, Ellipsis.Omit) : "",
|
|
1023
1042
|
);
|
|
1024
|
-
cached = { key, lines: indented };
|
|
1043
|
+
if (!hasDynamicRetry) cached = { key, lines: indented };
|
|
1025
1044
|
return indented;
|
|
1026
1045
|
},
|
|
1027
1046
|
invalidate() {
|
|
@@ -1065,16 +1084,16 @@ function renderNestedTaskTree(
|
|
|
1065
1084
|
): string[] {
|
|
1066
1085
|
const lines: string[] = [];
|
|
1067
1086
|
for (const details of detailsList) {
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1087
|
+
const inflight = details.progress;
|
|
1088
|
+
const hasInflight = Boolean(inflight && inflight.length > 0);
|
|
1089
|
+
if (details.results && details.results.length > 0) {
|
|
1070
1090
|
details.results.forEach((result, index) => {
|
|
1071
|
-
const isLast = index === details.results.length - 1;
|
|
1091
|
+
const isLast = !hasInflight && index === details.results.length - 1;
|
|
1072
1092
|
lines.push(...renderAgentResult(result, isLast, expanded, theme));
|
|
1073
1093
|
});
|
|
1074
|
-
continue;
|
|
1075
1094
|
}
|
|
1076
|
-
const inflight = details.progress;
|
|
1077
1095
|
if (inflight && inflight.length > 0) {
|
|
1096
|
+
lines.push(...renderProviderDegradationNotices(inflight, theme));
|
|
1078
1097
|
inflight.forEach((prog, index) => {
|
|
1079
1098
|
const isLast = index === inflight.length - 1;
|
|
1080
1099
|
lines.push(...renderAgentProgress(prog, isLast, expanded, theme, spinnerFrame, staticTime));
|
package/src/task/skc-command.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
|
|
3
|
-
import { $
|
|
3
|
+
import { $pickenv } from "@sayknow-cli/utils";
|
|
4
4
|
|
|
5
5
|
interface SkcCommand {
|
|
6
6
|
cmd: string;
|
|
@@ -12,7 +12,7 @@ const DEFAULT_CMD = process.platform === "win32" ? "skc.cmd" : "skc";
|
|
|
12
12
|
const DEFAULT_SHELL = process.platform === "win32";
|
|
13
13
|
|
|
14
14
|
export function resolveSkcCommand(): SkcCommand {
|
|
15
|
-
const envCmd = $
|
|
15
|
+
const envCmd = $pickenv("SKC_SUBPROCESS_CMD", "PI_SUBPROCESS_CMD");
|
|
16
16
|
if (envCmd?.trim()) {
|
|
17
17
|
return { cmd: envCmd, args: [], shell: DEFAULT_SHELL };
|
|
18
18
|
}
|
package/src/task/types.ts
CHANGED
|
@@ -14,23 +14,28 @@ export type AgentSource = "bundled" | "user" | "project";
|
|
|
14
14
|
export type ForkContextPolicy = "forbidden" | "allowed";
|
|
15
15
|
export type ForkContextMode = "none" | "receipt" | "last-turn" | "bounded" | "full";
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
} catch {}
|
|
17
|
+
const parsePositiveIntegerEnvironment = (keys: string[], defaultValue: number): number => {
|
|
18
|
+
for (const key of keys) {
|
|
19
|
+
const value = $env[key];
|
|
20
|
+
if (!value || value.trim().length === 0) continue;
|
|
21
|
+
if (!/^\d+$/.test(value)) return defaultValue;
|
|
22
|
+
const number = Number(value);
|
|
23
|
+
return Number.isSafeInteger(number) && number > 0 ? number : defaultValue;
|
|
25
24
|
}
|
|
26
25
|
return defaultValue;
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
/** Maximum output bytes per agent */
|
|
30
|
-
export const MAX_OUTPUT_BYTES =
|
|
29
|
+
export const MAX_OUTPUT_BYTES = parsePositiveIntegerEnvironment(
|
|
30
|
+
["SKC_TASK_MAX_OUTPUT_BYTES", "PI_TASK_MAX_OUTPUT_BYTES"],
|
|
31
|
+
500_000,
|
|
32
|
+
);
|
|
31
33
|
|
|
32
34
|
/** Maximum output lines per agent */
|
|
33
|
-
export const MAX_OUTPUT_LINES =
|
|
35
|
+
export const MAX_OUTPUT_LINES = parsePositiveIntegerEnvironment(
|
|
36
|
+
["SKC_TASK_MAX_OUTPUT_LINES", "PI_TASK_MAX_OUTPUT_LINES"],
|
|
37
|
+
5000,
|
|
38
|
+
);
|
|
34
39
|
|
|
35
40
|
/** EventBus channel for raw subagent events */
|
|
36
41
|
export const TASK_SUBAGENT_EVENT_CHANNEL = "task:subagent:event";
|
|
@@ -74,17 +79,41 @@ const spawnPlanSchema = z
|
|
|
74
79
|
})
|
|
75
80
|
.describe("justification required before spawning more than four tasks");
|
|
76
81
|
|
|
82
|
+
const repositoryBindingSchema = z
|
|
83
|
+
.object({
|
|
84
|
+
schema: z.literal("skc.repository_binding.v1"),
|
|
85
|
+
worktreeRoot: z.string().min(1).describe("canonical git worktree root"),
|
|
86
|
+
commonDir: z.string().min(1).nullable().describe("git common dir, or null outside a git checkout"),
|
|
87
|
+
relativeSubdir: z.string().min(1).optional().describe("optional repo-relative subdirectory; not an absolute cwd"),
|
|
88
|
+
displayPath: z.string().min(1).optional().describe("human-facing path; never used for authority"),
|
|
89
|
+
head: z.string().min(1).optional(),
|
|
90
|
+
branch: z.string().min(1).optional(),
|
|
91
|
+
})
|
|
92
|
+
.strict()
|
|
93
|
+
.describe("authoritative repository identity for multi-repo fail-closed spawn");
|
|
94
|
+
|
|
77
95
|
const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
78
96
|
z.object({
|
|
79
97
|
id: z.string().max(48).refine(isValidTaskId, TASK_ID_DESCRIPTION).describe("filesystem-safe task identifier"),
|
|
80
98
|
description: z.string().describe("ui label, not seen by subagent"),
|
|
81
99
|
assignment: z.string().describe(assignmentDescription),
|
|
100
|
+
executionMode: z
|
|
101
|
+
.enum(["default", "ultragoal-red-team"])
|
|
102
|
+
.optional()
|
|
103
|
+
.describe(
|
|
104
|
+
"typed executor mode: default keeps ordinary executor behavior; ultragoal-red-team injects the Ultragoal QA/red-team prompt fragment. Prefer this over free-form assignment text (#2698).",
|
|
105
|
+
),
|
|
82
106
|
inheritContext: z
|
|
83
107
|
.enum(["none", "receipt", "last-turn", "bounded", "full"])
|
|
84
108
|
.optional()
|
|
85
109
|
.describe(
|
|
86
110
|
"fork-context mode: none/omitted copies no parent context; receipt copies a minimal receipt-sized snapshot; last-turn copies only the latest exchange; bounded copies the bounded default snapshot; full copies a larger sanitized snapshot up to the configured/model token cap",
|
|
87
111
|
),
|
|
112
|
+
repositoryBinding: repositoryBindingSchema
|
|
113
|
+
.optional()
|
|
114
|
+
.describe(
|
|
115
|
+
"authoritative repository identity; omitted items are stamped from session cwd before discovery/spawn and still fail closed on sibling drift",
|
|
116
|
+
),
|
|
88
117
|
});
|
|
89
118
|
|
|
90
119
|
/** Single task item for parallel execution (default shape with context enabled). */
|
|
@@ -267,6 +296,9 @@ export interface AgentProgress {
|
|
|
267
296
|
attempt: number;
|
|
268
297
|
maxAttempts: number;
|
|
269
298
|
unbounded?: boolean;
|
|
299
|
+
kind: "first_event_timeout" | "idle_stream_stall" | "provider_error";
|
|
300
|
+
provider?: string;
|
|
301
|
+
lastProviderProgressAtMs?: number;
|
|
270
302
|
delayMs: number;
|
|
271
303
|
errorMessage: string;
|
|
272
304
|
startedAtMs: number;
|
|
@@ -353,6 +385,19 @@ export interface SingleResult {
|
|
|
353
385
|
* never changes the actual mode selection).
|
|
354
386
|
*/
|
|
355
387
|
forkContextAdvisory?: { recommendedMode: ForkContextMode; reasons: string[] };
|
|
388
|
+
/**
|
|
389
|
+
* Resolved repository identity used for this task after pre-discovery stamping
|
|
390
|
+
* and fail-closed validation (#2901).
|
|
391
|
+
*/
|
|
392
|
+
repositoryBinding?: {
|
|
393
|
+
schema: "skc.repository_binding.v1";
|
|
394
|
+
worktreeRoot: string;
|
|
395
|
+
commonDir: string | null;
|
|
396
|
+
relativeSubdir?: string;
|
|
397
|
+
displayPath?: string;
|
|
398
|
+
head?: string;
|
|
399
|
+
branch?: string;
|
|
400
|
+
};
|
|
356
401
|
}
|
|
357
402
|
|
|
358
403
|
/** True only for complete, factual five-bucket cost accounting. */
|