@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
|
@@ -5,7 +5,15 @@ import * as fs from "node:fs/promises";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import type { NativeExactUnlinkResult } from "@sayknow-cli/natives";
|
|
7
7
|
import * as native from "@sayknow-cli/natives";
|
|
8
|
-
import { resolveEquivalentPath } from "@sayknow-cli/utils";
|
|
8
|
+
import { $credentialEnv, resolveEquivalentPath } from "@sayknow-cli/utils";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
isModelProfileError,
|
|
12
|
+
type ModelProfileErrorDetails,
|
|
13
|
+
validateModelProfileName,
|
|
14
|
+
} from "../../config/model-profile-contract";
|
|
15
|
+
import { mergeModelProfiles } from "../../config/model-profiles";
|
|
16
|
+
import { ModelsConfigFile } from "../../config/model-registry";
|
|
9
17
|
import { validateManagedArtifactTree } from "../../session/internal/managed-session-storage";
|
|
10
18
|
import {
|
|
11
19
|
FileSessionStorage,
|
|
@@ -21,6 +29,7 @@ import {
|
|
|
21
29
|
planLaunchWorktree,
|
|
22
30
|
type SkcLaunchWorktreePlan,
|
|
23
31
|
} from "../../skc-runtime/launch-worktree";
|
|
32
|
+
import type { SessionLifecycleMcpServer } from "../acp/mcp";
|
|
24
33
|
import { SdkClient, SdkClientError } from "../client/client";
|
|
25
34
|
import {
|
|
26
35
|
type LogicalSessionCandidate,
|
|
@@ -207,6 +216,7 @@ export interface SessionLifecycleLaunchRequest {
|
|
|
207
216
|
/** Broker-issued effect marker which the child echoes only after host readiness. */
|
|
208
217
|
effectMarker?: string;
|
|
209
218
|
modelPreset?: string;
|
|
219
|
+
mcpServers?: SessionLifecycleMcpServer[];
|
|
210
220
|
worktree?: SessionLifecycleWorktreeTarget;
|
|
211
221
|
receivedAt: number;
|
|
212
222
|
requestedReadinessTimeoutMs: number;
|
|
@@ -240,6 +250,77 @@ function hasValidTranscriptAuthority(path: unknown, identity: unknown): path is
|
|
|
240
250
|
return typeof path === "string" && path.length > 0 && isSessionLifecycleTranscriptIdentity(identity);
|
|
241
251
|
}
|
|
242
252
|
|
|
253
|
+
function isSessionLifecycleMcpServer(value: unknown): value is SessionLifecycleMcpServer {
|
|
254
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
255
|
+
const server = value as Record<string, unknown>;
|
|
256
|
+
if (server.type === "http" || server.type === "sse") {
|
|
257
|
+
if (
|
|
258
|
+
!Object.keys(server).every(key => key === "type" || key === "name" || key === "url" || key === "headers") ||
|
|
259
|
+
typeof server.name !== "string" ||
|
|
260
|
+
!/^[A-Za-z0-9_.-]{1,100}$/.test(server.name) ||
|
|
261
|
+
typeof server.url !== "string" ||
|
|
262
|
+
server.url.length > 8_192
|
|
263
|
+
)
|
|
264
|
+
return false;
|
|
265
|
+
try {
|
|
266
|
+
const url = new URL(server.url);
|
|
267
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return false;
|
|
268
|
+
} catch {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
if (server.headers === undefined) return true;
|
|
272
|
+
if (typeof server.headers !== "object" || server.headers === null || Array.isArray(server.headers)) return false;
|
|
273
|
+
const headers = server.headers as Record<string, unknown>;
|
|
274
|
+
return (
|
|
275
|
+
Object.keys(headers).length <= 100 &&
|
|
276
|
+
Object.entries(headers).every(
|
|
277
|
+
([name, headerValue]) =>
|
|
278
|
+
name.length > 0 &&
|
|
279
|
+
name.length <= 256 &&
|
|
280
|
+
!name.includes("\r") &&
|
|
281
|
+
!name.includes("\n") &&
|
|
282
|
+
typeof headerValue === "string" &&
|
|
283
|
+
headerValue.length <= 8_192 &&
|
|
284
|
+
!headerValue.includes("\r") &&
|
|
285
|
+
!headerValue.includes("\n"),
|
|
286
|
+
)
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
const env = server.env;
|
|
290
|
+
return (
|
|
291
|
+
Object.keys(server).every(
|
|
292
|
+
key => key === "type" || key === "name" || key === "command" || key === "args" || key === "env",
|
|
293
|
+
) &&
|
|
294
|
+
(server.type === undefined || server.type === "stdio") &&
|
|
295
|
+
typeof server.name === "string" &&
|
|
296
|
+
/^[A-Za-z0-9_.-]{1,100}$/.test(server.name) &&
|
|
297
|
+
typeof server.command === "string" &&
|
|
298
|
+
server.command.length <= 4_096 &&
|
|
299
|
+
path.isAbsolute(server.command) &&
|
|
300
|
+
Array.isArray(server.args) &&
|
|
301
|
+
server.args.length <= 100 &&
|
|
302
|
+
server.args.every(argument => typeof argument === "string" && argument.length <= 8_192) &&
|
|
303
|
+
(env === undefined ||
|
|
304
|
+
(typeof env === "object" &&
|
|
305
|
+
env !== null &&
|
|
306
|
+
!Array.isArray(env) &&
|
|
307
|
+
Object.keys(env).length <= 100 &&
|
|
308
|
+
Object.entries(env).every(
|
|
309
|
+
([name, envValue]) =>
|
|
310
|
+
/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) && typeof envValue === "string" && envValue.length <= 32_768,
|
|
311
|
+
)))
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function isSessionLifecycleMcpServers(value: unknown): value is SessionLifecycleMcpServer[] {
|
|
316
|
+
return (
|
|
317
|
+
Array.isArray(value) &&
|
|
318
|
+
value.length <= 64 &&
|
|
319
|
+
value.every(isSessionLifecycleMcpServer) &&
|
|
320
|
+
new Set(value.map(server => server.name)).size === value.length
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
243
324
|
export function readSessionLifecycleLaunchRequest(
|
|
244
325
|
value: string | undefined,
|
|
245
326
|
now = Date.now(),
|
|
@@ -270,6 +351,7 @@ export function readSessionLifecycleLaunchRequest(
|
|
|
270
351
|
(request.effectMarker !== undefined &&
|
|
271
352
|
(typeof request.effectMarker !== "string" || !/^[A-Za-z0-9._-]{1,128}$/.test(request.effectMarker))) ||
|
|
272
353
|
(request.modelPreset !== undefined && (typeof request.modelPreset !== "string" || !request.modelPreset)) ||
|
|
354
|
+
(request.mcpServers !== undefined && !isSessionLifecycleMcpServers(request.mcpServers)) ||
|
|
273
355
|
!hasValidLifecycleDeadlines(
|
|
274
356
|
{
|
|
275
357
|
receivedAt: request.receivedAt as number,
|
|
@@ -302,6 +384,7 @@ type SessionLaunch = {
|
|
|
302
384
|
sessionPath?: string;
|
|
303
385
|
sessionIdentity?: SessionLifecycleTranscriptIdentity;
|
|
304
386
|
modelPreset?: string;
|
|
387
|
+
mcpServers?: SessionLifecycleMcpServer[];
|
|
305
388
|
worktree?: SessionLifecycleWorktreeTarget;
|
|
306
389
|
worktreePlan?: SkcLaunchWorktreePlan;
|
|
307
390
|
};
|
|
@@ -325,14 +408,36 @@ function serializeCleanupIdentity(identity: CleanupIdentity): BrokerCleanupIdent
|
|
|
325
408
|
};
|
|
326
409
|
}
|
|
327
410
|
|
|
328
|
-
const fail = (
|
|
411
|
+
const fail = (
|
|
412
|
+
code: string,
|
|
413
|
+
message: string,
|
|
414
|
+
cleanup?: CleanupEvidence,
|
|
415
|
+
details?: ModelProfileErrorDetails,
|
|
416
|
+
): BrokerResponse => ({
|
|
329
417
|
ok: false,
|
|
330
|
-
error: { code: code as never, message, ...(cleanup ? { cleanup } : {}) },
|
|
418
|
+
error: { code: code as never, message, ...(details ? { details } : {}), ...(cleanup ? { cleanup } : {}) },
|
|
331
419
|
});
|
|
332
420
|
function text(value: unknown): string | undefined {
|
|
333
421
|
return typeof value === "string" && value ? value : undefined;
|
|
334
422
|
}
|
|
335
423
|
|
|
424
|
+
function validateBrokerModelPreset(agentDir: string, requestedProfile: string): string | BrokerResponse {
|
|
425
|
+
const modelsConfigFile = ModelsConfigFile.relocate(path.join(agentDir, "models.yml"));
|
|
426
|
+
modelsConfigFile.invalidate();
|
|
427
|
+
const loaded = modelsConfigFile.tryLoad();
|
|
428
|
+
const profiles = mergeModelProfiles(loaded.status === "ok" ? loaded.value.profiles : undefined);
|
|
429
|
+
try {
|
|
430
|
+
return validateModelProfileName(requestedProfile, profiles, loaded.status === "error" ? loaded.error : undefined);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
if (isModelProfileError(error)) return fail(error.code, error.message, undefined, error.details);
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function validateBrokerModelPresetForTest(agentDir: string, requestedProfile: string): string | BrokerResponse {
|
|
438
|
+
return validateBrokerModelPreset(agentDir, requestedProfile);
|
|
439
|
+
}
|
|
440
|
+
|
|
336
441
|
function readinessTimeout(input: Input): number | BrokerResponse {
|
|
337
442
|
const value = input.readinessTimeoutMs;
|
|
338
443
|
if (value === undefined) return READY_TIMEOUT_MS;
|
|
@@ -601,12 +706,31 @@ async function reconcileReadyScope(broker: Broker, id: string, scope: string | u
|
|
|
601
706
|
});
|
|
602
707
|
}
|
|
603
708
|
|
|
709
|
+
/**
|
|
710
|
+
* Operator override for the session-host command, resolved from trusted
|
|
711
|
+
* environment sources only.
|
|
712
|
+
*
|
|
713
|
+
* The result is spawned directly, so whatever can set it chooses which binary
|
|
714
|
+
* the broker runs. `$env` merges the caller's `cwd/.env` into `process.env`, so
|
|
715
|
+
* reading it there would let repository content replace the session host;
|
|
716
|
+
* resolve it the same way provider credentials are (launching shell plus
|
|
717
|
+
* SKC/user-owned `.env` files, never the project `.env`).
|
|
718
|
+
*/
|
|
719
|
+
function sdkSessionCommandOverride(): { file: string; args: string[] } | undefined {
|
|
720
|
+
const configured = $credentialEnv("SKC_SDK_SESSION_COMMAND");
|
|
721
|
+
if (!configured) return undefined;
|
|
722
|
+
const [file, ...args] = configured.trim().split(/\s+/);
|
|
723
|
+
return file ? { file, args } : undefined;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/** Test seam: the session-host command override as resolved from trusted env. */
|
|
727
|
+
export function sdkSessionCommandOverrideForTest(): { file: string; args: string[] } | undefined {
|
|
728
|
+
return sdkSessionCommandOverride();
|
|
729
|
+
}
|
|
730
|
+
|
|
604
731
|
function command(broker: Broker): LifecycleCommand {
|
|
605
|
-
const configured =
|
|
606
|
-
if (configured)
|
|
607
|
-
const [file, ...args] = configured.trim().split(/\s+/);
|
|
608
|
-
if (file) return { file, args };
|
|
609
|
-
}
|
|
732
|
+
const configured = sdkSessionCommandOverride();
|
|
733
|
+
if (configured) return configured;
|
|
610
734
|
return lifecycleCommandResolversForTest.get(broker)?.() ?? resolveSdkInternalSpawnCommand("session-host-internal");
|
|
611
735
|
}
|
|
612
736
|
|
|
@@ -623,7 +747,7 @@ type ReadyAuthority = {
|
|
|
623
747
|
};
|
|
624
748
|
type ReadinessResult =
|
|
625
749
|
| { kind: "ready"; authority: ReadyAuthority }
|
|
626
|
-
| { kind: "startup_failed";
|
|
750
|
+
| { kind: "startup_failed"; failure: SdkStartupFailure }
|
|
627
751
|
| { kind: "child_exited" }
|
|
628
752
|
| { kind: "timeout" };
|
|
629
753
|
const processIncarnationReadersForTest = new WeakMap<Broker, (pid: number) => string | undefined>();
|
|
@@ -796,19 +920,25 @@ function isLifecycleTranscriptEvidence(value: unknown): value is LifecycleTransc
|
|
|
796
920
|
function isSdkStartupFailure(value: unknown): value is SdkStartupFailure {
|
|
797
921
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
798
922
|
const failure = value as Record<string, unknown>;
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
(
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
failure.reason
|
|
807
|
-
failure.reason
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
923
|
+
const keys = Object.keys(failure);
|
|
924
|
+
if (
|
|
925
|
+
!keys.every(
|
|
926
|
+
key => key === "phase" || key === "reason" || key === "message" || key === "code" || key === "details",
|
|
927
|
+
) ||
|
|
928
|
+
(failure.phase !== "registration" && failure.phase !== "startup") ||
|
|
929
|
+
(failure.reason !== "disabled" &&
|
|
930
|
+
failure.reason !== "ineligible" &&
|
|
931
|
+
failure.reason !== "factory_absent" &&
|
|
932
|
+
failure.reason !== "runner_absent" &&
|
|
933
|
+
failure.reason !== "pending" &&
|
|
934
|
+
failure.reason !== "failed") ||
|
|
935
|
+
typeof failure.message !== "string" ||
|
|
936
|
+
Buffer.byteLength(failure.message) === 0 ||
|
|
937
|
+
Buffer.byteLength(failure.message) > 512
|
|
938
|
+
)
|
|
939
|
+
return false;
|
|
940
|
+
if (failure.code === undefined && failure.details === undefined) return keys.length === 3;
|
|
941
|
+
return keys.length === 5 && isModelProfileError(failure);
|
|
812
942
|
}
|
|
813
943
|
|
|
814
944
|
function isLifecycleFailureArtifact(value: unknown): value is LifecycleFailureArtifact {
|
|
@@ -817,8 +947,14 @@ function isLifecycleFailureArtifact(value: unknown): value is LifecycleFailureAr
|
|
|
817
947
|
if (!isEffectMarker(record)) return false;
|
|
818
948
|
const artifact = value as LifecycleFailureArtifact;
|
|
819
949
|
return (
|
|
820
|
-
Object.keys(record).length ===
|
|
821
|
-
|
|
950
|
+
Object.keys(record).length ===
|
|
951
|
+
7 + (artifact.code === undefined ? 0 : 2) + (artifact.transcript === undefined ? 0 : 1) &&
|
|
952
|
+
isSdkStartupFailure({
|
|
953
|
+
phase: artifact.phase,
|
|
954
|
+
reason: artifact.reason,
|
|
955
|
+
message: artifact.message,
|
|
956
|
+
...(artifact.code === undefined ? {} : { code: artifact.code, details: artifact.details }),
|
|
957
|
+
}) &&
|
|
822
958
|
isRollbackResult(artifact.rollback) &&
|
|
823
959
|
(artifact.transcript === undefined || isLifecycleTranscriptEvidence(artifact.transcript))
|
|
824
960
|
);
|
|
@@ -864,6 +1000,8 @@ export async function writeSessionLifecycleFailure(
|
|
|
864
1000
|
...(transcript ? { transcript } : {}),
|
|
865
1001
|
};
|
|
866
1002
|
const bytes = Buffer.from(canonicalJson(artifact), "utf8");
|
|
1003
|
+
if (bytes.length > MAX_LIFECYCLE_METADATA_BYTES)
|
|
1004
|
+
throw new Error("Lifecycle startup failure exceeds the metadata size ceiling.");
|
|
867
1005
|
const target = lifecycleFailurePath(root, id, effectMarker);
|
|
868
1006
|
const temporary = path.join(directory, `.${id}.lifecycle.failure.${effectMarker}.${randomUUID()}.tmp`);
|
|
869
1007
|
let published = false;
|
|
@@ -1240,6 +1378,10 @@ function validateLifecycleMetadataReplay(cleanup: CleanupEvidence): BrokerRespon
|
|
|
1240
1378
|
if (!current) continue;
|
|
1241
1379
|
if (!sameLifecycleCleanupIdentity(current.identity, file.identity))
|
|
1242
1380
|
return fail("terminal_uncertain", "Lifecycle metadata candidate lacks exact replay authority.");
|
|
1381
|
+
// A completed file's recorded retained quarantine is receipt-bound durable
|
|
1382
|
+
// evidence; anything else that remains is an active survivor.
|
|
1383
|
+
if (file.completed && file.detachedPath && path.resolve(candidate) === path.resolve(file.detachedPath))
|
|
1384
|
+
continue;
|
|
1243
1385
|
activeCandidates++;
|
|
1244
1386
|
}
|
|
1245
1387
|
if (file.completed && activeCandidates > 0)
|
|
@@ -1288,7 +1430,21 @@ function lifecycleMetadataReplayAbsent(cleanup: CleanupEvidence): boolean {
|
|
|
1288
1430
|
const id = cleanup.sessionId!;
|
|
1289
1431
|
const required = new Set<string>([lifecycleMarkerPath(root, id), lifecycleReadyPath(root, id)]);
|
|
1290
1432
|
for (const file of files) for (const candidate of lifecycleCleanupCandidates(file)) required.add(candidate);
|
|
1291
|
-
|
|
1433
|
+
for (const candidate of required) {
|
|
1434
|
+
if (absentLifecyclePath(candidate)) continue;
|
|
1435
|
+
// A completed file's recorded retained quarantine is durable evidence, not a
|
|
1436
|
+
// survivor — accept it only at its receipt-bound path and identity.
|
|
1437
|
+
const owner = files.find(
|
|
1438
|
+
file =>
|
|
1439
|
+
file.completed === true &&
|
|
1440
|
+
file.detachedPath !== undefined &&
|
|
1441
|
+
path.resolve(file.detachedPath) === path.resolve(candidate),
|
|
1442
|
+
);
|
|
1443
|
+
if (!owner) return false;
|
|
1444
|
+
const current = captureLifecycleFile(candidate, true, true);
|
|
1445
|
+
if (!current || !sameLifecycleCleanupIdentity(current.identity, owner.identity)) return false;
|
|
1446
|
+
}
|
|
1447
|
+
return true;
|
|
1292
1448
|
}
|
|
1293
1449
|
|
|
1294
1450
|
/**
|
|
@@ -1551,16 +1707,28 @@ async function reconcileLifecycleCleanup(
|
|
|
1551
1707
|
const candidates = lifecycleCleanupCandidates(file);
|
|
1552
1708
|
if (file.completed) {
|
|
1553
1709
|
for (const candidate of candidates) {
|
|
1710
|
+
let stat: ReturnType<typeof fsSync.lstatSync>;
|
|
1554
1711
|
try {
|
|
1555
|
-
fsSync.lstatSync(candidate);
|
|
1556
|
-
return fail(
|
|
1557
|
-
"terminal_uncertain",
|
|
1558
|
-
"Lifecycle cleanup receipt marks a target complete while an authorized candidate remains.",
|
|
1559
|
-
);
|
|
1712
|
+
stat = fsSync.lstatSync(candidate);
|
|
1560
1713
|
} catch (error) {
|
|
1561
|
-
if ((error as NodeJS.ErrnoException).code
|
|
1562
|
-
|
|
1714
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
1715
|
+
return fail("terminal_uncertain", "Lifecycle cleanup completion could not be safely inspected.");
|
|
1716
|
+
}
|
|
1717
|
+
// A completed file's recorded retained quarantine is durable evidence,
|
|
1718
|
+
// not a survivor — accept it only at its receipt-bound path and identity.
|
|
1719
|
+
if (
|
|
1720
|
+
file.detachedPath &&
|
|
1721
|
+
path.resolve(candidate) === path.resolve(file.detachedPath) &&
|
|
1722
|
+
stat.isFile() &&
|
|
1723
|
+
!stat.isSymbolicLink()
|
|
1724
|
+
) {
|
|
1725
|
+
const current = captureLifecycleFile(candidate, true, true);
|
|
1726
|
+
if (current && sameLifecycleCleanupIdentity(current.identity, file.identity)) continue;
|
|
1563
1727
|
}
|
|
1728
|
+
return fail(
|
|
1729
|
+
"terminal_uncertain",
|
|
1730
|
+
"Lifecycle cleanup receipt marks a target complete while an authorized candidate remains.",
|
|
1731
|
+
);
|
|
1564
1732
|
}
|
|
1565
1733
|
continue;
|
|
1566
1734
|
}
|
|
@@ -1630,6 +1798,22 @@ async function reconcileLifecycleCleanup(
|
|
|
1630
1798
|
quarantineName: path.basename(currentFile.plannedPath),
|
|
1631
1799
|
});
|
|
1632
1800
|
if (!result.ok) {
|
|
1801
|
+
if (result.code === "cleanup_pending" && result.detachedPath === currentFile.plannedPath) {
|
|
1802
|
+
// Typed retained authority: the native verified the exact identity-bound
|
|
1803
|
+
// detach and retained the quarantine as durable evidence. Record the
|
|
1804
|
+
// evidence and advance — never claim a terminal byte deletion.
|
|
1805
|
+
const lifecycleFiles = activeCleanup.lifecycleFiles!.map((candidate, candidateIndex) =>
|
|
1806
|
+
candidateIndex === index
|
|
1807
|
+
? { ...candidate, detachedPath: result.detachedPath, completed: true as const }
|
|
1808
|
+
: candidate,
|
|
1809
|
+
);
|
|
1810
|
+
activeCleanup = { ...activeCleanup, lifecycleFiles };
|
|
1811
|
+
await broker.ledger.transition(identity, "effect_started", {
|
|
1812
|
+
response: fail("cleanup_pending", "Lifecycle cleanup completion was durably reconciled.", activeCleanup),
|
|
1813
|
+
});
|
|
1814
|
+
lifecycleCleanupHooksForTest.get(broker)?.();
|
|
1815
|
+
continue;
|
|
1816
|
+
}
|
|
1633
1817
|
const lifecycleFiles = activeCleanup.lifecycleFiles!.map((candidate, candidateIndex) =>
|
|
1634
1818
|
candidateIndex === index && result.detachedPath
|
|
1635
1819
|
? { ...candidate, detachedPath: result.detachedPath }
|
|
@@ -1664,6 +1848,22 @@ async function readSessionLifecycleFailure(
|
|
|
1664
1848
|
?.artifact;
|
|
1665
1849
|
}
|
|
1666
1850
|
|
|
1851
|
+
export async function readSessionLifecycleFailureForTest(
|
|
1852
|
+
root: string,
|
|
1853
|
+
id: string,
|
|
1854
|
+
expected: { pid: number; effectMarker: string; incarnation: string },
|
|
1855
|
+
): Promise<SdkStartupFailure | undefined> {
|
|
1856
|
+
const artifact = await readSessionLifecycleFailure(root, id, expected);
|
|
1857
|
+
return artifact
|
|
1858
|
+
? {
|
|
1859
|
+
phase: artifact.phase,
|
|
1860
|
+
reason: artifact.reason,
|
|
1861
|
+
message: artifact.message,
|
|
1862
|
+
...(artifact.code === undefined ? {} : { code: artifact.code, details: artifact.details }),
|
|
1863
|
+
}
|
|
1864
|
+
: undefined;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1667
1867
|
async function hasDurableProcessIdentity(
|
|
1668
1868
|
root: string,
|
|
1669
1869
|
id: string,
|
|
@@ -2055,14 +2255,14 @@ async function waitForReady(
|
|
|
2055
2255
|
): Promise<ReadinessResult> {
|
|
2056
2256
|
while (timing.now() < deadline) {
|
|
2057
2257
|
const startupFailure = await readSessionLifecycleFailure(root, id, expected);
|
|
2058
|
-
if (startupFailure) return { kind: "startup_failed",
|
|
2258
|
+
if (startupFailure) return { kind: "startup_failed", failure: startupFailure };
|
|
2059
2259
|
if (
|
|
2060
2260
|
observeProcess(expected.pid, expected.incarnation, value => processIncarnationForBroker(broker, value)) ===
|
|
2061
2261
|
"exited"
|
|
2062
2262
|
) {
|
|
2063
2263
|
const finalStartupFailure = await readSessionLifecycleFailure(root, id, expected);
|
|
2064
2264
|
return finalStartupFailure
|
|
2065
|
-
? { kind: "startup_failed",
|
|
2265
|
+
? { kind: "startup_failed", failure: finalStartupFailure }
|
|
2066
2266
|
: { kind: "child_exited" };
|
|
2067
2267
|
}
|
|
2068
2268
|
try {
|
|
@@ -2190,10 +2390,15 @@ async function launchInput(
|
|
|
2190
2390
|
const requested = sessionId(input);
|
|
2191
2391
|
if (requested !== undefined && !isCanonicalSessionId(requested))
|
|
2192
2392
|
return fail("invalid_input", "sessionId must be a canonical safe identifier.");
|
|
2393
|
+
if (input.modelPreset !== undefined && (typeof input.modelPreset !== "string" || input.modelPreset.length === 0))
|
|
2394
|
+
return fail("invalid_input", "modelPreset must be a non-empty exact profile ID.");
|
|
2193
2395
|
const modelPreset = text(input.modelPreset);
|
|
2396
|
+
if (input.mcpServers !== undefined && !isSessionLifecycleMcpServers(input.mcpServers))
|
|
2397
|
+
return fail("invalid_input", "mcpServers must contain unique valid stdio, HTTP, or SSE server definitions.");
|
|
2398
|
+
const mcpServers = input.mcpServers as SessionLifecycleMcpServer[] | undefined;
|
|
2194
2399
|
|
|
2195
2400
|
if (operation === "session.create")
|
|
2196
|
-
return { id: randomUUID(), cwd, root: resolvedRoot, modelPreset, worktree, worktreePlan };
|
|
2401
|
+
return { id: randomUUID(), cwd, root: resolvedRoot, modelPreset, mcpServers, worktree, worktreePlan };
|
|
2197
2402
|
if (operation === "session.resume") {
|
|
2198
2403
|
if (!requested) return fail("invalid_input", "sessionId is required to resume a saved session.");
|
|
2199
2404
|
const savedPath = text(input.sessionPath);
|
|
@@ -2207,6 +2412,7 @@ async function launchInput(
|
|
|
2207
2412
|
sessionPath: saved.path,
|
|
2208
2413
|
sessionIdentity: saved.identity,
|
|
2209
2414
|
modelPreset,
|
|
2415
|
+
mcpServers,
|
|
2210
2416
|
worktree,
|
|
2211
2417
|
worktreePlan,
|
|
2212
2418
|
};
|
|
@@ -2228,6 +2434,7 @@ async function launchInput(
|
|
|
2228
2434
|
sourceSessionIdentity: source.identity,
|
|
2229
2435
|
sourceCwd,
|
|
2230
2436
|
modelPreset,
|
|
2437
|
+
mcpServers,
|
|
2231
2438
|
worktree,
|
|
2232
2439
|
worktreePlan,
|
|
2233
2440
|
};
|
|
@@ -2614,6 +2821,11 @@ async function executeLifecycleResponse(
|
|
|
2614
2821
|
|
|
2615
2822
|
const launch = await launchInput(broker, operation, input);
|
|
2616
2823
|
if ("ok" in launch) return launch;
|
|
2824
|
+
if (launch.modelPreset) {
|
|
2825
|
+
const validatedModelPreset = validateBrokerModelPreset(broker.settings.agentDir, launch.modelPreset);
|
|
2826
|
+
if (typeof validatedModelPreset !== "string") return validatedModelPreset;
|
|
2827
|
+
launch.modelPreset = validatedModelPreset;
|
|
2828
|
+
}
|
|
2617
2829
|
if (!hasProcessIncarnationAuthority())
|
|
2618
2830
|
return fail(
|
|
2619
2831
|
"incarnation_unavailable",
|
|
@@ -2692,6 +2904,7 @@ async function executeLifecycleResponse(
|
|
|
2692
2904
|
...(launch.sessionPath ? { sessionPath: launch.sessionPath } : {}),
|
|
2693
2905
|
...(launch.sessionIdentity ? { sessionIdentity: launch.sessionIdentity } : {}),
|
|
2694
2906
|
...(launch.modelPreset ? { modelPreset: launch.modelPreset } : {}),
|
|
2907
|
+
...(launch.mcpServers ? { mcpServers: launch.mcpServers } : {}),
|
|
2695
2908
|
...(launch.worktree ? { worktree: launch.worktree } : {}),
|
|
2696
2909
|
};
|
|
2697
2910
|
let child: ChildProcess | undefined;
|
|
@@ -2767,7 +2980,12 @@ async function executeLifecycleResponse(
|
|
|
2767
2980
|
`Session ${launch.id} did not become ready and its spawned process could not be verified dead.`,
|
|
2768
2981
|
);
|
|
2769
2982
|
return readiness.kind === "startup_failed"
|
|
2770
|
-
? fail(
|
|
2983
|
+
? fail(
|
|
2984
|
+
readiness.failure.code ?? "spawn_failed",
|
|
2985
|
+
readiness.failure.message,
|
|
2986
|
+
undefined,
|
|
2987
|
+
readiness.failure.details,
|
|
2988
|
+
)
|
|
2771
2989
|
: readiness.kind === "child_exited"
|
|
2772
2990
|
? fail("spawn_failed", `Session ${launch.id} exited before registering readiness.`)
|
|
2773
2991
|
: fail(
|
|
@@ -3042,11 +3260,26 @@ async function executeLifecycleResponse(
|
|
|
3042
3260
|
`Unable to delete saved session artifacts: ${error instanceof Error ? error.message : String(error)}`,
|
|
3043
3261
|
);
|
|
3044
3262
|
}
|
|
3045
|
-
|
|
3263
|
+
const retainedArtifactsCleanup =
|
|
3264
|
+
deleted.kind === "cleanup_pending" &&
|
|
3265
|
+
deleted.phase === "artifacts" &&
|
|
3266
|
+
deleted.detachedArtifactsPath !== undefined &&
|
|
3267
|
+
cleanupTarget.plannedArtifactsPath !== undefined &&
|
|
3268
|
+
(deleted.detachedArtifactsPath === cleanupTarget.plannedArtifactsPath ||
|
|
3269
|
+
deleted.detachedArtifactsPath === `${cleanupTarget.plannedArtifactsPath}.removing`);
|
|
3270
|
+
if (deleted.kind === "artifacts_removed" || retainedArtifactsCleanup) {
|
|
3046
3271
|
const transcriptPhaseCleanup = {
|
|
3047
3272
|
...preauthorizedCleanup,
|
|
3048
3273
|
phase: "transcript" as const,
|
|
3049
3274
|
artifactsRemoved: true,
|
|
3275
|
+
...(deleted.kind === "cleanup_pending" && deleted.phase === "artifacts"
|
|
3276
|
+
? {
|
|
3277
|
+
detachedArtifactsPath: deleted.detachedArtifactsPath,
|
|
3278
|
+
...(deleted.artifactsIdentity
|
|
3279
|
+
? { artifactsIdentity: serializeCleanupIdentity(deleted.artifactsIdentity) }
|
|
3280
|
+
: {}),
|
|
3281
|
+
}
|
|
3282
|
+
: {}),
|
|
3050
3283
|
...(preauthorizedCleanup.artifactTree
|
|
3051
3284
|
? { artifactTree: { ...preauthorizedCleanup.artifactTree, completed: true as const } }
|
|
3052
3285
|
: {}),
|
|
@@ -3067,7 +3300,14 @@ async function executeLifecycleResponse(
|
|
|
3067
3300
|
artifactsRemoved: true,
|
|
3068
3301
|
});
|
|
3069
3302
|
}
|
|
3070
|
-
if (
|
|
3303
|
+
if (
|
|
3304
|
+
deleted.kind === "cleanup_pending" &&
|
|
3305
|
+
!(
|
|
3306
|
+
deleted.phase === "transcript" &&
|
|
3307
|
+
deleted.detachedTranscriptPath !== undefined &&
|
|
3308
|
+
deleted.detachedTranscriptPath === cleanupTarget.plannedTranscriptPath
|
|
3309
|
+
)
|
|
3310
|
+
)
|
|
3071
3311
|
return fail(
|
|
3072
3312
|
"cleanup_pending",
|
|
3073
3313
|
`Saved session cleanup is pending in ${deleted.phase}: ${deleted.error.message}`,
|
|
@@ -3322,6 +3562,9 @@ export async function executeLifecycle(
|
|
|
3322
3562
|
phase: evidence.artifact.phase,
|
|
3323
3563
|
reason: evidence.artifact.reason,
|
|
3324
3564
|
message: evidence.artifact.message,
|
|
3565
|
+
...(evidence.artifact.code === undefined
|
|
3566
|
+
? {}
|
|
3567
|
+
: { code: evidence.artifact.code, details: evidence.artifact.details }),
|
|
3325
3568
|
rollback: {
|
|
3326
3569
|
endpointGeneration: evidence.artifact.rollback.endpointGeneration,
|
|
3327
3570
|
fenced: evidence.artifact.rollback.fenced,
|
|
@@ -3386,11 +3629,18 @@ export async function executeLifecycle(
|
|
|
3386
3629
|
: startupFailure && cleanupProof
|
|
3387
3630
|
? {
|
|
3388
3631
|
ok: false,
|
|
3389
|
-
error:
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3632
|
+
error: startupFailure.code
|
|
3633
|
+
? {
|
|
3634
|
+
code: startupFailure.code,
|
|
3635
|
+
message: startupFailure.message,
|
|
3636
|
+
details: startupFailure.details!,
|
|
3637
|
+
endpoint: "unavailable" as const,
|
|
3638
|
+
}
|
|
3639
|
+
: {
|
|
3640
|
+
code: "spawn_failed",
|
|
3641
|
+
message: "No ready SDK endpoint remains available.",
|
|
3642
|
+
endpoint: "unavailable" as const,
|
|
3643
|
+
},
|
|
3394
3644
|
...(durableEffects ? { durableEffects } : {}),
|
|
3395
3645
|
startupFailure,
|
|
3396
3646
|
}
|
|
@@ -131,6 +131,7 @@ function compactEvents(events: SessionIndexEvent[]): SessionIndexEvent[] {
|
|
|
131
131
|
|
|
132
132
|
async function appendSync(file: string, value: string): Promise<void> {
|
|
133
133
|
const h = await fs.open(file, "a", 0o600);
|
|
134
|
+
let failure: { error: unknown } | undefined;
|
|
134
135
|
try {
|
|
135
136
|
const data = Buffer.from(`${value}\n`);
|
|
136
137
|
for (let offset = 0; offset < data.length; ) {
|
|
@@ -139,9 +140,18 @@ async function appendSync(file: string, value: string): Promise<void> {
|
|
|
139
140
|
offset += bytesWritten;
|
|
140
141
|
}
|
|
141
142
|
await h.sync();
|
|
142
|
-
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
failure = { error };
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
143
147
|
await h.close();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
// Bun may report EBADF when concurrent child-pipe teardown has already
|
|
150
|
+
// released a fully written and fsynced append handle. The descriptor is
|
|
151
|
+
// closed in that case; every other close failure remains fatal.
|
|
152
|
+
if ((error as NodeJS.ErrnoException).code !== "EBADF" && failure === undefined) failure = { error };
|
|
144
153
|
}
|
|
154
|
+
if (failure !== undefined) throw failure.error;
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
async function syncDirectory(file: string): Promise<void> {
|
|
@@ -67,7 +67,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
|
|
|
67
67
|
idleTimeoutMs: 60_000,
|
|
68
68
|
rich: { enabled: true },
|
|
69
69
|
richDraft: { enabled: false },
|
|
70
|
-
toolActivity: { enabled:
|
|
70
|
+
toolActivity: { enabled: false },
|
|
71
71
|
topics: { nameTemplate: undefined },
|
|
72
72
|
btw: { enabled: true },
|
|
73
73
|
streaming: { enabled: true },
|
|
@@ -102,7 +102,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
|
|
|
102
102
|
idleTimeoutMs: 60_000,
|
|
103
103
|
rich: { enabled: true },
|
|
104
104
|
richDraft: { enabled: false },
|
|
105
|
-
toolActivity: { enabled:
|
|
105
|
+
toolActivity: { enabled: false },
|
|
106
106
|
topics: { nameTemplate: undefined },
|
|
107
107
|
btw: { enabled: true },
|
|
108
108
|
streaming: { enabled: true },
|
|
@@ -27,11 +27,19 @@ export type ChatDaemonAction = "stop" | "reload";
|
|
|
27
27
|
* Generation 6 carries the retained managed filesystem authority boundary.
|
|
28
28
|
* Generation 7 restores macOS daemon signaling (kill(2) with a start-time
|
|
29
29
|
* incarnation recheck) so a live/hung owner can be replaced without an external
|
|
30
|
-
* `kill -9`.
|
|
30
|
+
* `kill -9`. Generation 8 adopts Windows expected-identity ACL verification and
|
|
31
|
+
* repair for shared native authority. Generation 9 refreshes the shared native
|
|
32
|
+
* authority declaration contract with bounded frame-delivery acknowledgement.
|
|
33
|
+
* Generation 11 accepts typed retained exact-unlink cleanup authority — a
|
|
34
|
+
* concrete detached quarantine plus a proven-absent canonical lock pathname —
|
|
35
|
+
* when deleting an observed owner-lock lease. Generation 12 refreshes retained
|
|
36
|
+
* native path and process authority semantics. Generation 13 rejects special
|
|
37
|
+
* files before retained native authority opens. Generation 14 reloads shared
|
|
38
|
+
* chat daemons after notification configuration parsing changes.
|
|
31
39
|
*/
|
|
32
40
|
export const CHAT_DAEMON_GENERATIONS: Readonly<Record<ChatDaemonKind, number>> = {
|
|
33
|
-
discord:
|
|
34
|
-
slack:
|
|
41
|
+
discord: 14,
|
|
42
|
+
slack: 14,
|
|
35
43
|
};
|
|
36
44
|
|
|
37
45
|
export function chatDaemonGeneration(kind: ChatDaemonKind): number {
|
|
@@ -937,10 +945,19 @@ async function ownsChatDaemonOwnerLock(lock: string, lease: ChatDaemonOwnerLockL
|
|
|
937
945
|
/** Deletes only the exact lease observed by this contender; a successor is retained. */
|
|
938
946
|
function unlinkExactChatDaemonOwnerLock(lock: string, lease: ChatDaemonOwnerLockLease): boolean {
|
|
939
947
|
try {
|
|
940
|
-
|
|
948
|
+
const removed = native.exactUnlink(lock, {
|
|
941
949
|
...lease,
|
|
942
950
|
quarantineName: `.skc-delete-chat-daemon-lock-${crypto.randomUUID()}`,
|
|
943
|
-
})
|
|
951
|
+
});
|
|
952
|
+
if (removed.ok) return true;
|
|
953
|
+
// Accept only typed retained authority: a concrete detached quarantine plus
|
|
954
|
+
// a proven-absent canonical lock pathname. Anything else stays fail-closed.
|
|
955
|
+
return (
|
|
956
|
+
removed.code === "cleanup_pending" &&
|
|
957
|
+
typeof removed.detachedPath === "string" &&
|
|
958
|
+
removed.detachedPath.length > 0 &&
|
|
959
|
+
!fs.existsSync(lock)
|
|
960
|
+
);
|
|
944
961
|
} catch {
|
|
945
962
|
return false;
|
|
946
963
|
}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* removed), but the user can still adjust per-surface behaviour from inside a
|
|
6
6
|
* session thread with small slash commands:
|
|
7
7
|
*
|
|
8
|
-
* - `/verbose` switch the mirror to
|
|
9
|
-
* - `/lean`
|
|
8
|
+
* - `/verbose` switch the mirror to per-turn assistant text + tool summaries + reasoning summaries
|
|
9
|
+
* - `/lean` settled assistant answer at idle + immediate ask lead-ins (no intermediate tool-turn flood)
|
|
10
10
|
* - `/verbosity lean|verbose`
|
|
11
11
|
* - `/redact on|off` toggle redaction of streamed content
|
|
12
12
|
*
|