@sayknow-cli/coding-agent 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +112 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-contract.d.ts +40 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +156 -10
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +33 -2
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +5 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +13 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
- package/dist/types/runtime-mcp/manager.d.ts +9 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +52 -4
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/query/handlers.d.ts +14 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +9 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +83 -24
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
- package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +89 -18
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/skc-runtime/state-writer.d.ts +5 -1
- package/dist/types/skc-runtime/team-launch.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
- package/dist/types/skc-runtime/team-store.d.ts +12 -0
- package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +133 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +22 -9
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +7 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -12
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/compile-args.ts +0 -1
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-docs-index.ts +4 -1
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +58 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +16 -100
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +6 -38
- package/src/cli/read-cli.ts +2 -1
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +133 -11
- package/src/commands/daemon.ts +22 -5
- package/src/commands/deep-interview.ts +26 -1
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +134 -17
- package/src/commands/team.ts +1 -1
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-contract.ts +174 -0
- package/src/config/model-profiles.ts +51 -10
- package/src/config/model-registry.ts +24 -4
- package/src/config/model-resolver.ts +2 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +157 -10
- package/src/config/settings.ts +254 -108
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
- package/src/defaults/skc/skills/team/SKILL.md +1 -1
- package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/eval/py/executor.ts +37 -13
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/extensibility/shared-events.ts +5 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/internal-urls/docs-index.generated.ts +17 -16
- package/src/internal-urls/local-protocol.ts +87 -25
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +120 -42
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +52 -0
- package/src/modes/acp/acp-agent.ts +647 -83
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/sayknow-pet-widget.ts +27 -8
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +26 -8
- package/src/modes/components/welcome.ts +50 -23
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +101 -54
- package/src/modes/controllers/event-controller.ts +119 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +100 -8
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +4 -1
- package/src/modes/interactive-mode.ts +201 -60
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +99 -4
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +17 -3
- package/src/prompts/agents/critic.md +17 -2
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -3
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
- package/src/runtime-mcp/json-rpc.ts +28 -11
- package/src/runtime-mcp/manager.ts +77 -59
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +293 -43
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +22 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +10 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +793 -121
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +87 -25
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/prompt-reconciliation.ts +240 -0
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
- package/src/sdk/bus/telegram-daemon-control.ts +161 -40
- package/src/sdk/bus/telegram-daemon.ts +3649 -593
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +8 -2
- package/src/sdk/host/control/operations.ts +3 -2
- package/src/sdk/host/host.ts +27 -9
- package/src/sdk/host/query/handlers.ts +125 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +145 -0
- package/src/sdk/protocol/operation-registry.ts +24 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +69 -8
- package/src/sdk/startup-capability.ts +15 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +1112 -314
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +1426 -259
- package/src/session/internal/managed-session-storage.ts +414 -238
- package/src/session/internal/native-publish-outcome.ts +271 -0
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2079 -469
- package/src/session/session-storage.ts +151 -11
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +49 -5
- package/src/setup/provider-presets.json +2 -1
- package/src/skc-runtime/deep-interview-runtime.ts +303 -36
- package/src/skc-runtime/deep-interview-stage.ts +1016 -0
- package/src/skc-runtime/deep-interview-state.ts +8 -5
- package/src/skc-runtime/gc-render.ts +1 -0
- package/src/skc-runtime/gc-runtime.ts +11 -1
- package/src/skc-runtime/linux-proc.ts +70 -36
- package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/skc-runtime/ralplan-runtime.ts +1057 -75
- package/src/skc-runtime/repository-binding.ts +267 -0
- package/src/skc-runtime/session-state-sidecar.ts +25 -9
- package/src/skc-runtime/state-runtime.ts +50 -12
- package/src/skc-runtime/state-writer.ts +68 -56
- package/src/skc-runtime/team-launch.ts +34 -5
- package/src/skc-runtime/team-runtime.ts +41 -1
- package/src/skc-runtime/team-store.ts +32 -0
- package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/skc-runtime/team-workers.ts +1 -0
- package/src/skc-runtime/tmux-common.ts +13 -2
- package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
- package/src/skc-runtime/tmux-sessions.ts +45 -6
- package/src/skc-runtime/ultragoal-guard.ts +93 -15
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
- package/src/skc-runtime/ultragoal-runtime.ts +469 -38
- package/src/skc-runtime/workflow-manifest.generated.json +195 -5
- package/src/skc-runtime/workflow-manifest.ts +67 -3
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +104 -12
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/executor.ts +165 -47
- package/src/task/index.ts +123 -15
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/receipt.ts +3 -0
- package/src/task/render.ts +45 -26
- package/src/task/skc-command.ts +2 -2
- package/src/task/types.ts +55 -10
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +127 -154
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +7 -1
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1544 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
|
@@ -167,6 +167,8 @@ export interface SessionStorage {
|
|
|
167
167
|
readSnapshotSync?(path: string): SessionStorageSnapshot;
|
|
168
168
|
statSync(path: string): SessionStorageStat;
|
|
169
169
|
listFilesSync(dir: string, pattern: string): string[];
|
|
170
|
+
/** List matching files with mtimes without issuing one JavaScript stat call per path. */
|
|
171
|
+
listFilesByMtime?(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>>;
|
|
170
172
|
/**
|
|
171
173
|
* Strict directory scan that never suppresses scan/root errors. Used by strict
|
|
172
174
|
* authorization inventory; the forgiving {@link listFilesSync} stays display-only.
|
|
@@ -259,6 +261,19 @@ export interface VerifiedSessionDeleteTarget {
|
|
|
259
261
|
detachedArtifactsPath?: string;
|
|
260
262
|
/** Identity-bound quarantine path retained when transcript unlink deferred cleanup. */
|
|
261
263
|
detachedTranscriptPath?: string;
|
|
264
|
+
/** Native-retained publisher successor observed during transcript cleanup. */
|
|
265
|
+
retainedTranscriptSuccessorPath?: string;
|
|
266
|
+
/** Native-retained exchange placeholder observed during transcript cleanup. */
|
|
267
|
+
retainedTranscriptPlaceholderPath?: string;
|
|
268
|
+
/** Native-retained transcript entry whose identity could not be verified. */
|
|
269
|
+
retainedTranscriptUnknownPath?: string;
|
|
270
|
+
/** Native-retained publisher successor observed during cleanup. */
|
|
271
|
+
retainedArtifactsSuccessorPath?: string;
|
|
272
|
+
/** Native-retained exchange placeholder observed during cleanup. */
|
|
273
|
+
retainedArtifactsPlaceholderPath?: string;
|
|
274
|
+
/** Native-retained entry whose identity could not be verified. */
|
|
275
|
+
retainedArtifactsUnknownPath?: string;
|
|
276
|
+
|
|
262
277
|
/** Caller-published, no-replace quarantine pathname for the next artifact detach. */
|
|
263
278
|
plannedArtifactsPath?: string;
|
|
264
279
|
/** Caller-published, no-replace quarantine pathname for the next transcript detach. */
|
|
@@ -288,6 +303,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
288
303
|
artifactsTree: NativeDirectoryTreeSnapshot;
|
|
289
304
|
/** Transcript identity (unchanged) for retry binding. */
|
|
290
305
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
306
|
+
retainedSuccessorPath?: string;
|
|
307
|
+
retainedPlaceholderPath?: string;
|
|
308
|
+
retainedUnknownPath?: string;
|
|
291
309
|
}
|
|
292
310
|
| {
|
|
293
311
|
kind: "cleanup_pending";
|
|
@@ -297,6 +315,9 @@ export type VerifiedSessionDeleteResult =
|
|
|
297
315
|
transcriptIdentity: SessionStorageFileIdentity;
|
|
298
316
|
/** Optional identity-bound transcript quarantine path for restart cleanup. */
|
|
299
317
|
detachedTranscriptPath?: string;
|
|
318
|
+
retainedSuccessorPath?: string;
|
|
319
|
+
retainedPlaceholderPath?: string;
|
|
320
|
+
retainedUnknownPath?: string;
|
|
300
321
|
};
|
|
301
322
|
|
|
302
323
|
/** Default OS-close dispatcher: a direct `fs.closeSync`. */
|
|
@@ -306,7 +327,22 @@ const defaultCloseAdapter: SessionStorageWriterCloseAdapter = {
|
|
|
306
327
|
},
|
|
307
328
|
};
|
|
308
329
|
|
|
309
|
-
type NativeExactUnlinkResult =
|
|
330
|
+
type NativeExactUnlinkResult =
|
|
331
|
+
| {
|
|
332
|
+
ok: true;
|
|
333
|
+
detachedPath?: string;
|
|
334
|
+
retainedSuccessorPath?: string;
|
|
335
|
+
retainedPlaceholderPath?: string;
|
|
336
|
+
retainedUnknownPath?: string;
|
|
337
|
+
}
|
|
338
|
+
| {
|
|
339
|
+
ok: false;
|
|
340
|
+
code: string;
|
|
341
|
+
detachedPath?: string;
|
|
342
|
+
retainedSuccessorPath?: string;
|
|
343
|
+
retainedPlaceholderPath?: string;
|
|
344
|
+
retainedUnknownPath?: string;
|
|
345
|
+
};
|
|
310
346
|
type NativeExactUnlink = (
|
|
311
347
|
path: string,
|
|
312
348
|
identity: {
|
|
@@ -671,6 +707,21 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
671
707
|
return [];
|
|
672
708
|
}
|
|
673
709
|
}
|
|
710
|
+
async listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
711
|
+
const result = await native.glob({
|
|
712
|
+
path: dir,
|
|
713
|
+
pattern,
|
|
714
|
+
fileType: native.FileType.File,
|
|
715
|
+
recursive: false,
|
|
716
|
+
hidden: false,
|
|
717
|
+
gitignore: false,
|
|
718
|
+
sortByMtime: true,
|
|
719
|
+
});
|
|
720
|
+
return result.matches.map(match => ({
|
|
721
|
+
path: path.join(dir, match.path),
|
|
722
|
+
mtimeMs: match.mtime ?? 0,
|
|
723
|
+
}));
|
|
724
|
+
}
|
|
674
725
|
|
|
675
726
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
676
727
|
// Strict: never suppress scan/root errors. Authorization inventory depends on
|
|
@@ -766,7 +817,13 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
766
817
|
expectedArtifactsIdentity,
|
|
767
818
|
expectedArtifactsTree,
|
|
768
819
|
detachedArtifactsPath,
|
|
820
|
+
retainedArtifactsSuccessorPath,
|
|
821
|
+
retainedArtifactsPlaceholderPath,
|
|
822
|
+
retainedArtifactsUnknownPath,
|
|
769
823
|
detachedTranscriptPath,
|
|
824
|
+
retainedTranscriptSuccessorPath,
|
|
825
|
+
retainedTranscriptPlaceholderPath,
|
|
826
|
+
retainedTranscriptUnknownPath,
|
|
770
827
|
plannedArtifactsPath,
|
|
771
828
|
plannedTranscriptPath,
|
|
772
829
|
artifactsRemoved,
|
|
@@ -847,7 +904,8 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
847
904
|
if (
|
|
848
905
|
!expectedArtifactsIdentity ||
|
|
849
906
|
path.dirname(detachedArtifactsPath) !== path.dirname(transcriptPath) ||
|
|
850
|
-
!path.basename(detachedArtifactsPath).startsWith(".skc-delete-")
|
|
907
|
+
(!path.basename(detachedArtifactsPath).startsWith(".skc-delete-") &&
|
|
908
|
+
!detachedArtifactsPath.endsWith(".removing"))
|
|
851
909
|
) {
|
|
852
910
|
throw new SessionDeleteVerificationError("artifacts", "Detached artifact cleanup evidence is invalid");
|
|
853
911
|
}
|
|
@@ -882,17 +940,20 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
882
940
|
artifactsIdentity: expectedArtifactsIdentity,
|
|
883
941
|
detachedArtifactsPath: retainedRoot,
|
|
884
942
|
artifactsTree: expectedArtifactsTree,
|
|
943
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
944
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
945
|
+
: {}),
|
|
946
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
947
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
948
|
+
: {}),
|
|
949
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
950
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
951
|
+
: {}),
|
|
885
952
|
transcriptIdentity,
|
|
886
953
|
};
|
|
887
954
|
}
|
|
888
955
|
}
|
|
889
956
|
|
|
890
|
-
if (artifactsRemoved && (detachedArtifactsPath || expectedArtifactsIdentity)) {
|
|
891
|
-
throw new SessionDeleteVerificationError(
|
|
892
|
-
"artifacts",
|
|
893
|
-
"Artifact phase receipt conflicts with pending artifact cleanup",
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
957
|
const artifactsDir = transcriptPath.slice(0, -6);
|
|
897
958
|
const artifactsIdentity = this.#optionalDirectoryIdentity(artifactsDir);
|
|
898
959
|
if (artifactsRemoved && artifactsIdentity) {
|
|
@@ -947,12 +1008,48 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
947
1008
|
directory: true,
|
|
948
1009
|
quarantineName: path.basename(plannedArtifactsPath),
|
|
949
1010
|
});
|
|
950
|
-
if (!detach.
|
|
1011
|
+
if (!detach.detachedPath) {
|
|
951
1012
|
throw new SessionDeleteVerificationError(
|
|
952
1013
|
"artifacts",
|
|
953
1014
|
`Exact artifact detach rejected: ${detach.ok ? "missing_path" : detach.code}`,
|
|
954
1015
|
);
|
|
955
1016
|
}
|
|
1017
|
+
if (!detach.ok && process.platform !== "win32") {
|
|
1018
|
+
let descriptor: number | undefined;
|
|
1019
|
+
try {
|
|
1020
|
+
descriptor = fs.openSync(
|
|
1021
|
+
path.dirname(transcriptPath),
|
|
1022
|
+
fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW,
|
|
1023
|
+
);
|
|
1024
|
+
const durableParent = fs.fstatSync(descriptor, { bigint: true });
|
|
1025
|
+
if (
|
|
1026
|
+
!durableParent.isDirectory() ||
|
|
1027
|
+
durableParent.dev !== parentIdentity.dev ||
|
|
1028
|
+
durableParent.ino !== parentIdentity.ino
|
|
1029
|
+
)
|
|
1030
|
+
throw new Error("parent_changed");
|
|
1031
|
+
fs.fsyncSync(descriptor);
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
throw new SessionDeleteVerificationError("artifacts", "durability_failed", { cause: toError(error) });
|
|
1034
|
+
} finally {
|
|
1035
|
+
if (descriptor !== undefined) fs.closeSync(descriptor);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (!detach.ok) {
|
|
1039
|
+
return {
|
|
1040
|
+
kind: "cleanup_pending",
|
|
1041
|
+
phase: "artifacts",
|
|
1042
|
+
error: new SessionDeleteVerificationError("artifacts", `Exact artifact detach retained: ${detach.code}`),
|
|
1043
|
+
artifactsIdentity,
|
|
1044
|
+
detachedArtifactsPath: detach.detachedPath,
|
|
1045
|
+
artifactsTree,
|
|
1046
|
+
...(detach.retainedSuccessorPath ? { retainedSuccessorPath: detach.retainedSuccessorPath } : {}),
|
|
1047
|
+
...(detach.retainedPlaceholderPath ? { retainedPlaceholderPath: detach.retainedPlaceholderPath } : {}),
|
|
1048
|
+
...(detach.retainedUnknownPath ? { retainedUnknownPath: detach.retainedUnknownPath } : {}),
|
|
1049
|
+
|
|
1050
|
+
transcriptIdentity,
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
956
1053
|
const removal = removeDirectoryTreeExact(detach.detachedPath, artifactsTree);
|
|
957
1054
|
if (!removal.ok) {
|
|
958
1055
|
const retainedRoot = removal.detachedPath ?? detach.detachedPath;
|
|
@@ -971,6 +1068,15 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
971
1068
|
artifactsIdentity,
|
|
972
1069
|
detachedArtifactsPath: retainedRoot,
|
|
973
1070
|
artifactsTree,
|
|
1071
|
+
...((removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath)
|
|
1072
|
+
? { retainedSuccessorPath: removal.retainedSuccessorPath ?? retainedArtifactsSuccessorPath }
|
|
1073
|
+
: {}),
|
|
1074
|
+
...((removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath)
|
|
1075
|
+
? { retainedPlaceholderPath: removal.retainedPlaceholderPath ?? retainedArtifactsPlaceholderPath }
|
|
1076
|
+
: {}),
|
|
1077
|
+
...((removal.retainedUnknownPath ?? retainedArtifactsUnknownPath)
|
|
1078
|
+
? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
|
|
1079
|
+
: {}),
|
|
974
1080
|
transcriptIdentity,
|
|
975
1081
|
};
|
|
976
1082
|
}
|
|
@@ -1010,13 +1116,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1010
1116
|
});
|
|
1011
1117
|
if (!deletion.ok) {
|
|
1012
1118
|
const error = exactUnlinkFailure(deletion);
|
|
1013
|
-
|
|
1119
|
+
const retainedAuthority =
|
|
1120
|
+
deletion.detachedPath ||
|
|
1121
|
+
deletion.retainedSuccessorPath ||
|
|
1122
|
+
deletion.retainedPlaceholderPath ||
|
|
1123
|
+
deletion.retainedUnknownPath;
|
|
1124
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1014
1125
|
return {
|
|
1015
1126
|
kind: "cleanup_pending",
|
|
1016
1127
|
phase: "transcript",
|
|
1017
1128
|
error,
|
|
1018
1129
|
transcriptIdentity,
|
|
1019
1130
|
detachedTranscriptPath: deletion.detachedPath ?? detachedTranscriptPath,
|
|
1131
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1132
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1133
|
+
: {}),
|
|
1134
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1135
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1136
|
+
: {}),
|
|
1137
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1138
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1139
|
+
: {}),
|
|
1020
1140
|
};
|
|
1021
1141
|
}
|
|
1022
1142
|
return { kind: "deleted" };
|
|
@@ -1058,13 +1178,27 @@ export class FileSessionStorage implements SessionStorage {
|
|
|
1058
1178
|
});
|
|
1059
1179
|
if (!deletion.ok) {
|
|
1060
1180
|
const error = exactUnlinkFailure(deletion);
|
|
1061
|
-
|
|
1181
|
+
const retainedAuthority =
|
|
1182
|
+
deletion.detachedPath ||
|
|
1183
|
+
deletion.retainedSuccessorPath ||
|
|
1184
|
+
deletion.retainedPlaceholderPath ||
|
|
1185
|
+
deletion.retainedUnknownPath;
|
|
1186
|
+
if ((error.kind === "identity" || error.kind === "symlink") && !retainedAuthority) throw error;
|
|
1062
1187
|
return {
|
|
1063
1188
|
kind: "cleanup_pending",
|
|
1064
1189
|
phase: "transcript",
|
|
1065
1190
|
error,
|
|
1066
1191
|
transcriptIdentity,
|
|
1067
1192
|
detachedTranscriptPath: deletion.detachedPath,
|
|
1193
|
+
...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
|
|
1194
|
+
? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
|
|
1195
|
+
: {}),
|
|
1196
|
+
...((deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath)
|
|
1197
|
+
? { retainedPlaceholderPath: deletion.retainedPlaceholderPath ?? retainedTranscriptPlaceholderPath }
|
|
1198
|
+
: {}),
|
|
1199
|
+
...((deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath)
|
|
1200
|
+
? { retainedUnknownPath: deletion.retainedUnknownPath ?? retainedTranscriptUnknownPath }
|
|
1201
|
+
: {}),
|
|
1068
1202
|
};
|
|
1069
1203
|
}
|
|
1070
1204
|
return { kind: "deleted" };
|
|
@@ -1353,6 +1487,12 @@ export class MemorySessionStorage implements SessionStorage {
|
|
|
1353
1487
|
}
|
|
1354
1488
|
return files;
|
|
1355
1489
|
}
|
|
1490
|
+
listFilesByMtime(dir: string, pattern: string): Promise<Array<{ path: string; mtimeMs: number }>> {
|
|
1491
|
+
const files = this.listFilesSync(dir, pattern)
|
|
1492
|
+
.map(path => ({ path, mtimeMs: this.statSync(path).mtimeMs }))
|
|
1493
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
1494
|
+
return Promise.resolve(files);
|
|
1495
|
+
}
|
|
1356
1496
|
listFilesStrictSync(dir: string, pattern: string): string[] {
|
|
1357
1497
|
// In-memory scan never suppresses; identical to the display scan.
|
|
1358
1498
|
return this.listFilesSync(dir, pattern);
|
|
@@ -114,8 +114,12 @@ export interface TruncationResult {
|
|
|
114
114
|
elidedLines?: number;
|
|
115
115
|
lastLinePartial?: boolean;
|
|
116
116
|
firstLineExceedsLimit?: boolean;
|
|
117
|
+
lastLineExceedsLimit?: boolean;
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/** Direction vocabulary used by callers that select which end of content to retain. */
|
|
121
|
+
export type TruncationDirection = "head" | "last" | "both";
|
|
122
|
+
|
|
119
123
|
export interface TruncationOptions {
|
|
120
124
|
/** Maximum number of lines (default: 3000) */
|
|
121
125
|
maxLines?: number;
|
|
@@ -131,6 +135,8 @@ export interface TruncationOptions {
|
|
|
131
135
|
* window receives `maxLines - maxHeadLines`. Default `floor(maxLines/2)`.
|
|
132
136
|
*/
|
|
133
137
|
maxHeadLines?: number;
|
|
138
|
+
/** Direction to dispatch through truncateContent (head, last, or both). */
|
|
139
|
+
direction?: TruncationDirection;
|
|
134
140
|
}
|
|
135
141
|
|
|
136
142
|
/** Result from byte-level truncation helpers. */
|
|
@@ -452,6 +458,7 @@ export function truncateTail(content: string, options: TruncationOptions = {}):
|
|
|
452
458
|
outputBytes: tail.bytes,
|
|
453
459
|
lastLinePartial: true,
|
|
454
460
|
firstLineExceedsLimit: false,
|
|
461
|
+
lastLineExceedsLimit: true,
|
|
455
462
|
};
|
|
456
463
|
}
|
|
457
464
|
break;
|
|
@@ -474,6 +481,7 @@ export function truncateTail(content: string, options: TruncationOptions = {}):
|
|
|
474
481
|
outputBytes: tail.bytes,
|
|
475
482
|
lastLinePartial: true,
|
|
476
483
|
firstLineExceedsLimit: false,
|
|
484
|
+
lastLineExceedsLimit: true,
|
|
477
485
|
};
|
|
478
486
|
}
|
|
479
487
|
break;
|
|
@@ -516,14 +524,129 @@ export function formatMiddleElisionMarker(elidedLines: number, elidedBytes: numb
|
|
|
516
524
|
}
|
|
517
525
|
|
|
518
526
|
/**
|
|
519
|
-
*
|
|
527
|
+
* A retained source segment returned by {@link truncateMiddleWindows}.
|
|
528
|
+
* Line coordinates are 1-indexed and inclusive.
|
|
529
|
+
*/
|
|
530
|
+
export type ReadSegment =
|
|
531
|
+
| {
|
|
532
|
+
kind: "lines";
|
|
533
|
+
content: string;
|
|
534
|
+
lines: number;
|
|
535
|
+
bytes: number;
|
|
536
|
+
origin: { startLine: number; endLine: number };
|
|
537
|
+
lastLinePartial: false;
|
|
538
|
+
}
|
|
539
|
+
| {
|
|
540
|
+
kind: "partial-line";
|
|
541
|
+
content: string;
|
|
542
|
+
lines: 1;
|
|
543
|
+
bytes: number;
|
|
544
|
+
origin: { startLine: number; endLine: number };
|
|
545
|
+
sourceLineBytes: number;
|
|
546
|
+
lastLinePartial: true;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
/** The actual windows retained by middle truncation. */
|
|
550
|
+
export interface ReadWindow {
|
|
551
|
+
kind: "full" | "head-only" | "tail-only" | "middle";
|
|
552
|
+
head?: ReadSegment;
|
|
553
|
+
tail?: ReadSegment;
|
|
554
|
+
// With normalized head/tail line budgets, an overlap cannot occur: a
|
|
555
|
+
// source that can fit both windows returns `full`, otherwise their kept
|
|
556
|
+
// line counts sum to less than the source. Retain this defensive value for
|
|
557
|
+
// custom/future budgets and wire compatibility.
|
|
558
|
+
overlap: "disjoint" | "adjacent" | "overlapping";
|
|
559
|
+
elidedLines: number;
|
|
560
|
+
elidedBytes: number;
|
|
561
|
+
totalLines: number;
|
|
562
|
+
totalBytes: number;
|
|
563
|
+
/** @deprecated Non-enumerable compatibility reason for older consumers. */
|
|
564
|
+
truncatedBy?: "lines" | "bytes" | "middle";
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function outputLineCount(result: TruncationResult): number {
|
|
568
|
+
return result.outputLines ?? (result.content.length === 0 ? 0 : countNewlines(result.content) + 1);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function outputByteCount(result: TruncationResult): number {
|
|
572
|
+
return result.outputBytes ?? Buffer.byteLength(result.content, "utf-8");
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function lastSourceLineBytes(content: string): number {
|
|
576
|
+
const lineStart = content.lastIndexOf(NL) + 1;
|
|
577
|
+
return Buffer.byteLength(content.slice(lineStart), "utf-8");
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function makeReadSegment(
|
|
581
|
+
result: TruncationResult,
|
|
582
|
+
startLine: number,
|
|
583
|
+
totalLines: number,
|
|
584
|
+
sourceContent: string = result.content,
|
|
585
|
+
): ReadSegment | undefined {
|
|
586
|
+
const lines = outputLineCount(result);
|
|
587
|
+
if (lines <= 0) return undefined;
|
|
588
|
+
const bytes = outputByteCount(result);
|
|
589
|
+
const endLine = Math.min(totalLines, startLine + lines - 1);
|
|
590
|
+
if (result.lastLinePartial) {
|
|
591
|
+
const partialLine = endLine;
|
|
592
|
+
return {
|
|
593
|
+
kind: "partial-line",
|
|
594
|
+
content: result.content,
|
|
595
|
+
lines: 1,
|
|
596
|
+
bytes,
|
|
597
|
+
origin: { startLine: partialLine, endLine: partialLine },
|
|
598
|
+
sourceLineBytes: lastSourceLineBytes(sourceContent),
|
|
599
|
+
lastLinePartial: true,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
return {
|
|
603
|
+
kind: "lines",
|
|
604
|
+
content: result.content,
|
|
605
|
+
lines,
|
|
606
|
+
bytes,
|
|
607
|
+
origin: { startLine, endLine },
|
|
608
|
+
lastLinePartial: false,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function withMiddleWindowReason(windows: ReadWindow, truncatedBy: "lines" | "bytes" | "middle"): ReadWindow {
|
|
613
|
+
Object.defineProperty(windows, "truncatedBy", { value: truncatedBy, enumerable: false });
|
|
614
|
+
return windows;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function fullReadWindow(
|
|
618
|
+
content: string,
|
|
619
|
+
totalLines: number,
|
|
620
|
+
totalBytes: number,
|
|
621
|
+
overlap: ReadWindow["overlap"] = "disjoint",
|
|
622
|
+
): ReadWindow {
|
|
623
|
+
return {
|
|
624
|
+
kind: "full",
|
|
625
|
+
head: {
|
|
626
|
+
kind: "lines",
|
|
627
|
+
content,
|
|
628
|
+
lines: totalLines,
|
|
629
|
+
bytes: totalBytes,
|
|
630
|
+
origin: { startLine: 1, endLine: totalLines },
|
|
631
|
+
lastLinePartial: false,
|
|
632
|
+
},
|
|
633
|
+
overlap,
|
|
634
|
+
elidedLines: 0,
|
|
635
|
+
elidedBytes: 0,
|
|
636
|
+
totalLines,
|
|
637
|
+
totalBytes,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Truncate content while exposing the exact retained head/tail windows.
|
|
520
643
|
*
|
|
521
|
-
* The
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
*
|
|
644
|
+
* The budget split and fallback ordering intentionally mirror truncateMiddle.
|
|
645
|
+
* The overlap classification is performed before resolving an overlap to a
|
|
646
|
+
* full window, so callers can distinguish disjoint, adjacent and overlapping
|
|
647
|
+
* candidate windows even when no marker is needed.
|
|
525
648
|
*/
|
|
526
|
-
export function
|
|
649
|
+
export function truncateMiddleWindows(content: string, options: TruncationOptions = {}): ReadWindow {
|
|
527
650
|
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
528
651
|
const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
529
652
|
const headBytes = options.maxHeadBytes ?? Math.floor(maxBytes / 2);
|
|
@@ -533,57 +656,153 @@ export function truncateMiddle(content: string, options: TruncationOptions = {})
|
|
|
533
656
|
|
|
534
657
|
const totalBytes = Buffer.byteLength(content, "utf-8");
|
|
535
658
|
const totalLines = countNewlines(content) + 1;
|
|
659
|
+
if (totalBytes <= maxBytes && totalLines <= maxLines) return fullReadWindow(content, totalLines, totalBytes);
|
|
660
|
+
|
|
661
|
+
const oneSided = (kind: "head-only" | "tail-only", result: TruncationResult, startLine: number): ReadWindow => {
|
|
662
|
+
const lines = outputLineCount(result);
|
|
663
|
+
const bytes = outputByteCount(result);
|
|
664
|
+
const segment = makeReadSegment(result, startLine, totalLines, content);
|
|
665
|
+
|
|
666
|
+
const windows: ReadWindow = {
|
|
667
|
+
kind,
|
|
668
|
+
...(kind === "head-only" ? (segment ? { head: segment } : {}) : segment ? { tail: segment } : {}),
|
|
669
|
+
overlap: "disjoint",
|
|
670
|
+
elidedLines: Math.max(0, totalLines - lines),
|
|
671
|
+
elidedBytes: Math.max(0, totalBytes - bytes),
|
|
672
|
+
totalLines,
|
|
673
|
+
totalBytes,
|
|
674
|
+
};
|
|
675
|
+
return withMiddleWindowReason(windows, result.truncatedBy === "lines" ? "lines" : "bytes");
|
|
676
|
+
};
|
|
536
677
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// Degenerate budgets → fall back to one-sided truncation.
|
|
678
|
+
// Keep this order identical to truncateMiddle: degenerate budgets are
|
|
679
|
+
// resolved before either one-sided truncator is called.
|
|
542
680
|
if (headBytes <= 0 || headLines <= 0) {
|
|
543
|
-
|
|
681
|
+
const tail = truncateTail(content, { maxBytes: tailBytes || maxBytes, maxLines: tailLines || maxLines });
|
|
682
|
+
return oneSided("tail-only", tail, totalLines - outputLineCount(tail) + 1);
|
|
544
683
|
}
|
|
545
684
|
if (tailBytes <= 0 || tailLines <= 0) {
|
|
546
|
-
|
|
685
|
+
const head = truncateHead(content, { maxBytes: headBytes, maxLines: headLines });
|
|
686
|
+
return oneSided("head-only", head, 1);
|
|
547
687
|
}
|
|
548
688
|
|
|
549
689
|
const head = truncateHead(content, { maxBytes: headBytes, maxLines: headLines });
|
|
550
690
|
const tail = truncateTail(content, { maxBytes: tailBytes, maxLines: tailLines });
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
const
|
|
554
|
-
const
|
|
555
|
-
const tailBytesKept = tail.outputBytes ?? Buffer.byteLength(tail.content, "utf-8");
|
|
691
|
+
const headLinesKept = outputLineCount(head);
|
|
692
|
+
const tailLinesKept = outputLineCount(tail);
|
|
693
|
+
const headBytesKept = outputByteCount(head);
|
|
694
|
+
const tailBytesKept = outputByteCount(tail);
|
|
556
695
|
|
|
557
696
|
// Head unusable (first line exceeds budget) → tail-only.
|
|
558
|
-
if (headLinesKept === 0 || head.firstLineExceedsLimit)
|
|
697
|
+
if (headLinesKept === 0 || head.firstLineExceedsLimit) {
|
|
698
|
+
return oneSided("tail-only", tail, totalLines - tailLinesKept + 1);
|
|
699
|
+
}
|
|
559
700
|
// Tail unusable → head-only.
|
|
560
|
-
if (tailLinesKept === 0) return head;
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
701
|
+
if (tailLinesKept === 0) return oneSided("head-only", head, 1);
|
|
702
|
+
|
|
703
|
+
const headEnd = headLinesKept;
|
|
704
|
+
const tailStart = totalLines - tailLinesKept + 1;
|
|
705
|
+
const overlap: ReadWindow["overlap"] =
|
|
706
|
+
headEnd + 1 < tailStart ? "disjoint" : headEnd + 1 === tailStart ? "adjacent" : "overlapping";
|
|
707
|
+
const tailPartial = tail.lastLinePartial === true;
|
|
708
|
+
|
|
709
|
+
// Adjacent/overlapping complete windows cover the whole source. Return one
|
|
710
|
+
// full segment so renderers never insert a marker for an overlap.
|
|
711
|
+
if (headLinesKept + tailLinesKept >= totalLines && !tailPartial) {
|
|
712
|
+
return fullReadWindow(content, totalLines, totalBytes, overlap);
|
|
564
713
|
}
|
|
565
714
|
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
const
|
|
572
|
-
|
|
715
|
+
const headWindow = makeReadSegment(head, 1, totalLines, content);
|
|
716
|
+
const tailWindow = makeReadSegment(tail, tailStart, totalLines, content);
|
|
717
|
+
if (!headWindow || !tailWindow) return fullReadWindow(content, totalLines, totalBytes, overlap);
|
|
718
|
+
|
|
719
|
+
const partialSourceBytes = tailWindow.kind === "partial-line" ? tailWindow.sourceLineBytes : tailWindow.bytes;
|
|
720
|
+
const elidedLines = tailPartial
|
|
721
|
+
? Math.max(0, tailStart - headEnd - 1)
|
|
722
|
+
: Math.max(0, totalLines - headLinesKept - tailLinesKept);
|
|
723
|
+
const elidedBytes = tailPartial
|
|
724
|
+
? Math.max(0, totalBytes - headBytesKept - partialSourceBytes)
|
|
725
|
+
: Math.max(0, totalBytes - headBytesKept - tailBytesKept);
|
|
726
|
+
return withMiddleWindowReason(
|
|
727
|
+
{
|
|
728
|
+
kind: "middle",
|
|
729
|
+
head: headWindow,
|
|
730
|
+
tail: tailWindow,
|
|
731
|
+
overlap,
|
|
732
|
+
elidedLines,
|
|
733
|
+
elidedBytes,
|
|
734
|
+
totalLines,
|
|
735
|
+
totalBytes,
|
|
736
|
+
},
|
|
737
|
+
"middle",
|
|
738
|
+
);
|
|
739
|
+
}
|
|
573
740
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
741
|
+
/**
|
|
742
|
+
* Truncate content keeping a head window and a tail window, eliding the middle.
|
|
743
|
+
* The composed return shape remains field-for-field compatible with the
|
|
744
|
+
* historical implementation; callers that need coordinates use
|
|
745
|
+
* truncateMiddleWindows directly.
|
|
746
|
+
*/
|
|
747
|
+
export function truncateMiddle(content: string, options: TruncationOptions = {}): TruncationResult {
|
|
748
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
749
|
+
const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
|
|
750
|
+
const headBytes = options.maxHeadBytes ?? Math.floor(maxBytes / 2);
|
|
751
|
+
const tailBytes = Math.max(0, maxBytes - headBytes);
|
|
752
|
+
const headLines = options.maxHeadLines ?? Math.max(1, Math.floor(maxLines / 2));
|
|
753
|
+
const tailLines = Math.max(0, maxLines - headLines);
|
|
754
|
+
const windows = truncateMiddleWindows(content, options);
|
|
755
|
+
|
|
756
|
+
switch (windows.kind) {
|
|
757
|
+
case "full":
|
|
758
|
+
return noTruncResult(content, windows.totalLines, windows.totalBytes);
|
|
759
|
+
case "tail-only":
|
|
760
|
+
return truncateTail(content, { maxBytes: tailBytes || maxBytes, maxLines: tailLines || maxLines });
|
|
761
|
+
case "head-only":
|
|
762
|
+
return truncateHead(content, { maxBytes: headBytes, maxLines: headLines });
|
|
763
|
+
case "middle": {
|
|
764
|
+
const head = windows.head;
|
|
765
|
+
const tail = windows.tail;
|
|
766
|
+
if (!head || !tail) return noTruncResult(content, windows.totalLines, windows.totalBytes);
|
|
767
|
+
if (head.lines + tail.lines >= windows.totalLines)
|
|
768
|
+
return noTruncResult(content, windows.totalLines, windows.totalBytes);
|
|
769
|
+
const legacyElidedLines = Math.max(0, windows.totalLines - head.lines - tail.lines);
|
|
770
|
+
const legacyElidedBytes =
|
|
771
|
+
tail.kind === "partial-line"
|
|
772
|
+
? Math.max(0, windows.totalBytes - head.bytes - tail.bytes)
|
|
773
|
+
: windows.elidedBytes;
|
|
774
|
+
const marker = formatMiddleElisionMarker(legacyElidedLines, legacyElidedBytes);
|
|
775
|
+
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
776
|
+
return {
|
|
777
|
+
content: `${head.content}\n${marker}\n${tail.content}`,
|
|
778
|
+
truncated: true,
|
|
779
|
+
truncatedBy: "middle",
|
|
780
|
+
totalLines: windows.totalLines,
|
|
781
|
+
totalBytes: windows.totalBytes,
|
|
782
|
+
outputLines: head.lines + tail.lines + 1,
|
|
783
|
+
outputBytes: head.bytes + tail.bytes + markerBytes + 2,
|
|
784
|
+
elidedLines: legacyElidedLines,
|
|
785
|
+
elidedBytes: legacyElidedBytes,
|
|
786
|
+
lastLinePartial: tail.lastLinePartial,
|
|
787
|
+
firstLineExceedsLimit: false,
|
|
788
|
+
...(tail.lastLinePartial ? { lastLineExceedsLimit: true } : {}),
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Dispatch truncation using the caller-facing direction vocabulary.
|
|
796
|
+
*/
|
|
797
|
+
export function truncateContent(content: string, options: TruncationOptions = {}): TruncationResult {
|
|
798
|
+
switch (options.direction) {
|
|
799
|
+
case "last":
|
|
800
|
+
return truncateTail(content, options);
|
|
801
|
+
case "both":
|
|
802
|
+
return truncateMiddle(content, options);
|
|
803
|
+
default:
|
|
804
|
+
return truncateHead(content, options);
|
|
805
|
+
}
|
|
587
806
|
}
|
|
588
807
|
|
|
589
808
|
// =============================================================================
|