@sayknow-cli/coding-agent 0.4.7 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-contract.d.ts +40 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +156 -10
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +33 -2
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +5 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +13 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
- package/dist/types/runtime-mcp/manager.d.ts +9 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +52 -4
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/query/handlers.d.ts +14 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +9 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +83 -24
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
- package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +89 -18
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/skc-runtime/state-writer.d.ts +5 -1
- package/dist/types/skc-runtime/team-launch.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
- package/dist/types/skc-runtime/team-store.d.ts +12 -0
- package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +133 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +22 -9
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +7 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -12
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/compile-args.ts +0 -1
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-docs-index.ts +4 -1
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +58 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +16 -100
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +6 -38
- package/src/cli/read-cli.ts +2 -1
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +133 -11
- package/src/commands/daemon.ts +22 -5
- package/src/commands/deep-interview.ts +26 -1
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +134 -17
- package/src/commands/team.ts +1 -1
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-contract.ts +174 -0
- package/src/config/model-profiles.ts +51 -10
- package/src/config/model-registry.ts +24 -4
- package/src/config/model-resolver.ts +2 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +157 -10
- package/src/config/settings.ts +254 -108
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
- package/src/defaults/skc/skills/team/SKILL.md +1 -1
- package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/eval/py/executor.ts +37 -13
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/extensibility/shared-events.ts +5 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/internal-urls/docs-index.generated.ts +17 -16
- package/src/internal-urls/local-protocol.ts +87 -25
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +120 -42
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +52 -0
- package/src/modes/acp/acp-agent.ts +647 -83
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/sayknow-pet-widget.ts +20 -8
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +26 -8
- package/src/modes/components/welcome.ts +50 -23
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +101 -54
- package/src/modes/controllers/event-controller.ts +119 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +100 -8
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +4 -1
- package/src/modes/interactive-mode.ts +201 -60
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +99 -4
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +17 -3
- package/src/prompts/agents/critic.md +17 -2
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -3
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
- package/src/runtime-mcp/json-rpc.ts +28 -11
- package/src/runtime-mcp/manager.ts +77 -59
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +293 -43
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +22 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +10 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +793 -121
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +87 -25
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/prompt-reconciliation.ts +240 -0
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
- package/src/sdk/bus/telegram-daemon-control.ts +161 -40
- package/src/sdk/bus/telegram-daemon.ts +3649 -593
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +8 -2
- package/src/sdk/host/control/operations.ts +3 -2
- package/src/sdk/host/host.ts +27 -9
- package/src/sdk/host/query/handlers.ts +125 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +145 -0
- package/src/sdk/protocol/operation-registry.ts +24 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +69 -8
- package/src/sdk/startup-capability.ts +15 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +1112 -314
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +1426 -259
- package/src/session/internal/managed-session-storage.ts +414 -238
- package/src/session/internal/native-publish-outcome.ts +271 -0
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2079 -469
- package/src/session/session-storage.ts +151 -11
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +49 -5
- package/src/setup/provider-presets.json +2 -1
- package/src/skc-runtime/deep-interview-runtime.ts +303 -36
- package/src/skc-runtime/deep-interview-stage.ts +1016 -0
- package/src/skc-runtime/deep-interview-state.ts +8 -5
- package/src/skc-runtime/gc-render.ts +1 -0
- package/src/skc-runtime/gc-runtime.ts +11 -1
- package/src/skc-runtime/linux-proc.ts +70 -36
- package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/skc-runtime/ralplan-runtime.ts +1057 -75
- package/src/skc-runtime/repository-binding.ts +267 -0
- package/src/skc-runtime/session-state-sidecar.ts +25 -9
- package/src/skc-runtime/state-runtime.ts +50 -12
- package/src/skc-runtime/state-writer.ts +68 -56
- package/src/skc-runtime/team-launch.ts +34 -5
- package/src/skc-runtime/team-runtime.ts +41 -1
- package/src/skc-runtime/team-store.ts +32 -0
- package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/skc-runtime/team-workers.ts +1 -0
- package/src/skc-runtime/tmux-common.ts +13 -2
- package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
- package/src/skc-runtime/tmux-sessions.ts +45 -6
- package/src/skc-runtime/ultragoal-guard.ts +93 -15
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
- package/src/skc-runtime/ultragoal-runtime.ts +469 -38
- package/src/skc-runtime/workflow-manifest.generated.json +195 -5
- package/src/skc-runtime/workflow-manifest.ts +67 -3
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +104 -12
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/executor.ts +165 -47
- package/src/task/index.ts +123 -15
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/receipt.ts +3 -0
- package/src/task/render.ts +45 -26
- package/src/task/skc-command.ts +2 -2
- package/src/task/types.ts +55 -10
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +127 -154
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +7 -1
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1544 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authoritative repository/worktree binding for plans and delegated tasks (#2901).
|
|
3
|
+
*
|
|
4
|
+
* Multi-repo parent directories must not let QA/review lanes infer a sibling repo
|
|
5
|
+
* from prose. Bindings carry a validated worktree root + git common-dir identity
|
|
6
|
+
* so spawn/handoff can fail closed on mismatch.
|
|
7
|
+
*/
|
|
8
|
+
import * as fssync from "node:fs";
|
|
9
|
+
import * as fs from "node:fs/promises";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
import { type GitRepository, head, repo } from "../utils/git";
|
|
12
|
+
|
|
13
|
+
export const REPOSITORY_BINDING_SCHEMA = "skc.repository_binding.v1" as const;
|
|
14
|
+
|
|
15
|
+
export interface RepositoryBinding {
|
|
16
|
+
schema: typeof REPOSITORY_BINDING_SCHEMA;
|
|
17
|
+
/** Canonical realpath of the git worktree root (or resolved cwd root). */
|
|
18
|
+
worktreeRoot: string;
|
|
19
|
+
/** Git common dir realpath when available; null outside a git checkout. */
|
|
20
|
+
commonDir: string | null;
|
|
21
|
+
/** Optional repo-relative subdirectory the lane should operate under. */
|
|
22
|
+
relativeSubdir?: string;
|
|
23
|
+
/** Optional display path (may be non-canonical); never used for authority. */
|
|
24
|
+
displayPath?: string;
|
|
25
|
+
/** Optional baseline HEAD at capture time. */
|
|
26
|
+
head?: string;
|
|
27
|
+
/** Optional branch name at capture time (when not detached). */
|
|
28
|
+
branch?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type RepositoryBindingErrorCode =
|
|
32
|
+
| "not_a_repository"
|
|
33
|
+
| "identity_mismatch"
|
|
34
|
+
| "path_outside_root"
|
|
35
|
+
| "invalid_binding";
|
|
36
|
+
|
|
37
|
+
export class RepositoryBindingError extends Error {
|
|
38
|
+
readonly code: RepositoryBindingErrorCode;
|
|
39
|
+
|
|
40
|
+
constructor(code: RepositoryBindingErrorCode, message: string) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "RepositoryBindingError";
|
|
43
|
+
this.code = code;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function realpathOrResolve(target: string): Promise<string> {
|
|
48
|
+
try {
|
|
49
|
+
return await fs.realpath(target);
|
|
50
|
+
} catch {
|
|
51
|
+
return path.resolve(target);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Sync realpath for path-under-root checks (handles macOS /var → /private/var). */
|
|
56
|
+
function realpathSyncOrResolve(target: string): string {
|
|
57
|
+
try {
|
|
58
|
+
return fssync.realpathSync(target);
|
|
59
|
+
} catch {
|
|
60
|
+
return path.resolve(target);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
65
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
69
|
+
return typeof value === "string" && value.trim() !== "" ? value.trim() : undefined;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Capture a durable binding from the active cwd/worktree. */
|
|
73
|
+
export async function captureRepositoryBinding(
|
|
74
|
+
cwd: string,
|
|
75
|
+
options: { relativeSubdir?: string; displayPath?: string } = {},
|
|
76
|
+
): Promise<RepositoryBinding> {
|
|
77
|
+
const resolvedCwd = await realpathOrResolve(cwd);
|
|
78
|
+
const repository = await repo.resolve(resolvedCwd);
|
|
79
|
+
const worktreeRoot = repository ? await realpathOrResolve(repository.repoRoot) : resolvedCwd;
|
|
80
|
+
const commonDir = repository ? await realpathOrResolve(repository.commonDir) : null;
|
|
81
|
+
|
|
82
|
+
let headSha: string | undefined;
|
|
83
|
+
let branch: string | undefined;
|
|
84
|
+
if (repository) {
|
|
85
|
+
const headState = await head.resolve(resolvedCwd);
|
|
86
|
+
if (headState) {
|
|
87
|
+
headSha = headState.commit || undefined;
|
|
88
|
+
branch = headState.kind === "ref" ? headState.branchName || undefined : undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const binding: RepositoryBinding = {
|
|
93
|
+
schema: REPOSITORY_BINDING_SCHEMA,
|
|
94
|
+
worktreeRoot,
|
|
95
|
+
commonDir,
|
|
96
|
+
...(options.relativeSubdir ? { relativeSubdir: normalizeRelativeSubdir(options.relativeSubdir) } : {}),
|
|
97
|
+
...(options.displayPath ? { displayPath: options.displayPath } : {}),
|
|
98
|
+
...(headSha ? { head: headSha } : {}),
|
|
99
|
+
...(branch ? { branch } : {}),
|
|
100
|
+
};
|
|
101
|
+
return binding;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeRelativeSubdir(relative: string): string {
|
|
105
|
+
const normalized = relative
|
|
106
|
+
.replaceAll("\\", "/")
|
|
107
|
+
.replace(/^\.\/+/u, "")
|
|
108
|
+
.replace(/\/+$/u, "");
|
|
109
|
+
if (normalized === "" || normalized === ".") {
|
|
110
|
+
throw new RepositoryBindingError("invalid_binding", "relativeSubdir must be a non-empty relative path");
|
|
111
|
+
}
|
|
112
|
+
if (path.isAbsolute(normalized) || normalized.split("/").includes("..")) {
|
|
113
|
+
throw new RepositoryBindingError("invalid_binding", "relativeSubdir must be repo-relative without '..' segments");
|
|
114
|
+
}
|
|
115
|
+
return normalized;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Parse a binding from JSON/plan payload; fail closed on malformed shapes. */
|
|
119
|
+
export function parseRepositoryBinding(value: unknown): RepositoryBinding {
|
|
120
|
+
if (!isObject(value)) {
|
|
121
|
+
throw new RepositoryBindingError("invalid_binding", "repository binding must be an object");
|
|
122
|
+
}
|
|
123
|
+
const schema = nonEmptyString(value.schema);
|
|
124
|
+
if (schema !== REPOSITORY_BINDING_SCHEMA) {
|
|
125
|
+
throw new RepositoryBindingError(
|
|
126
|
+
"invalid_binding",
|
|
127
|
+
`repository binding schema must be ${REPOSITORY_BINDING_SCHEMA}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
const worktreeRoot = nonEmptyString(value.worktreeRoot ?? value.worktree_root);
|
|
131
|
+
if (!worktreeRoot) {
|
|
132
|
+
throw new RepositoryBindingError("invalid_binding", "repository binding requires worktreeRoot");
|
|
133
|
+
}
|
|
134
|
+
const commonDirRaw = value.commonDir ?? value.common_dir;
|
|
135
|
+
const commonDir =
|
|
136
|
+
commonDirRaw === null || commonDirRaw === undefined
|
|
137
|
+
? null
|
|
138
|
+
: (nonEmptyString(commonDirRaw) ??
|
|
139
|
+
(() => {
|
|
140
|
+
throw new RepositoryBindingError("invalid_binding", "commonDir must be a string or null");
|
|
141
|
+
})());
|
|
142
|
+
const relativeSubdirRaw = value.relativeSubdir ?? value.relative_subdir;
|
|
143
|
+
const relativeSubdir =
|
|
144
|
+
relativeSubdirRaw === undefined ? undefined : normalizeRelativeSubdir(String(relativeSubdirRaw));
|
|
145
|
+
const displayPath = nonEmptyString(value.displayPath ?? value.display_path);
|
|
146
|
+
const head = nonEmptyString(value.head);
|
|
147
|
+
const branch = nonEmptyString(value.branch);
|
|
148
|
+
return {
|
|
149
|
+
schema: REPOSITORY_BINDING_SCHEMA,
|
|
150
|
+
worktreeRoot: path.resolve(worktreeRoot),
|
|
151
|
+
commonDir: commonDir === null ? null : path.resolve(commonDir),
|
|
152
|
+
...(relativeSubdir ? { relativeSubdir } : {}),
|
|
153
|
+
...(displayPath ? { displayPath } : {}),
|
|
154
|
+
...(head ? { head } : {}),
|
|
155
|
+
...(branch ? { branch } : {}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** True when two bindings refer to the same repository identity (linked worktrees ok). */
|
|
160
|
+
export function repositoryBindingsMatch(left: RepositoryBinding, right: RepositoryBinding): boolean {
|
|
161
|
+
if (left.commonDir && right.commonDir) {
|
|
162
|
+
return path.resolve(left.commonDir) === path.resolve(right.commonDir);
|
|
163
|
+
}
|
|
164
|
+
// Non-git workspaces: require exact worktree root match.
|
|
165
|
+
return path.resolve(left.worktreeRoot) === path.resolve(right.worktreeRoot);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Ensure `cwd` is inside the bound repository (or the same linked worktree family).
|
|
170
|
+
* Fails closed on sibling-repo drift.
|
|
171
|
+
*/
|
|
172
|
+
export async function assertCwdMatchesRepositoryBinding(
|
|
173
|
+
cwd: string,
|
|
174
|
+
binding: RepositoryBinding,
|
|
175
|
+
): Promise<RepositoryBinding> {
|
|
176
|
+
const active = await captureRepositoryBinding(cwd);
|
|
177
|
+
if (!repositoryBindingsMatch(active, binding)) {
|
|
178
|
+
throw new RepositoryBindingError(
|
|
179
|
+
"identity_mismatch",
|
|
180
|
+
`Active worktree does not match plan/task repository binding. active=${active.worktreeRoot} (commonDir=${active.commonDir ?? "none"}) bound=${binding.worktreeRoot} (commonDir=${binding.commonDir ?? "none"}).`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return active;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Ensure a declared target path resolves under the bound worktree root. */
|
|
187
|
+
export function assertPathUnderRepositoryBinding(binding: RepositoryBinding, targetPath: string): string {
|
|
188
|
+
const root = realpathSyncOrResolve(binding.worktreeRoot);
|
|
189
|
+
const base = binding.relativeSubdir ? realpathSyncOrResolve(path.resolve(root, binding.relativeSubdir)) : root;
|
|
190
|
+
const candidate = path.isAbsolute(targetPath) ? path.resolve(targetPath) : path.resolve(base, targetPath);
|
|
191
|
+
// Prefer realpath when the path exists so macOS /var ↔ /private/var aliases match.
|
|
192
|
+
const resolved = realpathSyncOrResolve(candidate);
|
|
193
|
+
const relative = path.relative(root, resolved);
|
|
194
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
195
|
+
throw new RepositoryBindingError(
|
|
196
|
+
"path_outside_root",
|
|
197
|
+
`Path escapes bound repository root: ${targetPath} (root=${root})`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return resolved;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Public identity snapshot for receipts/handoffs (no display-only fields required).
|
|
205
|
+
* Always includes the schema + durable roots so downstream lanes can re-verify.
|
|
206
|
+
*/
|
|
207
|
+
export function publicRepositoryBinding(binding: RepositoryBinding): RepositoryBinding {
|
|
208
|
+
return {
|
|
209
|
+
schema: REPOSITORY_BINDING_SCHEMA,
|
|
210
|
+
worktreeRoot: path.resolve(binding.worktreeRoot),
|
|
211
|
+
commonDir: binding.commonDir === null ? null : path.resolve(binding.commonDir),
|
|
212
|
+
...(binding.relativeSubdir ? { relativeSubdir: binding.relativeSubdir } : {}),
|
|
213
|
+
...(binding.displayPath ? { displayPath: binding.displayPath } : {}),
|
|
214
|
+
...(binding.head ? { head: binding.head } : {}),
|
|
215
|
+
...(binding.branch ? { branch: binding.branch } : {}),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Resolve the authoritative binding for a delegated task before discovery/spawn.
|
|
221
|
+
*
|
|
222
|
+
* - Missing declaration → stamp from session cwd (never leave authority implicit).
|
|
223
|
+
* - Declared binding → parse + fail closed unless it matches the active session worktree.
|
|
224
|
+
* - relativeSubdir (when present) must resolve under the bound root.
|
|
225
|
+
*/
|
|
226
|
+
export async function resolveTaskRepositoryBinding(
|
|
227
|
+
sessionCwd: string,
|
|
228
|
+
declared: unknown | undefined,
|
|
229
|
+
): Promise<RepositoryBinding> {
|
|
230
|
+
const sessionBinding = await captureRepositoryBinding(sessionCwd, { displayPath: sessionCwd });
|
|
231
|
+
if (declared === undefined || declared === null) {
|
|
232
|
+
return publicRepositoryBinding(sessionBinding);
|
|
233
|
+
}
|
|
234
|
+
const taskBinding = parseRepositoryBinding(declared);
|
|
235
|
+
await assertCwdMatchesRepositoryBinding(sessionCwd, taskBinding);
|
|
236
|
+
if (taskBinding.relativeSubdir) {
|
|
237
|
+
assertPathUnderRepositoryBinding(taskBinding, ".");
|
|
238
|
+
}
|
|
239
|
+
return publicRepositoryBinding(taskBinding);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Ensure an execution/isolation root (cwd or worktree) still matches the bound identity.
|
|
244
|
+
* Used after isolation workspace creation so linked worktrees keep the source repository.
|
|
245
|
+
*/
|
|
246
|
+
export async function assertExecutionRootMatchesRepositoryBinding(
|
|
247
|
+
executionRoot: string,
|
|
248
|
+
binding: RepositoryBinding,
|
|
249
|
+
): Promise<RepositoryBinding> {
|
|
250
|
+
return await assertCwdMatchesRepositoryBinding(executionRoot, binding);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Optional helper for tests and diagnostics. */
|
|
254
|
+
export function bindingFromGitRepository(
|
|
255
|
+
repository: GitRepository,
|
|
256
|
+
options: { relativeSubdir?: string; displayPath?: string; head?: string; branch?: string } = {},
|
|
257
|
+
): RepositoryBinding {
|
|
258
|
+
return {
|
|
259
|
+
schema: REPOSITORY_BINDING_SCHEMA,
|
|
260
|
+
worktreeRoot: path.resolve(repository.repoRoot),
|
|
261
|
+
commonDir: path.resolve(repository.commonDir),
|
|
262
|
+
...(options.relativeSubdir ? { relativeSubdir: normalizeRelativeSubdir(options.relativeSubdir) } : {}),
|
|
263
|
+
...(options.displayPath ? { displayPath: options.displayPath } : {}),
|
|
264
|
+
...(options.head ? { head: options.head } : {}),
|
|
265
|
+
...(options.branch ? { branch: options.branch } : {}),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
@@ -911,15 +911,11 @@ async function operatorDispatchIdForOwner(
|
|
|
911
911
|
}
|
|
912
912
|
}
|
|
913
913
|
|
|
914
|
-
async function
|
|
914
|
+
async function observeOwnerTerminalPostmortem(
|
|
915
915
|
reason: postmortem.Reason,
|
|
916
|
-
|
|
917
|
-
stateFile: string,
|
|
916
|
+
owner: OwnerTerminalContext,
|
|
918
917
|
sessionId: string,
|
|
919
|
-
|
|
920
|
-
): Promise<void> {
|
|
921
|
-
const owner = context.ownerTerminal;
|
|
922
|
-
if (!owner) return;
|
|
918
|
+
): Promise<OwnerVerdict | null> {
|
|
923
919
|
try {
|
|
924
920
|
const now = new Date().toISOString();
|
|
925
921
|
const observation: Omit<ObserveTerminalRequest, "operator_dispatch_id"> = {
|
|
@@ -937,10 +933,27 @@ async function persistCoordinatorRuntimeStateFromOwnerTerminalPostmortem(
|
|
|
937
933
|
reason: "process_postmortem",
|
|
938
934
|
};
|
|
939
935
|
const operatorDispatchId = await operatorDispatchIdForOwner(owner, observation);
|
|
940
|
-
|
|
936
|
+
return await observeOwnerTerminal({
|
|
941
937
|
...observation,
|
|
942
938
|
...(operatorDispatchId ? { operator_dispatch_id: operatorDispatchId } : {}),
|
|
943
939
|
});
|
|
940
|
+
} catch {
|
|
941
|
+
return null;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
async function persistCoordinatorRuntimeStateFromOwnerTerminalPostmortem(
|
|
946
|
+
context: RuntimeStateContext,
|
|
947
|
+
stateFile: string,
|
|
948
|
+
sessionId: string,
|
|
949
|
+
previous: Record<string, unknown>,
|
|
950
|
+
verdict: OwnerVerdict | null,
|
|
951
|
+
): Promise<void> {
|
|
952
|
+
const owner = context.ownerTerminal;
|
|
953
|
+
if (!owner) return;
|
|
954
|
+
try {
|
|
955
|
+
if (!verdict) throw new Error("owner terminal verdict unavailable");
|
|
956
|
+
const now = new Date().toISOString();
|
|
944
957
|
const expected = verdict.classification === "expected_operator_shutdown";
|
|
945
958
|
const state: RuntimeState = expected ? "completed" : "errored";
|
|
946
959
|
const payload = {
|
|
@@ -1009,6 +1022,9 @@ export async function persistCoordinatorRuntimeStateFromPostmortem(
|
|
|
1009
1022
|
const stateFile = runtimeStateFileForContext(context);
|
|
1010
1023
|
if (!stateFile) return;
|
|
1011
1024
|
const identity = normalizedIdentity(context);
|
|
1025
|
+
const ownerTerminalVerdict = context.ownerTerminal
|
|
1026
|
+
? await observeOwnerTerminalPostmortem(reason, context.ownerTerminal, identity.sessionId)
|
|
1027
|
+
: null;
|
|
1012
1028
|
await serializeStateFileWrite(
|
|
1013
1029
|
stateFile,
|
|
1014
1030
|
async () =>
|
|
@@ -1033,11 +1049,11 @@ export async function persistCoordinatorRuntimeStateFromPostmortem(
|
|
|
1033
1049
|
}
|
|
1034
1050
|
if (context.ownerTerminal) {
|
|
1035
1051
|
await persistCoordinatorRuntimeStateFromOwnerTerminalPostmortem(
|
|
1036
|
-
reason,
|
|
1037
1052
|
context,
|
|
1038
1053
|
stateFile,
|
|
1039
1054
|
identity.sessionId,
|
|
1040
1055
|
previous,
|
|
1056
|
+
ownerTerminalVerdict,
|
|
1041
1057
|
);
|
|
1042
1058
|
return;
|
|
1043
1059
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
// Subpath import keeps this module native-free for the skc-state-gates shards:
|
|
5
|
+
// the package barrel pulls procmgr/ptree → @sayknow-cli/natives.
|
|
6
|
+
import * as logger from "@sayknow-cli/utils/logger";
|
|
4
7
|
import type { WorkflowHudSummary } from "../skill-state/active-state";
|
|
5
8
|
import {
|
|
6
9
|
applyHandoffToActiveState,
|
|
@@ -265,7 +268,21 @@ async function describeStaleClearState(
|
|
|
265
268
|
return undefined;
|
|
266
269
|
}
|
|
267
270
|
|
|
268
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Route a workflow-state warning through the TUI-safe centralized file logger
|
|
273
|
+
* (console transport off by default) so interactive sessions never paint raw
|
|
274
|
+
* bytes into the alternate-screen stream (#3002). CLI command handlers may also
|
|
275
|
+
* collect the warning via an `onWarning` sink to surface it on the structured
|
|
276
|
+
* {@link StateCommandResult.stderr} channel, so `skc state` automation still
|
|
277
|
+
* distinguishes corrupt state from absent state.
|
|
278
|
+
*/
|
|
279
|
+
function emitStateWarning(warning: string, context?: Record<string, unknown>): void {
|
|
280
|
+
logger.warn(warning, context);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
type StateWarningSink = (warning: string) => void;
|
|
284
|
+
|
|
285
|
+
async function readJsonFile(filePath: string, onWarning?: StateWarningSink): Promise<Record<string, unknown> | null> {
|
|
269
286
|
try {
|
|
270
287
|
const raw = await fs.readFile(filePath, "utf-8");
|
|
271
288
|
const parsed = JSON.parse(raw);
|
|
@@ -276,18 +293,22 @@ async function readJsonFile(filePath: string): Promise<Record<string, unknown> |
|
|
|
276
293
|
} catch (error) {
|
|
277
294
|
const err = error as NodeJS.ErrnoException;
|
|
278
295
|
if (err.code === "ENOENT") return null;
|
|
279
|
-
|
|
296
|
+
const warning = `WARNING: failed to read ${filePath}; ignoring corrupt state: ${err.message}`;
|
|
297
|
+
emitStateWarning(warning, { filePath, error: err.message });
|
|
298
|
+
onWarning?.(warning);
|
|
280
299
|
return null;
|
|
281
300
|
}
|
|
282
301
|
}
|
|
283
302
|
|
|
284
|
-
async function readJsonValue(filePath: string): Promise<unknown | null> {
|
|
303
|
+
async function readJsonValue(filePath: string, onWarning?: StateWarningSink): Promise<unknown | null> {
|
|
285
304
|
try {
|
|
286
305
|
return JSON.parse(await fs.readFile(filePath, "utf-8"));
|
|
287
306
|
} catch (error) {
|
|
288
307
|
const err = error as NodeJS.ErrnoException;
|
|
289
308
|
if (err.code === "ENOENT") return null;
|
|
290
|
-
|
|
309
|
+
const warning = `WARNING: failed to read ${filePath}; ignoring corrupt state: ${err.message}`;
|
|
310
|
+
emitStateWarning(warning, { filePath, error: err.message });
|
|
311
|
+
onWarning?.(warning);
|
|
291
312
|
return null;
|
|
292
313
|
}
|
|
293
314
|
}
|
|
@@ -836,7 +857,8 @@ function buildHudForMode(
|
|
|
836
857
|
: typeof payload.mode === "string"
|
|
837
858
|
? (payload.mode as string)
|
|
838
859
|
: undefined;
|
|
839
|
-
const
|
|
860
|
+
const rawVerdict = payload.last_review_verdict ?? payload.verdict;
|
|
861
|
+
const verdict = typeof rawVerdict === "string" ? rawVerdict : undefined;
|
|
840
862
|
const iteration = typeof payload.iteration === "number" ? (payload.iteration as number) : undefined;
|
|
841
863
|
const pendingApproval = payload.pending_approval === true || stage === "final";
|
|
842
864
|
return buildRalplanHudSummary({
|
|
@@ -1101,21 +1123,30 @@ export async function readWorkflowStateJson(
|
|
|
1101
1123
|
cwd: string,
|
|
1102
1124
|
skill: CanonicalSkcWorkflowSkill,
|
|
1103
1125
|
sessionId?: string,
|
|
1126
|
+
onWarning?: StateWarningSink,
|
|
1104
1127
|
): Promise<Record<string, unknown>> {
|
|
1105
1128
|
const session = await resolveSkcSessionForRead(cwd, {
|
|
1106
1129
|
payloadSessionId: sessionId,
|
|
1107
1130
|
envSessionId: process.env.SKC_SESSION_ID,
|
|
1108
1131
|
});
|
|
1109
|
-
return (await readJsonFile(modeStateFile(cwd, skill, session.skcSessionId))) ?? {};
|
|
1132
|
+
return (await readJsonFile(modeStateFile(cwd, skill, session.skcSessionId), onWarning)) ?? {};
|
|
1110
1133
|
}
|
|
1111
1134
|
|
|
1112
1135
|
async function handleRead(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
1113
1136
|
const selectors = await resolveSelectors(args, cwd, "read");
|
|
1114
1137
|
const mode = selectors.mode ?? (await inferModeFromActiveState(cwd, selectors.skcSessionId));
|
|
1115
1138
|
const fields = parseFieldsFlag(args);
|
|
1139
|
+
// Corrupt-state warnings are TUI-safe file-logged inside the readers; the CLI
|
|
1140
|
+
// path also surfaces them on the command result so `skc state read`
|
|
1141
|
+
// automation can tell corrupt state from absent state (#3002).
|
|
1142
|
+
const warnings: string[] = [];
|
|
1143
|
+
const warningStderr = (): Pick<StateCommandResult, "stderr"> =>
|
|
1144
|
+
warnings.length ? { stderr: warnings.map(warning => `${warning}\n`).join("") } : {};
|
|
1116
1145
|
if (mode) {
|
|
1117
1146
|
const filePath = modeStateFile(cwd, mode, selectors.skcSessionId);
|
|
1118
|
-
const existing = await readWorkflowStateJson(cwd, mode, selectors.skcSessionId
|
|
1147
|
+
const existing = await readWorkflowStateJson(cwd, mode, selectors.skcSessionId, warning =>
|
|
1148
|
+
warnings.push(warning),
|
|
1149
|
+
);
|
|
1119
1150
|
const envelope = { skill: mode, state: existing, storage_path: filePath };
|
|
1120
1151
|
const manifest = getSkillManifest(mode);
|
|
1121
1152
|
if (fields) {
|
|
@@ -1125,6 +1156,7 @@ async function handleRead(args: readonly string[], cwd: string): Promise<StateCo
|
|
|
1125
1156
|
stdout: hasFlag(args, "--json")
|
|
1126
1157
|
? `${JSON.stringify(projected, null, 2)}\n`
|
|
1127
1158
|
: renderStateMarkdown(mode, projected, manifest),
|
|
1159
|
+
...warningStderr(),
|
|
1128
1160
|
};
|
|
1129
1161
|
}
|
|
1130
1162
|
if (hasFlag(args, "--compact")) {
|
|
@@ -1134,6 +1166,7 @@ async function handleRead(args: readonly string[], cwd: string): Promise<StateCo
|
|
|
1134
1166
|
stdout: hasFlag(args, "--json")
|
|
1135
1167
|
? `${JSON.stringify(compact, null, 2)}\n`
|
|
1136
1168
|
: renderStateMarkdown(mode, envelope, manifest),
|
|
1169
|
+
...warningStderr(),
|
|
1137
1170
|
};
|
|
1138
1171
|
}
|
|
1139
1172
|
return {
|
|
@@ -1141,12 +1174,13 @@ async function handleRead(args: readonly string[], cwd: string): Promise<StateCo
|
|
|
1141
1174
|
stdout: hasFlag(args, "--json")
|
|
1142
1175
|
? `${JSON.stringify(envelope, null, 2)}\n`
|
|
1143
1176
|
: renderStateMarkdown(mode, envelope, manifest),
|
|
1177
|
+
...warningStderr(),
|
|
1144
1178
|
};
|
|
1145
1179
|
}
|
|
1146
1180
|
const filePath = activeStateFile(cwd, selectors.skcSessionId);
|
|
1147
|
-
const existingRaw = await readJsonValue(filePath);
|
|
1181
|
+
const existingRaw = await readJsonValue(filePath, warning => warnings.push(warning));
|
|
1148
1182
|
const existing = isPlainObject(existingRaw) ? existingRaw : null;
|
|
1149
|
-
return { status: 0, stdout: `${JSON.stringify(existing ?? {}, null, 2)}\n
|
|
1183
|
+
return { status: 0, stdout: `${JSON.stringify(existing ?? {}, null, 2)}\n`, ...warningStderr() };
|
|
1150
1184
|
}
|
|
1151
1185
|
|
|
1152
1186
|
async function handleStatus(args: readonly string[], cwd: string): Promise<StateCommandResult> {
|
|
@@ -1159,7 +1193,8 @@ async function handleStatus(args: readonly string[], cwd: string): Promise<State
|
|
|
1159
1193
|
);
|
|
1160
1194
|
}
|
|
1161
1195
|
const filePath = modeStateFile(cwd, mode, selectors.skcSessionId);
|
|
1162
|
-
const
|
|
1196
|
+
const warnings: string[] = [];
|
|
1197
|
+
const existing = await readWorkflowStateJson(cwd, mode, selectors.skcSessionId, warning => warnings.push(warning));
|
|
1163
1198
|
const summary = buildStateStatusSummary(
|
|
1164
1199
|
mode,
|
|
1165
1200
|
{ skill: mode, state: existing, storage_path: filePath },
|
|
@@ -1169,6 +1204,7 @@ async function handleStatus(args: readonly string[], cwd: string): Promise<State
|
|
|
1169
1204
|
return {
|
|
1170
1205
|
status: 0,
|
|
1171
1206
|
stdout: hasFlag(args, "--json") ? `${JSON.stringify(summary, null, 2)}\n` : renderStateStatusLine(summary),
|
|
1207
|
+
...(warnings.length ? { stderr: warnings.map(warning => `${warning}\n`).join("") } : {}),
|
|
1172
1208
|
};
|
|
1173
1209
|
}
|
|
1174
1210
|
|
|
@@ -1603,7 +1639,7 @@ async function handleHandoffUnlocked(args: readonly string[], cwd: string): Prom
|
|
|
1603
1639
|
toPhase: "handoff",
|
|
1604
1640
|
});
|
|
1605
1641
|
await updateWorkflowTransactionJournal(cwd, sessionId, mutationId, { steps: ["caller-mode-state"] });
|
|
1606
|
-
if (callerWrite.warning)
|
|
1642
|
+
if (callerWrite.warning) emitStateWarning(callerWrite.warning);
|
|
1607
1643
|
const stampedCallerReceipt = isPlainObject(callerWrite.stamped.receipt) ? callerWrite.stamped.receipt : {};
|
|
1608
1644
|
await syncSkillActiveState({
|
|
1609
1645
|
cwd,
|
|
@@ -1646,6 +1682,7 @@ async function handleHandoffUnlocked(args: readonly string[], cwd: string): Prom
|
|
|
1646
1682
|
active_state: activeStateFile(cwd, sessionId),
|
|
1647
1683
|
},
|
|
1648
1684
|
}),
|
|
1685
|
+
...(callerWrite.warning ? { stderr: `${callerWrite.warning}\n` } : {}),
|
|
1649
1686
|
};
|
|
1650
1687
|
}
|
|
1651
1688
|
|
|
@@ -1746,7 +1783,7 @@ async function handleHandoffUnlocked(args: readonly string[], cwd: string): Prom
|
|
|
1746
1783
|
);
|
|
1747
1784
|
const stampedCallerReceipt = isPlainObject(callerWrite.stamped.receipt) ? callerWrite.stamped.receipt : {};
|
|
1748
1785
|
const stampedCalleeReceipt = isPlainObject(calleeWrite.stamped.receipt) ? calleeWrite.stamped.receipt : {};
|
|
1749
|
-
for (const warning of warnings)
|
|
1786
|
+
for (const warning of warnings) emitStateWarning(warning);
|
|
1750
1787
|
if (process.env.SKC_STATE_HANDOFF_FAIL_AFTER_CALLER === mutationId) {
|
|
1751
1788
|
throw new StateCommandError(1, `injected handoff failure after caller write for ${mutationId}`);
|
|
1752
1789
|
}
|
|
@@ -1818,6 +1855,7 @@ async function handleHandoffUnlocked(args: readonly string[], cwd: string): Prom
|
|
|
1818
1855
|
active_state: activeStateFile(cwd, sessionId),
|
|
1819
1856
|
},
|
|
1820
1857
|
}),
|
|
1858
|
+
...(warnings.length ? { stderr: warnings.map(warning => `${warning}\n`).join("") } : {}),
|
|
1821
1859
|
};
|
|
1822
1860
|
}
|
|
1823
1861
|
|