@sayknow-cli/coding-agent 0.3.16 → 0.4.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 +52 -0
- package/package.json +133 -116
- package/scripts/build-sdk-package-smoke.ts +132 -0
- package/scripts/capture-notifications-settings-showcase.ts +435 -0
- package/scripts/compile-args.ts +5 -2
- package/scripts/g005-daemon-smoke.ts +4 -5
- package/scripts/g011-daemon-path-smoke.ts +5 -5
- package/scripts/generate-hotkeys-docs.ts +74 -0
- package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
- package/scripts/generate-sdk-operation-inventory.ts +914 -0
- package/scripts/generate-telegram-baseline-manifest.ts +128 -0
- package/scripts/manifest-schema.ts +124 -0
- package/scripts/run-test-manifest.ts +153 -0
- package/scripts/run-threat-model-check.ts +217 -0
- package/scripts/verify-skc-sdk-canonicalization.ts +3647 -0
- package/scripts/verify-skc-sdk-rename.ts +90 -0
- package/src/append-only-mode.ts +1 -1
- package/src/async/job-manager.ts +455 -69
- package/src/cli/args.ts +43 -12
- package/src/cli/auth-gateway-cli.ts +2 -2
- package/src/cli/config-cli.ts +77 -3
- package/src/cli/daemon-cli.ts +63 -14
- package/src/cli/launch-disposition.ts +42 -0
- package/src/cli/list-models.ts +1 -1
- package/src/cli/notify-cli.ts +373 -283
- package/src/cli/session-picker.ts +20 -3
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/worktree-cli.ts +9 -287
- package/src/cli.ts +31 -11
- package/src/commands/config.ts +1 -1
- package/src/commands/daemon.ts +50 -6
- package/src/commands/gc.ts +11 -1
- package/src/commands/harness.ts +127 -142
- package/src/commands/launch.ts +11 -6
- package/src/commands/mcp-serve.ts +97 -13
- package/src/commands/mcp.ts +2 -2
- package/src/commands/notify.ts +44 -7
- package/src/commands/sdk.ts +453 -0
- package/src/commands/ultragoal.ts +4 -1
- package/src/commands/worktree.ts +10 -53
- package/src/commit/agentic/agent.ts +1 -0
- package/src/config/atomic-yaml-patch.ts +490 -0
- package/src/config/file-lock.ts +105 -43
- package/src/config/keybindings.ts +64 -20
- package/src/config/model-auth.ts +9 -0
- package/src/config/model-bindings-applier.ts +139 -0
- package/src/config/model-discovery-manager.ts +224 -0
- package/src/config/model-equivalence.ts +66 -0
- package/src/config/model-profile-activation.ts +233 -86
- package/src/config/model-profiles.ts +52 -49
- package/src/config/model-registry.ts +5 -4
- package/src/config/model-resolver.ts +487 -410
- package/src/config/model-selector-value.ts +50 -0
- package/src/config/models-config-schema.ts +17 -23
- package/src/config/settings-schema.ts +372 -21
- package/src/config/settings.ts +1120 -182
- package/src/coordinator-mcp/model-preset.ts +74 -20
- package/src/coordinator-mcp/policy.ts +76 -11
- package/src/coordinator-mcp/question-gate-codec.ts +274 -0
- package/src/coordinator-mcp/question-state.ts +778 -0
- package/src/coordinator-mcp/server.ts +3120 -3111
- package/src/daemon/builtin.ts +8 -5
- package/src/daemon/control-types.ts +1 -1
- package/src/daemon/runtime.ts +2 -2
- package/src/debug/index.ts +1 -1
- package/src/defaults/skc/skills/team/SKILL.md +27 -13
- package/src/defaults/skc/skills/ultragoal/SKILL.md +0 -2
- package/src/discovery/helpers.ts +1 -50
- package/src/discovery/mcp-json.ts +327 -20
- package/src/edit/index.ts +64 -0
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/env.ts +26 -0
- package/src/eval/py/index.ts +2 -2
- package/src/eval/py/kernel.ts +5 -3
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +52 -18
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/compact-handler.ts +3 -3
- package/src/extensibility/extensions/get-commands-handler.ts +2 -2
- package/src/extensibility/extensions/loader.ts +77 -5
- package/src/extensibility/extensions/runner.ts +124 -7
- package/src/extensibility/extensions/types.ts +215 -18
- package/src/extensibility/hooks/runner.ts +2 -2
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/extensibility/runtime-skill-discovery.ts +33 -13
- package/src/extensibility/shared-events.ts +2 -0
- package/src/extensibility/skc-plugins/installer.ts +16 -16
- package/src/extensibility/slash-commands.ts +3 -3
- package/src/goals/continuation-timeout-guard.ts +79 -0
- package/src/goals/runtime.ts +7 -5
- package/src/goals/state.ts +18 -0
- package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
- package/src/harness-control-plane/operate.ts +9 -9
- package/src/harness-control-plane/owner.ts +341 -41
- package/src/harness-control-plane/phase-rollup.ts +4 -7
- package/src/harness-control-plane/receipt-ingest.ts +5 -0
- package/src/harness-control-plane/receipt-spool.ts +51 -7
- package/src/harness-control-plane/receipts.ts +0 -1
- package/src/harness-control-plane/sdk-transport.ts +395 -0
- package/src/harness-control-plane/session-lease.ts +12 -130
- package/src/harness-control-plane/session-transport.ts +94 -0
- package/src/harness-control-plane/types.ts +1 -1
- package/src/hindsight/backend.ts +26 -27
- package/src/hindsight/client.ts +65 -2
- package/src/hindsight/state.ts +101 -32
- package/src/hooks/skill-state.ts +17 -11
- package/src/i18n/messages/en.ts +1 -0
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +44 -37
- package/src/internal-urls/local-protocol.ts +420 -18
- package/src/lsp/client.ts +43 -7
- package/src/lsp/config.ts +145 -26
- package/src/lsp/index.ts +7 -91
- package/src/lsp/lspmux.ts +39 -10
- package/src/lsp/path-trust.ts +81 -0
- package/src/main.ts +451 -236
- package/src/memories/index.ts +1 -0
- package/src/memories/storage.ts +6 -2
- package/src/modes/DESIGN.md +258 -0
- package/src/modes/acp/acp-agent.ts +1236 -2612
- package/src/modes/acp/acp-client-bridge.ts +2 -20
- package/src/modes/acp/acp-event-mapper.ts +18 -2
- package/src/modes/acp/acp-mode.ts +8 -36
- package/src/modes/acp/permission-mode.ts +25 -0
- package/src/modes/acp/startup-options.ts +6 -0
- package/src/modes/action-registry.ts +165 -0
- package/src/modes/bridge/bridge-mode.ts +6 -4
- package/src/modes/components/agent-dashboard.ts +37 -17
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/chrome.ts +40 -0
- package/src/modes/components/command-palette.ts +168 -0
- package/src/modes/components/custom-editor.ts +105 -33
- package/src/modes/components/custom-provider-wizard.ts +77 -15
- package/src/modes/components/index.ts +2 -1
- package/src/modes/components/model-selector.ts +93 -35
- package/src/modes/components/notifications-settings-editor.ts +1403 -0
- package/src/modes/components/pet-capability.ts +115 -0
- package/src/modes/components/pet-selector.ts +15 -23
- package/src/modes/components/plan-preview-overlay.ts +295 -0
- package/src/modes/components/queue-mode-selector.ts +10 -27
- package/src/modes/components/queue-pane.ts +79 -0
- package/src/modes/components/read-tool-group.ts +13 -0
- package/src/modes/components/sayknow-pet-widget.ts +169 -20
- package/src/modes/components/session-observer-overlay.ts +213 -775
- package/src/modes/components/sessions-dashboard.ts +181 -0
- package/src/modes/components/settings-selector.ts +89 -12
- package/src/modes/components/show-images-selector.ts +10 -24
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +1 -969
- package/src/modes/components/tasks-pane.ts +73 -0
- package/src/modes/components/theme-selector.ts +11 -31
- package/src/modes/components/tool-execution.ts +22 -0
- package/src/modes/components/tool-status-header.ts +1056 -0
- package/src/modes/components/tool-transcript-format.ts +393 -0
- package/src/modes/components/transcript-viewer-overlay.ts +446 -0
- package/src/modes/components/welcome.ts +4 -4
- package/src/modes/controllers/btw-controller.ts +1 -0
- package/src/modes/controllers/command-controller.ts +25 -17
- package/src/modes/controllers/event-controller.ts +14 -18
- package/src/modes/controllers/extension-ui-controller.ts +441 -37
- package/src/modes/controllers/goal-mode-controller.ts +493 -0
- package/src/modes/controllers/input-controller.ts +889 -263
- package/src/modes/controllers/mode-gate.ts +24 -0
- package/src/modes/controllers/plan-mode-controller.ts +519 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +957 -54
- package/src/modes/controllers/stt-controller.ts +125 -0
- package/src/modes/index.ts +0 -22
- package/src/modes/interactive-mode.ts +376 -1130
- package/src/modes/jobs-observer.ts +3 -0
- package/src/modes/prompt-action-autocomplete.ts +7 -0
- package/src/modes/rpc/rpc-mode.ts +3 -1
- package/src/modes/runtime-init.ts +320 -6
- package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -1
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +65 -111
- package/src/modes/shared/agent-wire/event-contract.ts +7 -8
- package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
- package/src/modes/shared/agent-wire/event-observation.ts +16 -7
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1331 -89
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +19 -17
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +407 -0
- package/src/modes/tasks-aggregator.ts +199 -0
- package/src/modes/transcript-item-registry.ts +229 -0
- package/src/modes/types.ts +63 -21
- package/src/modes/utils/ui-helpers.ts +17 -9
- package/src/notifications/index.ts +8 -2
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
- package/src/prompts/agent-fragments/restricted-bash.md +1 -0
- package/src/prompts/agents/architect.md +3 -8
- package/src/prompts/agents/critic.md +3 -8
- package/src/prompts/agents/executor.md +3 -0
- package/src/prompts/agents/planner.md +4 -12
- package/src/prompts/system/custom-system-prompt.md +2 -24
- package/src/prompts/system/plan-mode-approved.md +8 -0
- package/src/prompts/system/system-prompt.md +18 -94
- package/src/prompts/tools/bash.md +4 -4
- package/src/prompts/tools/browser.md +15 -52
- package/src/prompts/tools/find.md +2 -2
- package/src/prompts/tools/hashline.md +13 -55
- package/src/prompts/tools/lsp.md +3 -3
- package/src/prompts/tools/read.md +3 -6
- package/src/prompts/tools/search-tool-bm25.md +0 -2
- package/src/prompts/tools/search.md +2 -4
- package/src/prompts/tools/skill-discovery.md +1 -2
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-mcp/client.ts +94 -19
- package/src/runtime-mcp/config.ts +27 -15
- package/src/runtime-mcp/discoverable-tool-metadata.ts +16 -13
- package/src/runtime-mcp/manager.ts +359 -106
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/runtime-mcp/oauth-flow.ts +42 -12
- package/src/runtime-mcp/transports/http.ts +49 -19
- package/src/runtime-mcp/transports/stdio.ts +47 -28
- package/src/runtime-mcp/types.ts +10 -0
- package/src/sdk/acp/adapter.ts +418 -0
- package/src/sdk/acp/index.ts +2 -0
- package/src/sdk/broker/broker.ts +877 -0
- package/src/sdk/broker/discovery.ts +238 -0
- package/src/sdk/broker/ensure.ts +393 -0
- package/src/sdk/broker/identity.ts +42 -0
- package/src/sdk/broker/index.ts +8 -0
- package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
- package/src/sdk/broker/internal-source.bunfig.toml +1 -0
- package/src/sdk/broker/internal.ts +10 -0
- package/src/sdk/broker/lifecycle-codec.ts +11 -0
- package/src/sdk/broker/lifecycle-ledger.ts +583 -0
- package/src/sdk/broker/lifecycle.ts +3413 -0
- package/src/sdk/broker/process-incarnation.ts +151 -0
- package/src/sdk/broker/runtime.ts +164 -0
- package/src/sdk/broker/session-index.ts +678 -0
- package/src/sdk/broker/state-version.ts +47 -0
- package/src/sdk/broker/transport.ts +125 -0
- package/src/sdk/bus/attachment-registry.ts +23 -0
- package/src/sdk/bus/chat-adapters.ts +147 -0
- package/src/sdk/bus/chat-command-policy.ts +107 -0
- package/src/sdk/bus/chat-daemon-cli.ts +278 -0
- package/src/sdk/bus/chat-daemon-control.ts +1101 -0
- package/src/sdk/bus/chat-daemon-runtime.ts +527 -0
- package/src/sdk/bus/chat-effect-journal.ts +363 -0
- package/src/sdk/bus/config-commands.ts +192 -0
- package/src/sdk/bus/config.ts +568 -0
- package/src/sdk/bus/conversation-store.ts +367 -0
- package/src/sdk/bus/daemon-paths.ts +34 -0
- package/src/sdk/bus/discord-conversation.ts +85 -0
- package/src/sdk/bus/discord-daemon.ts +2292 -0
- package/src/sdk/bus/discord-live-provider.ts +521 -0
- package/src/sdk/bus/discord-provider.ts +56 -0
- package/src/sdk/bus/engine.ts +100 -0
- package/src/sdk/bus/helpers.ts +135 -0
- package/src/sdk/bus/html-format.ts +518 -0
- package/src/sdk/bus/index.ts +4961 -0
- package/src/sdk/bus/lifecycle-commands.ts +291 -0
- package/src/sdk/bus/lifecycle-control-runtime.ts +1409 -0
- package/src/sdk/bus/lifecycle-orchestrator.ts +458 -0
- package/src/sdk/bus/managed-daemon.ts +163 -0
- package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
- package/src/sdk/bus/natives-augment.d.ts +63 -0
- package/src/sdk/bus/notification-orchestration.ts +535 -0
- package/src/sdk/bus/notification-service.ts +1174 -0
- package/src/sdk/bus/operator-runtime.ts +171 -0
- package/src/sdk/bus/rate-limit-pool.ts +276 -0
- package/src/sdk/bus/recent-activity.ts +279 -0
- package/src/sdk/bus/reply-sent-store.ts +134 -0
- package/src/sdk/bus/rich-document.ts +258 -0
- package/src/sdk/bus/rich-draft.ts +107 -0
- package/src/sdk/bus/rich-render.ts +135 -0
- package/src/sdk/bus/session-control.ts +439 -0
- package/src/sdk/bus/slack-conversation.ts +74 -0
- package/src/sdk/bus/slack-daemon.ts +2144 -0
- package/src/sdk/bus/slack-live-provider.ts +380 -0
- package/src/sdk/bus/slack-provider.ts +76 -0
- package/src/sdk/bus/telegram-cli.ts +194 -0
- package/src/sdk/bus/telegram-daemon-cli.ts +292 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +27 -0
- package/src/sdk/bus/telegram-daemon-control.ts +642 -0
- package/src/sdk/bus/telegram-daemon.ts +6675 -0
- package/src/sdk/bus/telegram-reference.ts +436 -0
- package/src/sdk/bus/telegram-setup.ts +663 -0
- package/src/sdk/bus/threaded-inbound.ts +141 -0
- package/src/sdk/bus/threaded-render.ts +294 -0
- package/src/sdk/bus/topic-registry.ts +364 -0
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/session-cli.ts +275 -0
- package/src/sdk/client/client.ts +1 -0
- package/src/sdk/client/discovery.ts +108 -0
- package/src/sdk/client/index.ts +2 -0
- package/src/sdk/host/control/dispatch.ts +388 -0
- package/src/sdk/host/control/index.ts +10 -0
- package/src/sdk/host/control/operations.ts +68 -0
- package/src/sdk/host/events.ts +91 -0
- package/src/sdk/host/host.ts +338 -0
- package/src/sdk/host/index.ts +4 -0
- package/src/sdk/host/query/cursor.ts +189 -0
- package/src/sdk/host/query/handlers.ts +531 -0
- package/src/sdk/host/query/index.ts +3 -0
- package/src/sdk/host/query/revision-store.ts +1149 -0
- package/src/sdk/host/reverse-leases.ts +325 -0
- package/src/sdk/host/types.ts +29 -0
- package/src/sdk/index.ts +14 -0
- package/src/sdk/lifecycle-session.ts +39 -0
- package/src/sdk/mcp/index.ts +1 -0
- package/src/sdk/mcp/server.ts +332 -0
- package/src/sdk/models.ts +168 -0
- package/src/sdk/protocol/adapter-validation.ts +48 -0
- package/src/sdk/protocol/operation-inventory.generated.json +4719 -0
- package/src/sdk/protocol/operation-registry.ts +312 -0
- package/src/sdk/session-directory.ts +141 -0
- package/src/sdk/session.ts +2869 -0
- package/src/sdk/startup-capability.ts +189 -0
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +5602 -2138
- package/src/session/agent-storage.ts +18 -0
- package/src/session/artifacts.ts +188 -23
- package/src/session/cache-economics.ts +25 -8
- package/src/session/context-estimation.ts +7 -2
- package/src/session/default-model-selection.ts +93 -0
- package/src/session/fallback-chain-controller.ts +186 -0
- package/src/session/internal/managed-session-scope.ts +2402 -0
- package/src/session/internal/managed-session-storage.ts +1559 -0
- package/src/session/messages.ts +91 -2
- package/src/session/session-manager-internal.ts +33 -0
- package/src/session/session-manager.ts +2578 -392
- package/src/session/session-storage.ts +550 -40
- package/src/session/streaming-output.ts +6 -0
- package/src/session/volatile-context-pruning.ts +78 -0
- package/src/session/yield-queue.ts +20 -0
- package/src/setup/credential-auto-import.ts +291 -35
- package/src/setup/hermes/templates/operator-instructions.v1.md +4 -6
- package/src/setup/provider-onboarding.ts +36 -61
- package/src/setup/provider-presets.json +5 -0
- package/src/skc-runtime/deep-interview-recorder.ts +70 -3
- package/src/skc-runtime/deep-interview-state.ts +441 -5
- package/src/skc-runtime/gc-render.ts +19 -1
- package/src/skc-runtime/gc-runtime.ts +68 -3
- package/src/skc-runtime/goal-mode-request.ts +55 -8
- package/src/skc-runtime/launch-worktree.ts +5 -5
- package/src/skc-runtime/linux-proc.ts +71 -0
- package/src/skc-runtime/managed-owner-admission.ts +352 -0
- package/src/skc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/skc-runtime/psmux-detect.ts +82 -4
- package/src/skc-runtime/ralplan-runtime.ts +74 -90
- package/src/skc-runtime/session-resolution.ts +18 -3
- package/src/skc-runtime/session-state-sidecar.ts +47 -138
- package/src/skc-runtime/state-argv.ts +292 -0
- package/src/skc-runtime/state-migrations.ts +1 -1
- package/src/skc-runtime/state-runtime.ts +387 -451
- package/src/skc-runtime/state-writer.ts +36 -40
- package/src/skc-runtime/team-launch.ts +319 -0
- package/src/skc-runtime/team-notify.ts +326 -0
- package/src/skc-runtime/team-runtime.ts +8 -2
- package/src/skc-runtime/team-store.ts +828 -0
- package/src/skc-runtime/team-workers.ts +962 -0
- package/src/skc-runtime/tmux-owner-isolation-cli.ts +55 -28
- package/src/skc-runtime/tmux-owner-isolation.ts +136 -11
- package/src/skc-runtime/tmux-sessions.ts +23 -34
- package/src/skc-runtime/ultragoal-change-set.ts +172 -0
- package/src/skc-runtime/ultragoal-evidence.ts +874 -0
- package/src/skc-runtime/ultragoal-guard.ts +136 -18
- package/src/skc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/skc-runtime/ultragoal-pipeline.ts +306 -0
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +102 -1
- package/src/skc-runtime/ultragoal-runtime.ts +325 -1322
- package/src/skc-runtime/windows-powershell-command.ts +58 -0
- package/src/skc-runtime/workflow-cli-common.ts +30 -0
- package/src/skc-runtime/workflow-manifest.generated.json +58 -0
- package/src/skc-runtime/workflow-manifest.ts +18 -0
- package/src/skill-state/active-state.ts +3 -13
- package/src/skill-state/workflow-mutation-guard.ts +661 -0
- package/src/skill-state/workflow-state-contract.ts +6 -41
- package/src/slash-commands/builtin-registry.ts +115 -41
- package/src/system-prompt.ts +37 -44
- package/src/task/agents.ts +19 -1
- package/src/task/executor.ts +223 -45
- package/src/task/index.ts +124 -56
- package/src/thinking.ts +21 -6
- package/src/tool-discovery/tool-index.ts +20 -134
- package/src/tools/ask-answer-registry.ts +16 -2
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/bash-allowed-prefixes.ts +79 -29
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +40 -2
- package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
- package/src/tools/browser/benchmark/detector-report.ts +157 -0
- package/src/tools/browser/benchmark/run-suite.ts +39 -0
- package/src/tools/browser/dead-tab-recovery.ts +78 -0
- package/src/tools/browser/launch.ts +127 -20
- package/src/tools/browser/profile-discovery.ts +63 -0
- package/src/tools/browser/profile-posture.ts +59 -0
- package/src/tools/browser/profile-reuse.ts +102 -0
- package/src/tools/browser/profile-warmup.ts +109 -0
- package/src/tools/browser/readable.ts +1 -1
- package/src/tools/browser/registry.ts +87 -11
- package/src/tools/browser/screenshot-format.ts +34 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +392 -142
- package/src/tools/browser/tab-worker-entry.ts +33 -14
- package/src/tools/browser/tab-worker-path-resolver.ts +67 -0
- package/src/tools/browser/tab-worker-smoke.ts +87 -0
- package/src/tools/browser/tab-worker.ts +8 -6
- package/src/tools/browser.ts +6 -0
- package/src/tools/computer.ts +10 -3
- package/src/tools/cron.ts +30 -16
- package/src/tools/fetch.ts +11 -4
- package/src/tools/index.ts +88 -32
- package/src/tools/output-meta.ts +19 -7
- package/src/tools/path-utils.ts +42 -35
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
- package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
- package/src/tools/read.ts +226 -65
- package/src/tools/resource-gc.ts +125 -36
- package/src/tools/search-tool-bm25.ts +11 -74
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent-render.ts +19 -8
- package/src/tools/subagent.ts +129 -34
- package/src/tools/telegram-send.ts +2 -2
- package/src/utils/event-bus.ts +9 -4
- package/src/utils/file-mentions.ts +4 -4
- package/src/utils/image-loading.ts +85 -34
- package/src/utils/image-resize.ts +16 -9
- package/src/utils/pasted-image-loading.ts +514 -0
- package/src/utils/pasted-image-path.ts +223 -98
- package/src/web/scrapers/html-to-markdown.ts +68 -0
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/types.ts +1 -68
- package/src/web/search/index.ts +3 -3
- package/src/web/search/providers/openai-compatible.ts +76 -23
- package/dist/types/append-only-mode.d.ts +0 -31
- package/dist/types/async/index.d.ts +0 -2
- package/dist/types/async/job-manager.d.ts +0 -328
- package/dist/types/async/support.d.ts +0 -2
- package/dist/types/autoresearch/dashboard.d.ts +0 -4
- package/dist/types/autoresearch/git.d.ts +0 -36
- package/dist/types/autoresearch/helpers.d.ts +0 -24
- package/dist/types/autoresearch/index.d.ts +0 -2
- package/dist/types/autoresearch/state.d.ts +0 -17
- package/dist/types/autoresearch/storage.d.ts +0 -142
- package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
- package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
- package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
- package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
- package/dist/types/autoresearch/types.d.ts +0 -154
- package/dist/types/build-metadata.d.ts +0 -7
- package/dist/types/capability/context-file.d.ts +0 -17
- package/dist/types/capability/extension-module.d.ts +0 -15
- package/dist/types/capability/extension.d.ts +0 -28
- package/dist/types/capability/fs.d.ts +0 -20
- package/dist/types/capability/hook.d.ts +0 -19
- package/dist/types/capability/index.d.ts +0 -80
- package/dist/types/capability/instruction.d.ts +0 -17
- package/dist/types/capability/mcp.d.ts +0 -49
- package/dist/types/capability/prompt.d.ts +0 -15
- package/dist/types/capability/rule.d.ts +0 -69
- package/dist/types/capability/settings.d.ts +0 -15
- package/dist/types/capability/skill.d.ts +0 -36
- package/dist/types/capability/slash-command.d.ts +0 -17
- package/dist/types/capability/ssh.d.ts +0 -23
- package/dist/types/capability/system-prompt.d.ts +0 -15
- package/dist/types/capability/tool.d.ts +0 -19
- package/dist/types/capability/types.d.ts +0 -154
- package/dist/types/cli/agents-cli.d.ts +0 -12
- package/dist/types/cli/args.d.ts +0 -57
- package/dist/types/cli/auth-broker-cli.d.ts +0 -25
- package/dist/types/cli/auth-gateway-cli.d.ts +0 -18
- package/dist/types/cli/classify-install-target.d.ts +0 -8
- package/dist/types/cli/commands/init-xdg.d.ts +0 -1
- package/dist/types/cli/completion-cli.d.ts +0 -41
- package/dist/types/cli/config-cli.d.ts +0 -23
- package/dist/types/cli/daemon-cli.d.ts +0 -27
- package/dist/types/cli/fast-help.d.ts +0 -1
- package/dist/types/cli/file-processor.d.ts +0 -11
- package/dist/types/cli/fixture-report.d.ts +0 -20
- package/dist/types/cli/fixture-report.test.d.ts +0 -1
- package/dist/types/cli/grep-cli.d.ts +0 -17
- package/dist/types/cli/initial-message.d.ts +0 -17
- package/dist/types/cli/list-models.d.ts +0 -36
- package/dist/types/cli/local-provider-smoke.d.ts +0 -59
- package/dist/types/cli/malloc-env-guard.d.ts +0 -65
- package/dist/types/cli/malloc-env-guard.test.d.ts +0 -1
- package/dist/types/cli/mcp-cli.d.ts +0 -25
- package/dist/types/cli/migrate-cli.d.ts +0 -20
- package/dist/types/cli/nofile-limit.d.ts +0 -11
- package/dist/types/cli/notify-cli.d.ts +0 -39
- package/dist/types/cli/plugin-cli.d.ts +0 -34
- package/dist/types/cli/read-cli.d.ts +0 -4
- package/dist/types/cli/session-picker.d.ts +0 -4
- package/dist/types/cli/setup-cli.d.ts +0 -73
- package/dist/types/cli/shell-cli.d.ts +0 -8
- package/dist/types/cli/skills-cli.d.ts +0 -9
- package/dist/types/cli/ssh-cli.d.ts +0 -21
- package/dist/types/cli/stats-cli.d.ts +0 -17
- package/dist/types/cli/telegram-cli.d.ts +0 -13
- package/dist/types/cli/update-cli.d.ts +0 -108
- package/dist/types/cli/web-search-cli.d.ts +0 -32
- package/dist/types/cli/worktree-cli.d.ts +0 -26
- package/dist/types/cli.d.ts +0 -140
- package/dist/types/commands/acp.d.ts +0 -12
- package/dist/types/commands/agents.d.ts +0 -34
- package/dist/types/commands/auth-broker.d.ts +0 -54
- package/dist/types/commands/auth-gateway.d.ts +0 -32
- package/dist/types/commands/codex-native-hook.d.ts +0 -6
- package/dist/types/commands/commit.d.ts +0 -27
- package/dist/types/commands/completion.d.ts +0 -29
- package/dist/types/commands/config.d.ts +0 -33
- package/dist/types/commands/contribution-prep.d.ts +0 -18
- package/dist/types/commands/coordinator.d.ts +0 -19
- package/dist/types/commands/daemon.d.ts +0 -45
- package/dist/types/commands/deep-interview.d.ts +0 -54
- package/dist/types/commands/gc.d.ts +0 -26
- package/dist/types/commands/grep.d.ts +0 -42
- package/dist/types/commands/harness.d.ts +0 -43
- package/dist/types/commands/launch.d.ts +0 -131
- package/dist/types/commands/local-provider.d.ts +0 -34
- package/dist/types/commands/mcp-serve.d.ts +0 -24
- package/dist/types/commands/mcp.d.ts +0 -70
- package/dist/types/commands/migrate.d.ts +0 -33
- package/dist/types/commands/notify.d.ts +0 -47
- package/dist/types/commands/plugin.d.ts +0 -58
- package/dist/types/commands/ralplan.d.ts +0 -7
- package/dist/types/commands/read.d.ts +0 -15
- package/dist/types/commands/rlm.d.ts +0 -10
- package/dist/types/commands/session.d.ts +0 -30
- package/dist/types/commands/setup.d.ts +0 -105
- package/dist/types/commands/shell.d.ts +0 -21
- package/dist/types/commands/skills.d.ts +0 -26
- package/dist/types/commands/ssh.d.ts +0 -48
- package/dist/types/commands/state.d.ts +0 -7
- package/dist/types/commands/stats.d.ts +0 -25
- package/dist/types/commands/team.d.ts +0 -24
- package/dist/types/commands/telegram.d.ts +0 -22
- package/dist/types/commands/ultragoal.d.ts +0 -8
- package/dist/types/commands/update.d.ts +0 -20
- package/dist/types/commands/web-search.d.ts +0 -87
- package/dist/types/commands/worktree.d.ts +0 -34
- package/dist/types/commit/agentic/agent.d.ts +0 -31
- package/dist/types/commit/agentic/fallback.d.ts +0 -5
- package/dist/types/commit/agentic/index.d.ts +0 -2
- package/dist/types/commit/agentic/state.d.ts +0 -58
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +0 -19
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +0 -10
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +0 -9
- package/dist/types/commit/agentic/tools/git-overview.d.ts +0 -9
- package/dist/types/commit/agentic/tools/index.d.ts +0 -16
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +0 -28
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +0 -36
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +0 -7
- package/dist/types/commit/agentic/tools/schemas.d.ts +0 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +0 -53
- package/dist/types/commit/agentic/topo-sort.d.ts +0 -4
- package/dist/types/commit/agentic/trivial.d.ts +0 -7
- package/dist/types/commit/agentic/validation.d.ts +0 -20
- package/dist/types/commit/analysis/conventional.d.ts +0 -22
- package/dist/types/commit/analysis/index.d.ts +0 -4
- package/dist/types/commit/analysis/scope.d.ts +0 -6
- package/dist/types/commit/analysis/summary.d.ts +0 -19
- package/dist/types/commit/analysis/validation.d.ts +0 -8
- package/dist/types/commit/changelog/detect.d.ts +0 -2
- package/dist/types/commit/changelog/generate.d.ts +0 -30
- package/dist/types/commit/changelog/index.d.ts +0 -30
- package/dist/types/commit/changelog/parse.d.ts +0 -2
- package/dist/types/commit/cli.d.ts +0 -3
- package/dist/types/commit/git/diff.d.ts +0 -5
- package/dist/types/commit/index.d.ts +0 -4
- package/dist/types/commit/map-reduce/index.d.ts +0 -28
- package/dist/types/commit/map-reduce/map-phase.d.ts +0 -17
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +0 -13
- package/dist/types/commit/map-reduce/utils.d.ts +0 -2
- package/dist/types/commit/message.d.ts +0 -2
- package/dist/types/commit/model-selection.d.ts +0 -15
- package/dist/types/commit/pipeline.d.ts +0 -5
- package/dist/types/commit/shared-llm.d.ts +0 -54
- package/dist/types/commit/types.d.ts +0 -78
- package/dist/types/commit/utils/exclusions.d.ts +0 -4
- package/dist/types/commit/utils.d.ts +0 -20
- package/dist/types/config/config-file.d.ts +0 -58
- package/dist/types/config/file-lock-gc.d.ts +0 -5
- package/dist/types/config/file-lock.d.ts +0 -36
- package/dist/types/config/keybindings.d.ts +0 -364
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-profile-activation.d.ts +0 -65
- package/dist/types/config/model-profiles.d.ts +0 -39
- package/dist/types/config/model-registry.d.ts +0 -473
- package/dist/types/config/model-resolver.d.ts +0 -217
- package/dist/types/config/models-config-schema.d.ts +0 -700
- package/dist/types/config/prompt-templates.d.ts +0 -32
- package/dist/types/config/resolve-config-value.d.ts +0 -17
- package/dist/types/config/settings-schema.d.ts +0 -4223
- package/dist/types/config/settings.d.ts +0 -158
- package/dist/types/config/skill-settings-defaults.d.ts +0 -14
- package/dist/types/config/telegram-autostart.d.ts +0 -14
- package/dist/types/config/telegram-env-bridge.d.ts +0 -18
- package/dist/types/config.d.ts +0 -66
- package/dist/types/coordinator/contract.d.ts +0 -4
- package/dist/types/coordinator-mcp/model-preset.d.ts +0 -45
- package/dist/types/coordinator-mcp/policy.d.ts +0 -27
- package/dist/types/coordinator-mcp/safety.d.ts +0 -26
- package/dist/types/coordinator-mcp/server.d.ts +0 -139
- package/dist/types/coordinator-mcp/session-reaper.d.ts +0 -53
- package/dist/types/cursor.d.ts +0 -24
- package/dist/types/daemon/builtin.d.ts +0 -20
- package/dist/types/daemon/control-types.d.ts +0 -67
- package/dist/types/daemon/operator-contract.d.ts +0 -47
- package/dist/types/daemon/runtime.d.ts +0 -25
- package/dist/types/dap/client.d.ts +0 -39
- package/dist/types/dap/config.d.ts +0 -6
- package/dist/types/dap/index.d.ts +0 -4
- package/dist/types/dap/session.d.ts +0 -108
- package/dist/types/dap/types.d.ts +0 -524
- package/dist/types/debug/crash-diagnostics.d.ts +0 -45
- package/dist/types/debug/index.d.ts +0 -15
- package/dist/types/debug/log-formatting.d.ts +0 -4
- package/dist/types/debug/log-viewer.d.ts +0 -68
- package/dist/types/debug/profiler.d.ts +0 -24
- package/dist/types/debug/raw-sse-buffer.d.ts +0 -44
- package/dist/types/debug/raw-sse.d.ts +0 -16
- package/dist/types/debug/report-bundle.d.ts +0 -55
- package/dist/types/debug/runtime-gauges.d.ts +0 -6
- package/dist/types/debug/system-info.d.ts +0 -26
- package/dist/types/deep-interview/plaintext-gate-guard.d.ts +0 -11
- package/dist/types/deep-interview/render-middleware.d.ts +0 -6
- package/dist/types/defaults/skc/extensions/grok-build/index.d.ts +0 -1
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/index.d.ts +0 -1
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/models/catalog.d.ts +0 -29
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/payload/sanitize.d.ts +0 -27
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/provider/billing.d.ts +0 -8
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/provider/register.d.ts +0 -5
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/provider/stream.d.ts +0 -10
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/provider/usage.d.ts +0 -2
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/base-url.d.ts +0 -2
- package/dist/types/defaults/skc/extensions/grok-cli-vendor/src/shared/errors.d.ts +0 -38
- package/dist/types/defaults/skc-defaults.d.ts +0 -64
- package/dist/types/defaults/skc-grok-cli.d.ts +0 -5
- package/dist/types/discovery/agents-md.d.ts +0 -1
- package/dist/types/discovery/agents.d.ts +0 -12
- package/dist/types/discovery/builtin.d.ts +0 -1
- package/dist/types/discovery/claude-plugins.d.ts +0 -1
- package/dist/types/discovery/claude.d.ts +0 -1
- package/dist/types/discovery/cline.d.ts +0 -1
- package/dist/types/discovery/codex.d.ts +0 -1
- package/dist/types/discovery/cursor.d.ts +0 -19
- package/dist/types/discovery/gemini.d.ts +0 -1
- package/dist/types/discovery/github.d.ts +0 -1
- package/dist/types/discovery/helpers.d.ts +0 -271
- package/dist/types/discovery/index.d.ts +0 -48
- package/dist/types/discovery/mcp-json.d.ts +0 -1
- package/dist/types/discovery/opencode.d.ts +0 -1
- package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
- package/dist/types/discovery/ssh.d.ts +0 -1
- package/dist/types/discovery/substitute-plugin-root.d.ts +0 -1
- package/dist/types/discovery/vscode.d.ts +0 -1
- package/dist/types/discovery/windsurf.d.ts +0 -15
- package/dist/types/edit/apply-patch/index.d.ts +0 -35
- package/dist/types/edit/apply-patch/parser.d.ts +0 -34
- package/dist/types/edit/diff.d.ts +0 -75
- package/dist/types/edit/file-read-cache.d.ts +0 -25
- package/dist/types/edit/index.d.ts +0 -58
- package/dist/types/edit/modes/apply-patch.d.ts +0 -24
- package/dist/types/edit/modes/patch.d.ts +0 -99
- package/dist/types/edit/modes/replace.d.ts +0 -149
- package/dist/types/edit/normalize.d.ts +0 -43
- package/dist/types/edit/notebook.d.ts +0 -26
- package/dist/types/edit/read-file.d.ts +0 -8
- package/dist/types/edit/renderer.d.ts +0 -110
- package/dist/types/edit/streaming.d.ts +0 -66
- package/dist/types/eval/backend.d.ts +0 -40
- package/dist/types/eval/index.d.ts +0 -4
- package/dist/types/eval/js/context-manager.d.ts +0 -27
- package/dist/types/eval/js/executor.d.ts +0 -29
- package/dist/types/eval/js/index.d.ts +0 -10
- package/dist/types/eval/js/shared/helpers.d.ts +0 -38
- package/dist/types/eval/js/shared/indirect-eval.d.ts +0 -14
- package/dist/types/eval/js/shared/prelude.d.ts +0 -1
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +0 -6
- package/dist/types/eval/js/shared/runtime.d.ts +0 -47
- package/dist/types/eval/js/shared/types.d.ts +0 -24
- package/dist/types/eval/js/tool-bridge.d.ts +0 -18
- package/dist/types/eval/js/worker-core.d.ts +0 -5
- package/dist/types/eval/js/worker-entry.d.ts +0 -1
- package/dist/types/eval/js/worker-protocol.d.ts +0 -77
- package/dist/types/eval/py/display.d.ts +0 -25
- package/dist/types/eval/py/executor.d.ts +0 -88
- package/dist/types/eval/py/index.d.ts +0 -10
- package/dist/types/eval/py/kernel.d.ts +0 -65
- package/dist/types/eval/py/prelude.d.ts +0 -1
- package/dist/types/eval/py/runtime.d.ts +0 -29
- package/dist/types/eval/py/tool-bridge.d.ts +0 -20
- package/dist/types/eval/types.d.ts +0 -52
- package/dist/types/exa/factory.d.ts +0 -13
- package/dist/types/exa/index.d.ts +0 -20
- package/dist/types/exa/mcp-client.d.ts +0 -45
- package/dist/types/exa/render.d.ts +0 -19
- package/dist/types/exa/researcher.d.ts +0 -9
- package/dist/types/exa/search.d.ts +0 -9
- package/dist/types/exa/types.d.ts +0 -155
- package/dist/types/exa/websets.d.ts +0 -9
- package/dist/types/exec/bash-executor.d.ts +0 -64
- package/dist/types/exec/exec.d.ts +0 -25
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/dist/types/exec/non-interactive-env.d.ts +0 -1
- package/dist/types/export/custom-share.d.ts +0 -20
- package/dist/types/export/html/index.d.ts +0 -10
- package/dist/types/export/html/template.generated.d.ts +0 -1
- package/dist/types/export/html/template.macro.d.ts +0 -5
- package/dist/types/export/ttsr.d.ts +0 -55
- package/dist/types/extensibility/custom-commands/bundled/ci-green/index.d.ts +0 -9
- package/dist/types/extensibility/custom-commands/index.d.ts +0 -2
- package/dist/types/extensibility/custom-commands/loader.d.ts +0 -29
- package/dist/types/extensibility/custom-commands/types.d.ts +0 -106
- package/dist/types/extensibility/custom-tools/index.d.ts +0 -6
- package/dist/types/extensibility/custom-tools/loader.d.ts +0 -69
- package/dist/types/extensibility/custom-tools/types.d.ts +0 -229
- package/dist/types/extensibility/custom-tools/wrapper.d.ts +0 -24
- package/dist/types/extensibility/extensions/compact-handler.d.ts +0 -26
- package/dist/types/extensibility/extensions/get-commands-handler.d.ts +0 -29
- package/dist/types/extensibility/extensions/index.d.ts +0 -9
- package/dist/types/extensibility/extensions/loader.d.ts +0 -43
- package/dist/types/extensibility/extensions/prefix-command-bridge.d.ts +0 -35
- package/dist/types/extensibility/extensions/runner.d.ts +0 -135
- package/dist/types/extensibility/extensions/types.d.ts +0 -920
- package/dist/types/extensibility/extensions/wrapper.d.ts +0 -55
- package/dist/types/extensibility/hooks/index.d.ts +0 -5
- package/dist/types/extensibility/hooks/loader.d.ts +0 -89
- package/dist/types/extensibility/hooks/runner.d.ts +0 -126
- package/dist/types/extensibility/hooks/tool-wrapper.d.ts +0 -25
- package/dist/types/extensibility/hooks/types.d.ts +0 -431
- package/dist/types/extensibility/plugins/doctor.d.ts +0 -3
- package/dist/types/extensibility/plugins/git-url.d.ts +0 -34
- package/dist/types/extensibility/plugins/index.d.ts +0 -8
- package/dist/types/extensibility/plugins/installer.d.ts +0 -5
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +0 -2
- package/dist/types/extensibility/plugins/loader.d.ts +0 -31
- package/dist/types/extensibility/plugins/manager.d.ts +0 -65
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +0 -41
- package/dist/types/extensibility/plugins/marketplace/fetcher.d.ts +0 -48
- package/dist/types/extensibility/plugins/marketplace/index.d.ts +0 -6
- package/dist/types/extensibility/plugins/marketplace/manager.d.ts +0 -57
- package/dist/types/extensibility/plugins/marketplace/registry.d.ts +0 -34
- package/dist/types/extensibility/plugins/marketplace/source-resolver.d.ts +0 -28
- package/dist/types/extensibility/plugins/marketplace/types.d.ts +0 -130
- package/dist/types/extensibility/plugins/parser.d.ts +0 -52
- package/dist/types/extensibility/plugins/security-scanner.d.ts +0 -31
- package/dist/types/extensibility/plugins/types.d.ts +0 -149
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +0 -20
- package/dist/types/extensibility/shared-events.d.ts +0 -281
- package/dist/types/extensibility/skc-plugins/activation.d.ts +0 -14
- package/dist/types/extensibility/skc-plugins/compiler.d.ts +0 -19
- package/dist/types/extensibility/skc-plugins/constrained-hooks.d.ts +0 -29
- package/dist/types/extensibility/skc-plugins/index.d.ts +0 -18
- package/dist/types/extensibility/skc-plugins/injection.d.ts +0 -40
- package/dist/types/extensibility/skc-plugins/installer.d.ts +0 -13
- package/dist/types/extensibility/skc-plugins/loader.d.ts +0 -3
- package/dist/types/extensibility/skc-plugins/mcp-policy.d.ts +0 -26
- package/dist/types/extensibility/skc-plugins/observability.d.ts +0 -27
- package/dist/types/extensibility/skc-plugins/paths.d.ts +0 -8
- package/dist/types/extensibility/skc-plugins/prompt-appendix.d.ts +0 -16
- package/dist/types/extensibility/skc-plugins/registry.d.ts +0 -32
- package/dist/types/extensibility/skc-plugins/runtime-adapters.d.ts +0 -64
- package/dist/types/extensibility/skc-plugins/schema.d.ts +0 -3
- package/dist/types/extensibility/skc-plugins/session-validation.d.ts +0 -42
- package/dist/types/extensibility/skc-plugins/state.d.ts +0 -9
- package/dist/types/extensibility/skc-plugins/tools.d.ts +0 -8
- package/dist/types/extensibility/skc-plugins/types.d.ts +0 -220
- package/dist/types/extensibility/skc-plugins/validation.d.ts +0 -11
- package/dist/types/extensibility/skills.d.ts +0 -82
- package/dist/types/extensibility/slash-commands.d.ts +0 -48
- package/dist/types/extensibility/tool-proxy.d.ts +0 -4
- package/dist/types/extensibility/typebox.d.ts +0 -155
- package/dist/types/extensibility/utils.d.ts +0 -12
- package/dist/types/goals/index.d.ts +0 -3
- package/dist/types/goals/runtime.d.ts +0 -63
- package/dist/types/goals/state.d.ts +0 -32
- package/dist/types/goals/tools/goal-tool.d.ts +0 -62
- package/dist/types/harness-control-plane/classifier.d.ts +0 -13
- package/dist/types/harness-control-plane/control-endpoint.d.ts +0 -31
- package/dist/types/harness-control-plane/finalize.d.ts +0 -60
- package/dist/types/harness-control-plane/gc-adapter.d.ts +0 -3
- package/dist/types/harness-control-plane/operate.d.ts +0 -35
- package/dist/types/harness-control-plane/owner.d.ts +0 -53
- package/dist/types/harness-control-plane/phase-rollup.d.ts +0 -23
- package/dist/types/harness-control-plane/preserve.d.ts +0 -19
- package/dist/types/harness-control-plane/receipt-ingest.d.ts +0 -19
- package/dist/types/harness-control-plane/receipt-spool.d.ts +0 -19
- package/dist/types/harness-control-plane/receipts.d.ts +0 -149
- package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
- package/dist/types/harness-control-plane/seams.d.ts +0 -21
- package/dist/types/harness-control-plane/session-lease.d.ts +0 -65
- package/dist/types/harness-control-plane/state-machine.d.ts +0 -24
- package/dist/types/harness-control-plane/storage.d.ts +0 -81
- package/dist/types/harness-control-plane/types.d.ts +0 -187
- package/dist/types/hashline/anchors.d.ts +0 -20
- package/dist/types/hashline/apply.d.ts +0 -14
- package/dist/types/hashline/constants.d.ts +0 -19
- package/dist/types/hashline/diff-preview.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +0 -17
- package/dist/types/hashline/execute.d.ts +0 -4
- package/dist/types/hashline/hash.d.ts +0 -123
- package/dist/types/hashline/index.d.ts +0 -13
- package/dist/types/hashline/input.d.ts +0 -11
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/hashline/prefixes.d.ts +0 -7
- package/dist/types/hashline/recovery.d.ts +0 -27
- package/dist/types/hashline/stream.d.ts +0 -2
- package/dist/types/hashline/types.d.ts +0 -77
- package/dist/types/hindsight/backend.d.ts +0 -13
- package/dist/types/hindsight/bank.d.ts +0 -54
- package/dist/types/hindsight/client.d.ts +0 -224
- package/dist/types/hindsight/config.d.ts +0 -51
- package/dist/types/hindsight/content.d.ts +0 -70
- package/dist/types/hindsight/index.d.ts +0 -8
- package/dist/types/hindsight/mental-models.d.ts +0 -125
- package/dist/types/hindsight/state.d.ts +0 -99
- package/dist/types/hindsight/transcript.d.ts +0 -28
- package/dist/types/hooks/codex-native-hooks-config.d.ts +0 -29
- package/dist/types/hooks/native-skill-hook.d.ts +0 -28
- package/dist/types/hooks/skill-keywords.d.ts +0 -18
- package/dist/types/hooks/skill-state.d.ts +0 -93
- package/dist/types/i18n/index.d.ts +0 -38
- package/dist/types/i18n/messages/de.d.ts +0 -2
- package/dist/types/i18n/messages/de.messages.d.ts +0 -1
- package/dist/types/i18n/messages/de.settings.d.ts +0 -1
- package/dist/types/i18n/messages/en.d.ts +0 -81
- package/dist/types/i18n/messages/es.d.ts +0 -2
- package/dist/types/i18n/messages/es.messages.d.ts +0 -1
- package/dist/types/i18n/messages/es.settings.d.ts +0 -1
- package/dist/types/i18n/messages/fr.d.ts +0 -2
- package/dist/types/i18n/messages/fr.messages.d.ts +0 -1
- package/dist/types/i18n/messages/fr.settings.d.ts +0 -1
- package/dist/types/i18n/messages/ja.d.ts +0 -2
- package/dist/types/i18n/messages/ja.messages.d.ts +0 -1
- package/dist/types/i18n/messages/ja.settings.d.ts +0 -1
- package/dist/types/i18n/messages/ko.d.ts +0 -2
- package/dist/types/i18n/messages/ko.messages.d.ts +0 -1
- package/dist/types/i18n/messages/ko.settings.d.ts +0 -1
- package/dist/types/i18n/messages/zh.d.ts +0 -2
- package/dist/types/i18n/messages/zh.messages.d.ts +0 -1
- package/dist/types/i18n/messages/zh.settings.d.ts +0 -1
- package/dist/types/index.d.ts +0 -33
- package/dist/types/internal-urls/agent-protocol.d.ts +0 -12
- package/dist/types/internal-urls/artifact-protocol.d.ts +0 -6
- package/dist/types/internal-urls/docs-index.generated.d.ts +0 -2
- package/dist/types/internal-urls/index.d.ts +0 -20
- package/dist/types/internal-urls/issue-pr-protocol.d.ts +0 -17
- package/dist/types/internal-urls/json-query.d.ts +0 -30
- package/dist/types/internal-urls/local-protocol.d.ts +0 -43
- package/dist/types/internal-urls/mcp-protocol.d.ts +0 -12
- package/dist/types/internal-urls/memory-protocol.d.ts +0 -23
- package/dist/types/internal-urls/parse.d.ts +0 -19
- package/dist/types/internal-urls/registry-helpers.d.ts +0 -11
- package/dist/types/internal-urls/router.d.ts +0 -14
- package/dist/types/internal-urls/rule-protocol.d.ts +0 -6
- package/dist/types/internal-urls/skc-protocol.d.ts +0 -12
- package/dist/types/internal-urls/skill-protocol.d.ts +0 -13
- package/dist/types/internal-urls/types.d.ts +0 -109
- package/dist/types/lsp/client.d.ts +0 -66
- package/dist/types/lsp/clients/biome-client.d.ts +0 -16
- package/dist/types/lsp/clients/index.d.ts +0 -19
- package/dist/types/lsp/clients/lsp-linter-client.d.ts +0 -16
- package/dist/types/lsp/clients/swiftlint-client.d.ts +0 -20
- package/dist/types/lsp/config.d.ts +0 -65
- package/dist/types/lsp/edits.d.ts +0 -23
- package/dist/types/lsp/index.d.ts +0 -130
- package/dist/types/lsp/lspmux.d.ts +0 -58
- package/dist/types/lsp/render.d.ts +0 -32
- package/dist/types/lsp/startup-events.d.ts +0 -12
- package/dist/types/lsp/types.d.ts +0 -306
- package/dist/types/lsp/utils.d.ts +0 -108
- package/dist/types/main.d.ts +0 -139
- package/dist/types/memories/index.d.ts +0 -28
- package/dist/types/memories/storage.d.ts +0 -110
- package/dist/types/memory-backend/index.d.ts +0 -4
- package/dist/types/memory-backend/local-backend.d.ts +0 -9
- package/dist/types/memory-backend/off-backend.d.ts +0 -7
- package/dist/types/memory-backend/resolve.d.ts +0 -15
- package/dist/types/memory-backend/types.d.ts +0 -61
- package/dist/types/migrate/action-planner.d.ts +0 -11
- package/dist/types/migrate/adapters/claude-code.d.ts +0 -2
- package/dist/types/migrate/adapters/codex.d.ts +0 -5
- package/dist/types/migrate/adapters/index.d.ts +0 -45
- package/dist/types/migrate/adapters/opencode.d.ts +0 -2
- package/dist/types/migrate/executor.d.ts +0 -2
- package/dist/types/migrate/mcp-mapper.d.ts +0 -20
- package/dist/types/migrate/report.d.ts +0 -18
- package/dist/types/migrate/skill-normalizer.d.ts +0 -27
- package/dist/types/migrate/types.d.ts +0 -126
- package/dist/types/modes/acp/acp-agent.d.ts +0 -78
- package/dist/types/modes/acp/acp-client-bridge.d.ts +0 -9
- package/dist/types/modes/acp/acp-event-mapper.d.ts +0 -36
- package/dist/types/modes/acp/acp-mode.d.ts +0 -17
- package/dist/types/modes/acp/index.d.ts +0 -2
- package/dist/types/modes/acp/terminal-auth.d.ts +0 -6
- package/dist/types/modes/bridge/auth.d.ts +0 -12
- package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
- package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
- package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
- package/dist/types/modes/bridge/event-stream.d.ts +0 -8
- package/dist/types/modes/components/agent-dashboard.d.ts +0 -21
- package/dist/types/modes/components/assistant-message.d.ts +0 -20
- package/dist/types/modes/components/bash-execution.d.ts +0 -29
- package/dist/types/modes/components/bordered-loader.d.ts +0 -11
- package/dist/types/modes/components/branch-summary-message.d.ts +0 -13
- package/dist/types/modes/components/btw-panel.d.ts +0 -16
- package/dist/types/modes/components/compaction-summary-message.d.ts +0 -13
- package/dist/types/modes/components/countdown-timer.d.ts +0 -14
- package/dist/types/modes/components/custom-editor.d.ts +0 -69
- package/dist/types/modes/components/custom-message.d.ts +0 -15
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +0 -12
- package/dist/types/modes/components/custom-provider-wizard.d.ts +0 -10
- package/dist/types/modes/components/diff.d.ts +0 -11
- package/dist/types/modes/components/dynamic-border.d.ts +0 -14
- package/dist/types/modes/components/eval-execution.d.ts +0 -23
- package/dist/types/modes/components/execution-shared.d.ts +0 -46
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +0 -26
- package/dist/types/modes/components/extensions/extension-list.d.ts +0 -39
- package/dist/types/modes/components/extensions/index.d.ts +0 -8
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +0 -8
- package/dist/types/modes/components/extensions/state-manager.d.ts +0 -50
- package/dist/types/modes/components/extensions/types.d.ts +0 -151
- package/dist/types/modes/components/footer.d.ts +0 -30
- package/dist/types/modes/components/history-search.d.ts +0 -7
- package/dist/types/modes/components/hook-editor.d.ts +0 -18
- package/dist/types/modes/components/hook-input.d.ts +0 -15
- package/dist/types/modes/components/hook-message.d.ts +0 -15
- package/dist/types/modes/components/hook-selector.d.ts +0 -59
- package/dist/types/modes/components/index.d.ts +0 -35
- package/dist/types/modes/components/irc-sidebar.d.ts +0 -32
- package/dist/types/modes/components/jobs-overlay-model.d.ts +0 -31
- package/dist/types/modes/components/jobs-overlay.d.ts +0 -30
- package/dist/types/modes/components/keybinding-hints.d.ts +0 -40
- package/dist/types/modes/components/login-dialog.d.ts +0 -32
- package/dist/types/modes/components/message-frame.d.ts +0 -42
- package/dist/types/modes/components/model-selector.d.ts +0 -60
- package/dist/types/modes/components/oauth-selector.d.ts +0 -17
- package/dist/types/modes/components/pet-selector.d.ts +0 -11
- package/dist/types/modes/components/plugin-selector.d.ts +0 -26
- package/dist/types/modes/components/plugin-settings.d.ts +0 -66
- package/dist/types/modes/components/provider-onboarding-selector.d.ts +0 -7
- package/dist/types/modes/components/queue-mode-selector.d.ts +0 -9
- package/dist/types/modes/components/queued-message-selector.d.ts +0 -12
- package/dist/types/modes/components/read-tool-group.d.ts +0 -30
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +0 -26
- package/dist/types/modes/components/sayknow-pet-widget.d.ts +0 -59
- package/dist/types/modes/components/session-observer-overlay.d.ts +0 -11
- package/dist/types/modes/components/session-selector.d.ts +0 -43
- package/dist/types/modes/components/settings-defs.d.ts +0 -50
- package/dist/types/modes/components/settings-selector.d.ts +0 -64
- package/dist/types/modes/components/show-images-selector.d.ts +0 -9
- package/dist/types/modes/components/skill-hud/render.d.ts +0 -2
- package/dist/types/modes/components/skill-message.d.ts +0 -10
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -4
- package/dist/types/modes/components/status-line/git-utils.d.ts +0 -28
- package/dist/types/modes/components/status-line/index.d.ts +0 -4
- package/dist/types/modes/components/status-line/presets.d.ts +0 -3
- package/dist/types/modes/components/status-line/segments.d.ts +0 -5
- package/dist/types/modes/components/status-line/separators.d.ts +0 -3
- package/dist/types/modes/components/status-line/token-rate.d.ts +0 -10
- package/dist/types/modes/components/status-line/types.d.ts +0 -83
- package/dist/types/modes/components/status-line.d.ts +0 -94
- package/dist/types/modes/components/theme-selector.d.ts +0 -10
- package/dist/types/modes/components/thinking-selector.d.ts +0 -15
- package/dist/types/modes/components/todo-reminder.d.ts +0 -13
- package/dist/types/modes/components/tool-execution.d.ts +0 -55
- package/dist/types/modes/components/tree-selector.d.ts +0 -31
- package/dist/types/modes/components/ttsr-notification.d.ts +0 -13
- package/dist/types/modes/components/two-column-body.d.ts +0 -9
- package/dist/types/modes/components/user-message-selector.d.ts +0 -28
- package/dist/types/modes/components/user-message.d.ts +0 -8
- package/dist/types/modes/components/visual-truncate.d.ts +0 -19
- package/dist/types/modes/components/welcome.d.ts +0 -46
- package/dist/types/modes/controllers/btw-controller.d.ts +0 -10
- package/dist/types/modes/controllers/command-controller-shared.d.ts +0 -47
- package/dist/types/modes/controllers/command-controller.d.ts +0 -40
- package/dist/types/modes/controllers/event-controller.d.ts +0 -21
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -80
- package/dist/types/modes/controllers/input-controller.d.ts +0 -42
- package/dist/types/modes/controllers/runtime-mcp-command-controller.d.ts +0 -10
- package/dist/types/modes/controllers/selector-controller.d.ts +0 -68
- package/dist/types/modes/controllers/ssh-command-controller.d.ts +0 -10
- package/dist/types/modes/controllers/todo-command-controller.d.ts +0 -7
- package/dist/types/modes/emoji-autocomplete.d.ts +0 -16
- package/dist/types/modes/index.d.ts +0 -10
- package/dist/types/modes/interactive-mode.d.ts +0 -297
- package/dist/types/modes/irc-observation-ledger.d.ts +0 -22
- package/dist/types/modes/jobs-observer.d.ts +0 -57
- package/dist/types/modes/oauth-manual-input.d.ts +0 -8
- package/dist/types/modes/print-mode.d.ts +0 -44
- package/dist/types/modes/prompt-action-autocomplete.d.ts +0 -50
- package/dist/types/modes/rpc/host-tools.d.ts +0 -1
- package/dist/types/modes/rpc/host-uris.d.ts +0 -1
- package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
- package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
- package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
- package/dist/types/modes/runtime-init.d.ts +0 -21
- package/dist/types/modes/session-observer-registry.d.ts +0 -26
- package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +0 -57
- package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +0 -102
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +0 -103
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +0 -60
- package/dist/types/modes/shared/agent-wire/event-observation.d.ts +0 -37
- package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
- package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
- package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
- package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
- package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
- package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
- package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
- package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
- package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
- package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +0 -154
- package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +0 -39
- package/dist/types/modes/shared.d.ts +0 -15
- package/dist/types/modes/slash-command-visibility.d.ts +0 -1
- package/dist/types/modes/theme/defaults/index.d.ts +0 -652
- package/dist/types/modes/theme/mermaid-cache.d.ts +0 -9
- package/dist/types/modes/theme/shimmer.d.ts +0 -38
- package/dist/types/modes/theme/theme.d.ts +0 -275
- package/dist/types/modes/tmux-scroll.d.ts +0 -18
- package/dist/types/modes/types.d.ts +0 -318
- package/dist/types/modes/utils/abort-message.d.ts +0 -4
- package/dist/types/modes/utils/context-usage.d.ts +0 -36
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +0 -5
- package/dist/types/modes/utils/injected-user-submission.d.ts +0 -52
- package/dist/types/modes/utils/irc-message.d.ts +0 -38
- package/dist/types/modes/utils/keybinding-matchers.d.ts +0 -10
- package/dist/types/modes/utils/terminal-bell.d.ts +0 -3
- package/dist/types/modes/utils/tools-markdown.d.ts +0 -5
- package/dist/types/modes/utils/ui-helpers.d.ts +0 -75
- package/dist/types/notifications/attachment-registry.d.ts +0 -17
- package/dist/types/notifications/chat-adapters.d.ts +0 -9
- package/dist/types/notifications/config-commands.d.ts +0 -64
- package/dist/types/notifications/config.d.ts +0 -132
- package/dist/types/notifications/daemon-paths.d.ts +0 -17
- package/dist/types/notifications/engine.d.ts +0 -59
- package/dist/types/notifications/helpers.d.ts +0 -55
- package/dist/types/notifications/html-format.d.ts +0 -75
- package/dist/types/notifications/index.d.ts +0 -219
- package/dist/types/notifications/lifecycle-commands.d.ts +0 -69
- package/dist/types/notifications/lifecycle-control-runtime.d.ts +0 -107
- package/dist/types/notifications/lifecycle-orchestrator.d.ts +0 -144
- package/dist/types/notifications/managed-daemon.d.ts +0 -48
- package/dist/types/notifications/notification-service.d.ts +0 -144
- package/dist/types/notifications/operator-runtime.d.ts +0 -52
- package/dist/types/notifications/rate-limit-pool.d.ts +0 -113
- package/dist/types/notifications/recent-activity.d.ts +0 -39
- package/dist/types/notifications/reply-sent-store.d.ts +0 -53
- package/dist/types/notifications/rich-draft.d.ts +0 -68
- package/dist/types/notifications/rich-render.d.ts +0 -90
- package/dist/types/notifications/telegram-cli.d.ts +0 -19
- package/dist/types/notifications/telegram-daemon-cli.d.ts +0 -28
- package/dist/types/notifications/telegram-daemon-control.d.ts +0 -70
- package/dist/types/notifications/telegram-daemon.d.ts +0 -558
- package/dist/types/notifications/telegram-reference.d.ts +0 -136
- package/dist/types/notifications/threaded-inbound.d.ts +0 -77
- package/dist/types/notifications/threaded-render.d.ts +0 -88
- package/dist/types/notifications/topic-registry.d.ts +0 -89
- package/dist/types/plan-mode/approved-plan.d.ts +0 -49
- package/dist/types/plan-mode/state.d.ts +0 -6
- package/dist/types/registry/agent-registry.d.ts +0 -64
- package/dist/types/reminders/star-reminder.d.ts +0 -115
- package/dist/types/research-plan/index.d.ts +0 -1
- package/dist/types/research-plan/ledger.d.ts +0 -33
- package/dist/types/rlm/artifacts.d.ts +0 -9
- package/dist/types/rlm/complete-research-tool.d.ts +0 -35
- package/dist/types/rlm/data-context.d.ts +0 -6
- package/dist/types/rlm/index.d.ts +0 -47
- package/dist/types/rlm/notebook.d.ts +0 -12
- package/dist/types/rlm/preset.d.ts +0 -23
- package/dist/types/rlm/python-tool.d.ts +0 -16
- package/dist/types/rlm/report.d.ts +0 -14
- package/dist/types/rlm/types.d.ts +0 -37
- package/dist/types/runtime/process-lifecycle.d.ts +0 -108
- package/dist/types/runtime-api-key.d.ts +0 -7
- package/dist/types/runtime-credential-selector.d.ts +0 -7
- package/dist/types/runtime-mcp/client.d.ts +0 -74
- package/dist/types/runtime-mcp/config-writer.d.ts +0 -89
- package/dist/types/runtime-mcp/config.d.ts +0 -77
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/runtime-mcp/index.d.ts +0 -18
- package/dist/types/runtime-mcp/json-rpc.d.ts +0 -22
- package/dist/types/runtime-mcp/loader.d.ts +0 -45
- package/dist/types/runtime-mcp/manager.d.ts +0 -195
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +0 -40
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -59
- package/dist/types/runtime-mcp/render.d.ts +0 -25
- package/dist/types/runtime-mcp/smithery-auth.d.ts +0 -16
- package/dist/types/runtime-mcp/smithery-connect.d.ts +0 -38
- package/dist/types/runtime-mcp/smithery-registry.d.ts +0 -51
- package/dist/types/runtime-mcp/tool-bridge.d.ts +0 -86
- package/dist/types/runtime-mcp/tool-cache.d.ts +0 -8
- package/dist/types/runtime-mcp/transports/http.d.ts +0 -36
- package/dist/types/runtime-mcp/transports/index.d.ts +0 -5
- package/dist/types/runtime-mcp/transports/stdio.d.ts +0 -29
- package/dist/types/runtime-mcp/types.d.ts +0 -349
- package/dist/types/sdk.d.ts +0 -256
- package/dist/types/secrets/index.d.ts +0 -9
- package/dist/types/secrets/obfuscator.d.ts +0 -23
- package/dist/types/secrets/regex.d.ts +0 -2
- package/dist/types/session/agent-session.d.ts +0 -1238
- package/dist/types/session/agent-storage.d.ts +0 -101
- package/dist/types/session/artifacts.d.ts +0 -64
- package/dist/types/session/auth-broker-config.d.ts +0 -13
- package/dist/types/session/auth-storage.d.ts +0 -6
- package/dist/types/session/blob-store.d.ts +0 -160
- package/dist/types/session/cache-economics.d.ts +0 -79
- package/dist/types/session/client-bridge.d.ts +0 -88
- package/dist/types/session/context-estimation.d.ts +0 -31
- package/dist/types/session/contribution-prep.d.ts +0 -47
- package/dist/types/session/file-mention-pruning.d.ts +0 -8
- package/dist/types/session/history-storage.d.ts +0 -16
- package/dist/types/session/messages.d.ts +0 -169
- package/dist/types/session/provider-safety-stop.d.ts +0 -5
- package/dist/types/session/session-dump-format.d.ts +0 -22
- package/dist/types/session/session-manager.d.ts +0 -781
- package/dist/types/session/session-storage.d.ts +0 -230
- package/dist/types/session/streaming-output.d.ts +0 -209
- package/dist/types/session/tool-choice-queue.d.ts +0 -84
- package/dist/types/session/yield-queue.d.ts +0 -24
- package/dist/types/setup/credential-auto-import.d.ts +0 -63
- package/dist/types/setup/credential-import.d.ts +0 -82
- package/dist/types/setup/hermes-setup.d.ts +0 -78
- package/dist/types/setup/host-plugin-setup.d.ts +0 -39
- package/dist/types/setup/model-onboarding-guidance.d.ts +0 -9
- package/dist/types/setup/provider-onboarding.d.ts +0 -52
- package/dist/types/skc-runtime/cli-write-receipt.d.ts +0 -24
- package/dist/types/skc-runtime/deep-interview-ambiguity.d.ts +0 -39
- package/dist/types/skc-runtime/deep-interview-recorder.d.ts +0 -114
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +0 -36
- package/dist/types/skc-runtime/deep-interview-state.d.ts +0 -127
- package/dist/types/skc-runtime/gc-render.d.ts +0 -6
- package/dist/types/skc-runtime/gc-runtime.d.ts +0 -134
- package/dist/types/skc-runtime/goal-mode-request.d.ts +0 -53
- package/dist/types/skc-runtime/launch-tmux.d.ts +0 -78
- package/dist/types/skc-runtime/launch-worktree.d.ts +0 -46
- package/dist/types/skc-runtime/ledger-event-renderer.d.ts +0 -68
- package/dist/types/skc-runtime/psmux-detect.d.ts +0 -78
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +0 -26
- package/dist/types/skc-runtime/restricted-role-agent-bash.d.ts +0 -3
- package/dist/types/skc-runtime/session-layout.d.ts +0 -59
- package/dist/types/skc-runtime/session-resolution.d.ts +0 -47
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +0 -70
- package/dist/types/skc-runtime/state-graph.d.ts +0 -4
- package/dist/types/skc-runtime/state-migrations.d.ts +0 -33
- package/dist/types/skc-runtime/state-renderer.d.ts +0 -70
- package/dist/types/skc-runtime/state-runtime.d.ts +0 -38
- package/dist/types/skc-runtime/state-schema.d.ts +0 -319
- package/dist/types/skc-runtime/state-validation.d.ts +0 -6
- package/dist/types/skc-runtime/state-writer.d.ts +0 -244
- package/dist/types/skc-runtime/team-gc.d.ts +0 -7
- package/dist/types/skc-runtime/team-runtime.d.ts +0 -355
- package/dist/types/skc-runtime/tmux-common.d.ts +0 -111
- package/dist/types/skc-runtime/tmux-gc.d.ts +0 -7
- package/dist/types/skc-runtime/tmux-owner-isolation-cli.d.ts +0 -5
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +0 -309
- package/dist/types/skc-runtime/tmux-sessions.d.ts +0 -94
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +0 -93
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +0 -42
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +0 -410
- package/dist/types/skc-runtime/workflow-command-ref.d.ts +0 -43
- package/dist/types/skc-runtime/workflow-manifest.d.ts +0 -54
- package/dist/types/skill-state/active-state.d.ts +0 -126
- package/dist/types/skill-state/canonical-skills.d.ts +0 -3
- package/dist/types/skill-state/deep-interview-mutation-guard.d.ts +0 -38
- package/dist/types/skill-state/initial-phase.d.ts +0 -12
- package/dist/types/skill-state/workflow-hud.d.ts +0 -83
- package/dist/types/skill-state/workflow-state-contract.d.ts +0 -57
- package/dist/types/skill-state/workflow-state-version.d.ts +0 -3
- package/dist/types/slash-commands/acp-builtins.d.ts +0 -18
- package/dist/types/slash-commands/builtin-registry.d.ts +0 -24
- package/dist/types/slash-commands/helpers/context-report.d.ts +0 -7
- package/dist/types/slash-commands/helpers/fast-status-report.d.ts +0 -82
- package/dist/types/slash-commands/helpers/format.d.ts +0 -10
- package/dist/types/slash-commands/helpers/mcp.d.ts +0 -3
- package/dist/types/slash-commands/helpers/parse.d.ts +0 -33
- package/dist/types/slash-commands/helpers/ssh.d.ts +0 -3
- package/dist/types/slash-commands/helpers/todo.d.ts +0 -3
- package/dist/types/slash-commands/helpers/usage-report.d.ts +0 -7
- package/dist/types/slash-commands/types.d.ts +0 -120
- package/dist/types/ssh/config-writer.d.ts +0 -49
- package/dist/types/ssh/connection-manager.d.ts +0 -33
- package/dist/types/ssh/ssh-executor.d.ts +0 -37
- package/dist/types/ssh/sshfs-mount.d.ts +0 -6
- package/dist/types/ssh/utils.d.ts +0 -2
- package/dist/types/stt/downloader.d.ts +0 -9
- package/dist/types/stt/index.d.ts +0 -3
- package/dist/types/stt/recorder.d.ts +0 -13
- package/dist/types/stt/setup.d.ts +0 -18
- package/dist/types/stt/stt-controller.d.ts +0 -16
- package/dist/types/stt/transcriber.d.ts +0 -16
- package/dist/types/system-prompt.d.ts +0 -118
- package/dist/types/task/agents.d.ts +0 -28
- package/dist/types/task/commands.d.ts +0 -35
- package/dist/types/task/discovery.d.ts +0 -19
- package/dist/types/task/executor.d.ts +0 -127
- package/dist/types/task/fork-context-advisory.d.ts +0 -13
- package/dist/types/task/fork-context-budget.d.ts +0 -7
- package/dist/types/task/id.d.ts +0 -7
- package/dist/types/task/index.d.ts +0 -43
- package/dist/types/task/name-generator.d.ts +0 -16
- package/dist/types/task/output-manager.d.ts +0 -36
- package/dist/types/task/parallel.d.ts +0 -34
- package/dist/types/task/receipt.d.ts +0 -89
- package/dist/types/task/render.d.ts +0 -28
- package/dist/types/task/roi-reconciliation.d.ts +0 -27
- package/dist/types/task/simple-mode.d.ts +0 -8
- package/dist/types/task/skc-command.d.ts +0 -7
- package/dist/types/task/spawn-gate.d.ts +0 -29
- package/dist/types/task/subprocess-tool-registry.d.ts +0 -72
- package/dist/types/task/token-log.d.ts +0 -30
- package/dist/types/task/token-log.test.d.ts +0 -1
- package/dist/types/task/types.d.ts +0 -516
- package/dist/types/task/worktree.d.ts +0 -94
- package/dist/types/thinking-metadata.d.ts +0 -16
- package/dist/types/thinking.d.ts +0 -22
- package/dist/types/tool-discovery/tool-index.d.ts +0 -112
- package/dist/types/tools/archive-reader.d.ts +0 -40
- package/dist/types/tools/ask-answer-registry.d.ts +0 -18
- package/dist/types/tools/ask.d.ts +0 -123
- package/dist/types/tools/ast-edit.d.ts +0 -77
- package/dist/types/tools/ast-grep.d.ts +0 -69
- package/dist/types/tools/auto-generated-guard.d.ts +0 -17
- package/dist/types/tools/bash-allowed-prefixes.d.ts +0 -10
- package/dist/types/tools/bash-command-fixup.d.ts +0 -11
- package/dist/types/tools/bash-interactive.d.ts +0 -16
- package/dist/types/tools/bash-interceptor.d.ts +0 -24
- package/dist/types/tools/bash-pty-selection.d.ts +0 -7
- package/dist/types/tools/bash-skill-urls.d.ts +0 -31
- package/dist/types/tools/bash.d.ts +0 -157
- package/dist/types/tools/bisect.d.ts +0 -81
- package/dist/types/tools/browser/actions.d.ts +0 -54
- package/dist/types/tools/browser/attach.d.ts +0 -50
- package/dist/types/tools/browser/launch.d.ts +0 -62
- package/dist/types/tools/browser/readable.d.ts +0 -17
- package/dist/types/tools/browser/registry.d.ts +0 -57
- package/dist/types/tools/browser/render.d.ts +0 -53
- package/dist/types/tools/browser/tab-protocol.d.ts +0 -144
- package/dist/types/tools/browser/tab-supervisor.d.ts +0 -109
- package/dist/types/tools/browser/tab-worker-entry.d.ts +0 -1
- package/dist/types/tools/browser/tab-worker.d.ts +0 -19
- package/dist/types/tools/browser.d.ts +0 -211
- package/dist/types/tools/calculator.d.ts +0 -76
- package/dist/types/tools/checkpoint.d.ts +0 -63
- package/dist/types/tools/composer-bash-policy.d.ts +0 -14
- package/dist/types/tools/computer/render.d.ts +0 -17
- package/dist/types/tools/computer-gc.d.ts +0 -23
- package/dist/types/tools/computer.d.ts +0 -465
- package/dist/types/tools/conflict-detect.d.ts +0 -205
- package/dist/types/tools/context.d.ts +0 -19
- package/dist/types/tools/cron.d.ts +0 -91
- package/dist/types/tools/debug.d.ts +0 -209
- package/dist/types/tools/eval.d.ts +0 -108
- package/dist/types/tools/fetch.d.ts +0 -84
- package/dist/types/tools/file-recorder.d.ts +0 -13
- package/dist/types/tools/find.d.ts +0 -95
- package/dist/types/tools/fs-cache-invalidation.d.ts +0 -15
- package/dist/types/tools/gh-format.d.ts +0 -6
- package/dist/types/tools/gh-renderer.d.ts +0 -25
- package/dist/types/tools/gh.d.ts +0 -356
- package/dist/types/tools/github-cache.d.ts +0 -103
- package/dist/types/tools/grouped-file-output.d.ts +0 -36
- package/dist/types/tools/hindsight-recall.d.ts +0 -21
- package/dist/types/tools/hindsight-reflect.d.ts +0 -23
- package/dist/types/tools/hindsight-retain.d.ts +0 -27
- package/dist/types/tools/image-gen.d.ts +0 -79
- package/dist/types/tools/index.d.ts +0 -371
- package/dist/types/tools/irc.d.ts +0 -79
- package/dist/types/tools/job.d.ts +0 -78
- package/dist/types/tools/json-tree.d.ts +0 -23
- package/dist/types/tools/jtd-to-json-schema.d.ts +0 -29
- package/dist/types/tools/jtd-to-typescript.d.ts +0 -26
- package/dist/types/tools/jtd-utils.d.ts +0 -42
- package/dist/types/tools/list-limit.d.ts +0 -12
- package/dist/types/tools/match-line-format.d.ts +0 -11
- package/dist/types/tools/monitor.d.ts +0 -54
- package/dist/types/tools/output-meta.d.ts +0 -204
- package/dist/types/tools/path-utils.d.ts +0 -147
- package/dist/types/tools/plan-mode-guard.d.ts +0 -18
- package/dist/types/tools/read.d.ts +0 -85
- package/dist/types/tools/recipe/index.d.ts +0 -45
- package/dist/types/tools/recipe/render.d.ts +0 -36
- package/dist/types/tools/recipe/runner.d.ts +0 -60
- package/dist/types/tools/recipe/runners/cargo.d.ts +0 -16
- package/dist/types/tools/recipe/runners/index.d.ts +0 -2
- package/dist/types/tools/recipe/runners/just.d.ts +0 -2
- package/dist/types/tools/recipe/runners/make.d.ts +0 -2
- package/dist/types/tools/recipe/runners/pkg.d.ts +0 -2
- package/dist/types/tools/recipe/runners/task.d.ts +0 -2
- package/dist/types/tools/render-mermaid.d.ts +0 -37
- package/dist/types/tools/render-utils.d.ts +0 -158
- package/dist/types/tools/renderers.d.ts +0 -26
- package/dist/types/tools/resolve.d.ts +0 -80
- package/dist/types/tools/resource-gc.d.ts +0 -54
- package/dist/types/tools/review.d.ts +0 -63
- package/dist/types/tools/search-tool-bm25.d.ts +0 -65
- package/dist/types/tools/search.d.ts +0 -93
- package/dist/types/tools/skill-discovery.d.ts +0 -40
- package/dist/types/tools/skill.d.ts +0 -48
- package/dist/types/tools/sqlite-reader.d.ts +0 -94
- package/dist/types/tools/ssh.d.ts +0 -67
- package/dist/types/tools/subagent-render.d.ts +0 -31
- package/dist/types/tools/subagent.d.ts +0 -117
- package/dist/types/tools/telegram-send.d.ts +0 -39
- package/dist/types/tools/todo-write.d.ts +0 -120
- package/dist/types/tools/tool-errors.d.ts +0 -33
- package/dist/types/tools/tool-result.d.ts +0 -30
- package/dist/types/tools/tool-timeouts.d.ts +0 -56
- package/dist/types/tools/ultragoal-ask-guard.d.ts +0 -12
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/tools/write.d.ts +0 -59
- package/dist/types/tools/yield.d.ts +0 -25
- package/dist/types/tui/code-cell.d.ts +0 -32
- package/dist/types/tui/file-list.d.ts +0 -22
- package/dist/types/tui/hyperlink.d.ts +0 -42
- package/dist/types/tui/index.d.ts +0 -11
- package/dist/types/tui/output-block.d.ts +0 -28
- package/dist/types/tui/status-line.d.ts +0 -18
- package/dist/types/tui/tree-list.d.ts +0 -19
- package/dist/types/tui/types.d.ts +0 -13
- package/dist/types/tui/utils.d.ts +0 -36
- package/dist/types/utils/changelog.d.ts +0 -37
- package/dist/types/utils/clipboard.d.ts +0 -22
- package/dist/types/utils/cmux-workspace.d.ts +0 -52
- package/dist/types/utils/command-args.d.ts +0 -9
- package/dist/types/utils/commit-message-generator.d.ts +0 -7
- package/dist/types/utils/edit-mode.d.ts +0 -13
- package/dist/types/utils/event-bus.d.ts +0 -6
- package/dist/types/utils/external-editor.d.ts +0 -19
- package/dist/types/utils/file-display-mode.d.ts +0 -27
- package/dist/types/utils/file-mentions.d.ts +0 -25
- package/dist/types/utils/git.d.ts +0 -393
- package/dist/types/utils/image-loading.d.ts +0 -26
- package/dist/types/utils/image-resize.d.ts +0 -39
- package/dist/types/utils/lang-from-path.d.ts +0 -8
- package/dist/types/utils/linkedom.d.ts +0 -4
- package/dist/types/utils/markit.d.ts +0 -7
- package/dist/types/utils/open.d.ts +0 -2
- package/dist/types/utils/pasted-image-path.d.ts +0 -32
- package/dist/types/utils/session-color.d.ts +0 -10
- package/dist/types/utils/shell-snapshot.d.ts +0 -5
- package/dist/types/utils/sixel.d.ts +0 -21
- package/dist/types/utils/title-generator.d.ts +0 -31
- package/dist/types/utils/tool-choice.d.ts +0 -20
- package/dist/types/utils/tools-manager.d.ts +0 -9
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/dist/types/web/insane/bridge.d.ts +0 -103
- package/dist/types/web/insane/url-guard.d.ts +0 -25
- package/dist/types/web/kagi.d.ts +0 -23
- package/dist/types/web/parallel.d.ts +0 -58
- package/dist/types/web/scrapers/artifacthub.d.ts +0 -6
- package/dist/types/web/scrapers/arxiv.d.ts +0 -5
- package/dist/types/web/scrapers/aur.d.ts +0 -5
- package/dist/types/web/scrapers/biorxiv.d.ts +0 -5
- package/dist/types/web/scrapers/bluesky.d.ts +0 -5
- package/dist/types/web/scrapers/brew.d.ts +0 -5
- package/dist/types/web/scrapers/cheatsh.d.ts +0 -8
- package/dist/types/web/scrapers/chocolatey.d.ts +0 -5
- package/dist/types/web/scrapers/choosealicense.d.ts +0 -2
- package/dist/types/web/scrapers/cisa-kev.d.ts +0 -5
- package/dist/types/web/scrapers/clojars.d.ts +0 -5
- package/dist/types/web/scrapers/coingecko.d.ts +0 -5
- package/dist/types/web/scrapers/crates-io.d.ts +0 -5
- package/dist/types/web/scrapers/crossref.d.ts +0 -2
- package/dist/types/web/scrapers/devto.d.ts +0 -5
- package/dist/types/web/scrapers/discogs.d.ts +0 -8
- package/dist/types/web/scrapers/discourse.d.ts +0 -5
- package/dist/types/web/scrapers/dockerhub.d.ts +0 -5
- package/dist/types/web/scrapers/docs-rs.d.ts +0 -2
- package/dist/types/web/scrapers/fdroid.d.ts +0 -5
- package/dist/types/web/scrapers/firefox-addons.d.ts +0 -2
- package/dist/types/web/scrapers/flathub.d.ts +0 -2
- package/dist/types/web/scrapers/github-gist.d.ts +0 -5
- package/dist/types/web/scrapers/github.d.ts +0 -12
- package/dist/types/web/scrapers/gitlab.d.ts +0 -5
- package/dist/types/web/scrapers/go-pkg.d.ts +0 -5
- package/dist/types/web/scrapers/hackage.d.ts +0 -5
- package/dist/types/web/scrapers/hackernews.d.ts +0 -2
- package/dist/types/web/scrapers/hex.d.ts +0 -5
- package/dist/types/web/scrapers/huggingface.d.ts +0 -2
- package/dist/types/web/scrapers/iacr.d.ts +0 -5
- package/dist/types/web/scrapers/index.d.ts +0 -84
- package/dist/types/web/scrapers/jetbrains-marketplace.d.ts +0 -2
- package/dist/types/web/scrapers/lemmy.d.ts +0 -2
- package/dist/types/web/scrapers/lobsters.d.ts +0 -5
- package/dist/types/web/scrapers/mastodon.d.ts +0 -5
- package/dist/types/web/scrapers/maven.d.ts +0 -6
- package/dist/types/web/scrapers/mdn.d.ts +0 -2
- package/dist/types/web/scrapers/metacpan.d.ts +0 -5
- package/dist/types/web/scrapers/musicbrainz.d.ts +0 -5
- package/dist/types/web/scrapers/npm.d.ts +0 -5
- package/dist/types/web/scrapers/nuget.d.ts +0 -5
- package/dist/types/web/scrapers/nvd.d.ts +0 -5
- package/dist/types/web/scrapers/ollama.d.ts +0 -2
- package/dist/types/web/scrapers/open-vsx.d.ts +0 -5
- package/dist/types/web/scrapers/opencorporates.d.ts +0 -5
- package/dist/types/web/scrapers/openlibrary.d.ts +0 -5
- package/dist/types/web/scrapers/orcid.d.ts +0 -5
- package/dist/types/web/scrapers/osv.d.ts +0 -5
- package/dist/types/web/scrapers/packagist.d.ts +0 -5
- package/dist/types/web/scrapers/pub-dev.d.ts +0 -5
- package/dist/types/web/scrapers/pubmed.d.ts +0 -5
- package/dist/types/web/scrapers/pypi.d.ts +0 -5
- package/dist/types/web/scrapers/rawg.d.ts +0 -2
- package/dist/types/web/scrapers/readthedocs.d.ts +0 -2
- package/dist/types/web/scrapers/reddit.d.ts +0 -5
- package/dist/types/web/scrapers/repology.d.ts +0 -5
- package/dist/types/web/scrapers/rfc.d.ts +0 -5
- package/dist/types/web/scrapers/rubygems.d.ts +0 -5
- package/dist/types/web/scrapers/searchcode.d.ts +0 -2
- package/dist/types/web/scrapers/sec-edgar.d.ts +0 -5
- package/dist/types/web/scrapers/semantic-scholar.d.ts +0 -2
- package/dist/types/web/scrapers/snapcraft.d.ts +0 -2
- package/dist/types/web/scrapers/sourcegraph.d.ts +0 -2
- package/dist/types/web/scrapers/spdx.d.ts +0 -5
- package/dist/types/web/scrapers/spotify.d.ts +0 -8
- package/dist/types/web/scrapers/stackoverflow.d.ts +0 -6
- package/dist/types/web/scrapers/terraform.d.ts +0 -5
- package/dist/types/web/scrapers/tldr.d.ts +0 -7
- package/dist/types/web/scrapers/twitter.d.ts +0 -5
- package/dist/types/web/scrapers/types.d.ts +0 -83
- package/dist/types/web/scrapers/utils.d.ts +0 -32
- package/dist/types/web/scrapers/vimeo.d.ts +0 -5
- package/dist/types/web/scrapers/vscode-marketplace.d.ts +0 -5
- package/dist/types/web/scrapers/w3c.d.ts +0 -2
- package/dist/types/web/scrapers/wikidata.d.ts +0 -5
- package/dist/types/web/scrapers/wikipedia.d.ts +0 -5
- package/dist/types/web/scrapers/youtube.d.ts +0 -13
- package/dist/types/web/search/index.d.ts +0 -118
- package/dist/types/web/search/provider.d.ts +0 -55
- package/dist/types/web/search/providers/anthropic.d.ts +0 -32
- package/dist/types/web/search/providers/base.d.ts +0 -90
- package/dist/types/web/search/providers/brave.d.ts +0 -27
- package/dist/types/web/search/providers/codex.d.ts +0 -35
- package/dist/types/web/search/providers/duckduckgo.d.ts +0 -57
- package/dist/types/web/search/providers/exa.d.ts +0 -52
- package/dist/types/web/search/providers/gemini.d.ts +0 -57
- package/dist/types/web/search/providers/insane.d.ts +0 -53
- package/dist/types/web/search/providers/jina.d.ts +0 -26
- package/dist/types/web/search/providers/kagi.d.ts +0 -24
- package/dist/types/web/search/providers/kimi.d.ts +0 -27
- package/dist/types/web/search/providers/openai-compatible.d.ts +0 -9
- package/dist/types/web/search/providers/parallel.d.ts +0 -15
- package/dist/types/web/search/providers/perplexity.d.ts +0 -38
- package/dist/types/web/search/providers/searxng.d.ts +0 -44
- package/dist/types/web/search/providers/synthetic.d.ts +0 -21
- package/dist/types/web/search/providers/tavily.d.ts +0 -29
- package/dist/types/web/search/providers/text-citations.d.ts +0 -23
- package/dist/types/web/search/providers/utils.d.ts +0 -88
- package/dist/types/web/search/providers/xai.d.ts +0 -64
- package/dist/types/web/search/providers/zai.d.ts +0 -28
- package/dist/types/web/search/render.d.ts +0 -36
- package/dist/types/web/search/types.d.ts +0 -372
- package/dist/types/web/search/utils.d.ts +0 -4
- package/dist/types/workflow/workflow-intent-diff.d.ts +0 -25
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +0 -50
- package/dist/types/workflow/workflow-intent-report-schema.d.ts +0 -2
- package/dist/types/workflow/workflow-intent-report.d.ts +0 -48
- package/dist/types/workspace-tree.d.ts +0 -42
- package/src/sdk.ts +0 -2623
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as crypto from "node:crypto";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { inflateSync } from "node:zlib";
|
|
5
4
|
import type { WorkflowHudSummary } from "../skill-state/active-state";
|
|
6
5
|
import { buildUltragoalHudSummary as buildWorkflowUltragoalHudSummary } from "../skill-state/workflow-hud";
|
|
7
6
|
import { renderCliWriteReceipt } from "./cli-write-receipt";
|
|
@@ -9,6 +8,7 @@ import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
|
9
8
|
import {
|
|
10
9
|
computeUltragoalPlanGeneration,
|
|
11
10
|
findFreshBatchCloseReceipt,
|
|
11
|
+
findLedgerReceiptEvent,
|
|
12
12
|
requiredUltragoalGoals,
|
|
13
13
|
validateDeferredMemberReceiptFresh,
|
|
14
14
|
validateReceiptFreshBase,
|
|
@@ -33,6 +33,20 @@ import {
|
|
|
33
33
|
writeGuardedJsonAtomic,
|
|
34
34
|
} from "./state-writer";
|
|
35
35
|
|
|
36
|
+
export {
|
|
37
|
+
captureUltragoalRecoverySnapshot,
|
|
38
|
+
parseStrictTerminalTranscript,
|
|
39
|
+
persistUltragoalRecoveryDecision,
|
|
40
|
+
planUltragoalOwnerLossRecovery,
|
|
41
|
+
type UltragoalOwnerLossReceipt,
|
|
42
|
+
type UltragoalRecoveryBinding,
|
|
43
|
+
type UltragoalRecoveryDecision,
|
|
44
|
+
type UltragoalRecoverySnapshot,
|
|
45
|
+
validateOwnerLossBinding,
|
|
46
|
+
validateRawUltragoalEvidence,
|
|
47
|
+
validateRecoveryAdmission,
|
|
48
|
+
validateRecoveryPath,
|
|
49
|
+
} from "./ultragoal-owner-loss-recovery";
|
|
36
50
|
export type UltragoalSkcGoalMode = "aggregate" | "per-story";
|
|
37
51
|
export type UltragoalGoalStatus =
|
|
38
52
|
| "pending"
|
|
@@ -290,18 +304,18 @@ export interface UltragoalCommandResult {
|
|
|
290
304
|
createdPlan?: boolean;
|
|
291
305
|
}
|
|
292
306
|
|
|
293
|
-
interface JsonObject {
|
|
307
|
+
export interface JsonObject {
|
|
294
308
|
[key: string]: unknown;
|
|
295
309
|
}
|
|
296
310
|
|
|
297
|
-
function currentUltragoalSessionId(cwd: string): string {
|
|
311
|
+
export function currentUltragoalSessionId(cwd: string): string {
|
|
298
312
|
return resolveSkcSessionForWrite(cwd, { envSessionId: process.env.SKC_SESSION_ID }).skcSessionId;
|
|
299
313
|
}
|
|
300
314
|
|
|
301
315
|
const TERMINAL_OR_SKIPPED_STATUSES = new Set<UltragoalGoalStatus>(["complete", "superseded"]);
|
|
302
316
|
const CLEAN_ARCHITECT_STATUS = "CLEAR";
|
|
303
317
|
const APPROVE_RECOMMENDATION = "APPROVE";
|
|
304
|
-
const PASSED_STATUS = "passed";
|
|
318
|
+
export const PASSED_STATUS = "passed";
|
|
305
319
|
const NOT_APPLICABLE_STATUS = "not_applicable";
|
|
306
320
|
const COVERED_STATUS = "covered";
|
|
307
321
|
const ACCEPTED_PROOF_STATUSES = new Set([COVERED_STATUS, "passed", "verified"]);
|
|
@@ -363,13 +377,17 @@ export function getUltragoalPaths(cwd: string, sessionId?: string | null): Ultra
|
|
|
363
377
|
};
|
|
364
378
|
}
|
|
365
379
|
|
|
366
|
-
function isEnoent(error: unknown): boolean {
|
|
380
|
+
export function isEnoent(error: unknown): boolean {
|
|
367
381
|
return (
|
|
368
382
|
typeof error === "object" && error !== null && "code" in error && (error as { code?: unknown }).code === "ENOENT"
|
|
369
383
|
);
|
|
370
384
|
}
|
|
371
385
|
|
|
372
|
-
async function appendLedger(
|
|
386
|
+
export async function appendLedger(
|
|
387
|
+
cwd: string,
|
|
388
|
+
event: JsonObject,
|
|
389
|
+
sessionId?: string | null,
|
|
390
|
+
): Promise<UltragoalLedgerEvent> {
|
|
373
391
|
const resolvedSessionId =
|
|
374
392
|
sessionId?.trim() || resolveSkcSessionForWrite(cwd, { envSessionId: process.env.SKC_SESSION_ID }).skcSessionId;
|
|
375
393
|
const paths = getUltragoalPaths(cwd, resolvedSessionId);
|
|
@@ -553,7 +571,7 @@ export async function recordUltragoalNudgeIfBudgetRemaining(input: {
|
|
|
553
571
|
);
|
|
554
572
|
}
|
|
555
573
|
|
|
556
|
-
async function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string | null): Promise<void> {
|
|
574
|
+
export async function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string | null): Promise<void> {
|
|
557
575
|
const resolvedSessionId =
|
|
558
576
|
sessionId?.trim() || resolveSkcSessionForWrite(cwd, { envSessionId: process.env.SKC_SESSION_ID }).skcSessionId;
|
|
559
577
|
const paths = getUltragoalPaths(cwd, resolvedSessionId);
|
|
@@ -572,16 +590,32 @@ async function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string |
|
|
|
572
590
|
|
|
573
591
|
function chooseReceiptKind(
|
|
574
592
|
plan: UltragoalPlan,
|
|
593
|
+
ledger: readonly UltragoalLedgerEvent[],
|
|
575
594
|
goal: UltragoalGoal,
|
|
576
595
|
status: UltragoalGoalStatus,
|
|
577
596
|
): UltragoalReceiptKind {
|
|
578
597
|
if (plan.skcGoalMode === "per-story") return "per-goal";
|
|
579
598
|
if (status !== "complete") return "per-goal";
|
|
599
|
+
// A non-final validation-batch member must always carry a per-goal
|
|
600
|
+
// deferred receipt; only the batch's final goal may close the batch and
|
|
601
|
+
// (in aggregate mode) carry the final-aggregate receipt. Without this, a
|
|
602
|
+
// context-stale re-verification replay of a member could mint an invalid
|
|
603
|
+
// final-aggregate receipt with validationBatch.role "deferred-member".
|
|
604
|
+
if (goal.validationBatch && goal.validationBatch.finalGoalId !== goal.id) return "per-goal";
|
|
580
605
|
const requiredGoals = requiredUltragoalGoals(plan);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
584
|
-
|
|
606
|
+
// Only a still-fresh final-aggregate receipt on another goal defers this
|
|
607
|
+
// checkpoint to per-goal. A stale one (e.g. staled by `steer add_subgoal`
|
|
608
|
+
// appending goals after a terminal run) must not suppress re-minting,
|
|
609
|
+
// otherwise the run can never regain a verifiable final-aggregate receipt:
|
|
610
|
+
// the completion guard demands a fresh final-aggregate receipt while this
|
|
611
|
+
// gate would keep answering per-goal forever.
|
|
612
|
+
const existingFreshFinalAggregateGoal = requiredGoals.find(item => {
|
|
613
|
+
if (item.id === goal.id) return false;
|
|
614
|
+
const receipt = item.completionVerification;
|
|
615
|
+
if (receipt?.receiptKind !== "final-aggregate") return false;
|
|
616
|
+
return validateReceiptFreshBase({ plan, ledger, goal: item, receipt, receiptKind: "final-aggregate" }) === null;
|
|
617
|
+
});
|
|
618
|
+
if (existingFreshFinalAggregateGoal) return "per-goal";
|
|
585
619
|
const unfinishedRequiredGoals = requiredGoals.filter(
|
|
586
620
|
item => item.id !== goal.id && !TERMINAL_OR_SKIPPED_STATUSES.has(item.status),
|
|
587
621
|
);
|
|
@@ -672,10 +706,10 @@ function buildCompletionReceipt(input: {
|
|
|
672
706
|
};
|
|
673
707
|
}
|
|
674
708
|
|
|
675
|
-
function nonEmptyString(value: unknown): string | null {
|
|
709
|
+
export function nonEmptyString(value: unknown): string | null {
|
|
676
710
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
677
711
|
}
|
|
678
|
-
function stringArray(value: unknown): string[] | null {
|
|
712
|
+
export function stringArray(value: unknown): string[] | null {
|
|
679
713
|
return Array.isArray(value) && value.every(item => typeof item === "string") ? value.map(item => item.trim()) : null;
|
|
680
714
|
}
|
|
681
715
|
|
|
@@ -725,7 +759,7 @@ function pipelineMetadataHashBasis(metadata: Omit<UltragoalPipelineMetadata, "me
|
|
|
725
759
|
};
|
|
726
760
|
}
|
|
727
761
|
|
|
728
|
-
function hashPipelineMetadata(metadata: Omit<UltragoalPipelineMetadata, "metadataHash">): string {
|
|
762
|
+
export function hashPipelineMetadata(metadata: Omit<UltragoalPipelineMetadata, "metadataHash">): string {
|
|
729
763
|
return hashStructuredValue(pipelineMetadataHashBasis(metadata));
|
|
730
764
|
}
|
|
731
765
|
|
|
@@ -841,7 +875,7 @@ function pipelineMetadataConflictsWithValidationBatch(metadata: UltragoalPipelin
|
|
|
841
875
|
return metadata?.eligible === true || metadata?.source === "original_plan_graph" || metadata?.source === "steering";
|
|
842
876
|
}
|
|
843
877
|
|
|
844
|
-
function validateValidationBatchPipelineExclusion(goal: UltragoalGoal): void {
|
|
878
|
+
export function validateValidationBatchPipelineExclusion(goal: UltragoalGoal): void {
|
|
845
879
|
if (goal.validationBatch && pipelineMetadataConflictsWithValidationBatch(goal.pipelineMetadata)) {
|
|
846
880
|
throw new Error(`Goal ${goal.id} cannot combine validationBatch with eligible pipeline metadata`);
|
|
847
881
|
}
|
|
@@ -1034,28 +1068,28 @@ function normalizePipelineMetadataRecord(value: unknown, goalIds: ReadonlySet<st
|
|
|
1034
1068
|
return withPipelineMetadataHash(basis);
|
|
1035
1069
|
}
|
|
1036
1070
|
|
|
1037
|
-
function targetsAreDisjoint(left: UltragoalPipelineTargets, right: UltragoalPipelineTargets): boolean {
|
|
1071
|
+
export function targetsAreDisjoint(left: UltragoalPipelineTargets, right: UltragoalPipelineTargets): boolean {
|
|
1038
1072
|
return (
|
|
1039
1073
|
left.files.every(file => !right.files.includes(file)) &&
|
|
1040
1074
|
left.surfaces.every(surface => !right.surfaces.includes(surface))
|
|
1041
1075
|
);
|
|
1042
1076
|
}
|
|
1043
1077
|
|
|
1044
|
-
function targetsOverlap(left: UltragoalPipelineTargets, right: UltragoalPipelineTargets): boolean {
|
|
1078
|
+
export function targetsOverlap(left: UltragoalPipelineTargets, right: UltragoalPipelineTargets): boolean {
|
|
1045
1079
|
return (
|
|
1046
1080
|
left.files.some(file => right.files.includes(file)) ||
|
|
1047
1081
|
left.surfaces.some(surface => right.surfaces.includes(surface))
|
|
1048
1082
|
);
|
|
1049
1083
|
}
|
|
1050
1084
|
|
|
1051
|
-
function requireNonEmptyPipelineTargets(value: unknown, fieldName: string): UltragoalPipelineTargets {
|
|
1085
|
+
export function requireNonEmptyPipelineTargets(value: unknown, fieldName: string): UltragoalPipelineTargets {
|
|
1052
1086
|
const targets = normalizePipelineTargets(value, fieldName);
|
|
1053
1087
|
if (targets.files.length === 0 && targets.surfaces.length === 0)
|
|
1054
1088
|
throw new Error(`${fieldName} requires files or surfaces`);
|
|
1055
1089
|
return targets;
|
|
1056
1090
|
}
|
|
1057
1091
|
|
|
1058
|
-
function collectPipelineBlockerFootprints(result: JsonObject, fieldName: string): UltragoalPipelineTargets[] {
|
|
1092
|
+
export function collectPipelineBlockerFootprints(result: JsonObject, fieldName: string): UltragoalPipelineTargets[] {
|
|
1059
1093
|
const raw = Array.isArray(result.blockers)
|
|
1060
1094
|
? result.blockers
|
|
1061
1095
|
: Array.isArray(result.blockerFootprints)
|
|
@@ -1073,12 +1107,12 @@ function pipelineTargetsCoverPath(targets: UltragoalPipelineTargets, filePath: s
|
|
|
1073
1107
|
return targets.files.some(target => normalized === target || normalized.startsWith(`${target}/`));
|
|
1074
1108
|
}
|
|
1075
1109
|
|
|
1076
|
-
function pipelinePeer(plan: UltragoalPlan, metadata: UltragoalPipelineMetadata): UltragoalGoal | undefined {
|
|
1110
|
+
export function pipelinePeer(plan: UltragoalPlan, metadata: UltragoalPipelineMetadata): UltragoalGoal | undefined {
|
|
1077
1111
|
const peerId = metadata.goalId === metadata.priorGoalId ? metadata.nextGoalId : metadata.priorGoalId;
|
|
1078
1112
|
return peerId ? plan.goals.find(goal => goal.id === peerId) : undefined;
|
|
1079
1113
|
}
|
|
1080
1114
|
|
|
1081
|
-
function handleIdsFromValue(value: JsonObject | JsonObject[], fieldName: string): string[] {
|
|
1115
|
+
export function handleIdsFromValue(value: JsonObject | JsonObject[], fieldName: string): string[] {
|
|
1082
1116
|
const records = Array.isArray(value) ? value : [value];
|
|
1083
1117
|
const ids = records.map(
|
|
1084
1118
|
(record, index) =>
|
|
@@ -1091,13 +1125,13 @@ function handleIdsFromValue(value: JsonObject | JsonObject[], fieldName: string)
|
|
|
1091
1125
|
return ids;
|
|
1092
1126
|
}
|
|
1093
1127
|
|
|
1094
|
-
function resultHandleIds(value: JsonObject, fieldName: string): string[] {
|
|
1128
|
+
export function resultHandleIds(value: JsonObject, fieldName: string): string[] {
|
|
1095
1129
|
const ids = stringArray(value.handleIds) ?? stringArray(value.handles) ?? [];
|
|
1096
1130
|
if (ids.length === 0) throw new Error(`${fieldName} requires handleIds`);
|
|
1097
1131
|
return ids;
|
|
1098
1132
|
}
|
|
1099
1133
|
|
|
1100
|
-
function requireCoveredHandles(expected: readonly string[], actual: readonly string[], fieldName: string): void {
|
|
1134
|
+
export function requireCoveredHandles(expected: readonly string[], actual: readonly string[], fieldName: string): void {
|
|
1101
1135
|
const missing = expected.filter(id => !actual.includes(id));
|
|
1102
1136
|
if (missing.length > 0) throw new Error(`${fieldName} is missing handle coverage for ${missing.join(", ")}`);
|
|
1103
1137
|
}
|
|
@@ -1185,7 +1219,7 @@ function currentPipelineHash(metadata: UltragoalPipelineMetadata): string {
|
|
|
1185
1219
|
return hashPipelineMetadata(metadata);
|
|
1186
1220
|
}
|
|
1187
1221
|
|
|
1188
|
-
function requireFreshPipelineMetadata(goal: UltragoalGoal): UltragoalPipelineMetadata {
|
|
1222
|
+
export function requireFreshPipelineMetadata(goal: UltragoalGoal): UltragoalPipelineMetadata {
|
|
1189
1223
|
const metadata = goal.pipelineMetadata;
|
|
1190
1224
|
if (!metadata) throw new Error(`Goal ${goal.id} has no pipeline metadata`);
|
|
1191
1225
|
if (metadata.metadataHash !== currentPipelineHash(metadata))
|
|
@@ -1193,7 +1227,7 @@ function requireFreshPipelineMetadata(goal: UltragoalGoal): UltragoalPipelineMet
|
|
|
1193
1227
|
return metadata;
|
|
1194
1228
|
}
|
|
1195
1229
|
|
|
1196
|
-
function openPipelineOverlap(
|
|
1230
|
+
export function openPipelineOverlap(
|
|
1197
1231
|
plan: UltragoalPlan,
|
|
1198
1232
|
): { prior: UltragoalGoal; next: UltragoalGoal; overlapId: string } | null {
|
|
1199
1233
|
const openGoals = plan.goals.filter(goal => goal.pipelineMetadata?.overlap === "open");
|
|
@@ -1532,14 +1566,14 @@ export interface UltragoalRunCompletionState {
|
|
|
1532
1566
|
hasBlockers: boolean;
|
|
1533
1567
|
needsFinalAggregateReceipt: boolean;
|
|
1534
1568
|
}
|
|
1535
|
-
function requireJsonObjectValue(value: unknown, fieldName: string): JsonObject {
|
|
1569
|
+
export function requireJsonObjectValue(value: unknown, fieldName: string): JsonObject {
|
|
1536
1570
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
1537
1571
|
throw new Error(`${fieldName} must be an object`);
|
|
1538
1572
|
if (Object.keys(value).length === 0) throw new Error(`${fieldName} must be non-empty`);
|
|
1539
1573
|
return value as JsonObject;
|
|
1540
1574
|
}
|
|
1541
1575
|
|
|
1542
|
-
function requireJsonObjectOrArrayValue(value: unknown, fieldName: string): JsonObject | JsonObject[] {
|
|
1576
|
+
export function requireJsonObjectOrArrayValue(value: unknown, fieldName: string): JsonObject | JsonObject[] {
|
|
1543
1577
|
if (Array.isArray(value)) {
|
|
1544
1578
|
if (value.length === 0) throw new Error(`${fieldName} must be non-empty`);
|
|
1545
1579
|
return value.map((item, index) => requireJsonObjectValue(item, `${fieldName}[${index}]`));
|
|
@@ -1559,278 +1593,13 @@ async function readRequiredJsonObjectOrArray(
|
|
|
1559
1593
|
return requireJsonObjectOrArrayValue(await readStructuredValue(cwd, value), fieldName);
|
|
1560
1594
|
}
|
|
1561
1595
|
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
if (prior.status !== "active")
|
|
1568
|
-
throw new Error(`Prior goal ${prior.id} must be active before pipeline overlap starts`);
|
|
1569
|
-
if (next.status !== "pending" && next.status !== "failed")
|
|
1570
|
-
throw new Error(`Next goal ${next.id} must be pending or retryable failed`);
|
|
1571
|
-
validateValidationBatchPipelineExclusion(prior);
|
|
1572
|
-
validateValidationBatchPipelineExclusion(next);
|
|
1573
|
-
if (prior.validationBatch || next.validationBatch)
|
|
1574
|
-
throw new Error("pipeline overlap cannot start for validation batch goals");
|
|
1575
|
-
const priorMetadata = requireFreshPipelineMetadata(prior);
|
|
1576
|
-
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
1577
|
-
if (!priorMetadata.eligible || !nextMetadata.eligible)
|
|
1578
|
-
throw new Error("pipeline overlap requires eligible original-plan metadata on both goals");
|
|
1579
|
-
if (!priorMetadata.independentOf.includes(next.id) || !nextMetadata.independentOf.includes(prior.id)) {
|
|
1580
|
-
throw new Error("pipeline overlap requires symmetric original independence");
|
|
1581
|
-
}
|
|
1582
|
-
if (!targetsAreDisjoint(priorMetadata.targets, nextMetadata.targets))
|
|
1583
|
-
throw new Error("pipeline overlap requires disjoint files and surfaces");
|
|
1584
|
-
}
|
|
1596
|
+
import {
|
|
1597
|
+
joinUltragoalPipelineOverlap,
|
|
1598
|
+
rebaselineUltragoalPipelineOverlap,
|
|
1599
|
+
startUltragoalPipelineOverlap,
|
|
1600
|
+
} from "./ultragoal-pipeline";
|
|
1585
1601
|
|
|
1586
|
-
|
|
1587
|
-
return {
|
|
1588
|
-
priorMetadataHash: prior.pipelineMetadata?.metadataHash,
|
|
1589
|
-
nextMetadataHash: next.pipelineMetadata?.metadataHash,
|
|
1590
|
-
priorTargets: prior.pipelineMetadata?.targets,
|
|
1591
|
-
nextTargets: next.pipelineMetadata?.targets,
|
|
1592
|
-
};
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
function pipelineReceipt(
|
|
1596
|
-
cwd: string,
|
|
1597
|
-
event: string,
|
|
1598
|
-
overlapId: string,
|
|
1599
|
-
prior: UltragoalGoal,
|
|
1600
|
-
next: UltragoalGoal,
|
|
1601
|
-
): UltragoalPipelineOverlapReceipt {
|
|
1602
|
-
const paths = getUltragoalPaths(cwd, currentUltragoalSessionId(cwd));
|
|
1603
|
-
return {
|
|
1604
|
-
ok: true,
|
|
1605
|
-
event,
|
|
1606
|
-
overlap_id: overlapId,
|
|
1607
|
-
prior_goal_id: prior.id,
|
|
1608
|
-
next_goal_id: next.id,
|
|
1609
|
-
status: next.pipelineMetadata?.overlap,
|
|
1610
|
-
next_goal_status: next.status,
|
|
1611
|
-
goals_path: paths.goalsPath,
|
|
1612
|
-
ledger_path: paths.ledgerPath,
|
|
1613
|
-
};
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
export async function startUltragoalPipelineOverlap(input: {
|
|
1617
|
-
cwd: string;
|
|
1618
|
-
priorGoalId: string;
|
|
1619
|
-
nextGoalId: string;
|
|
1620
|
-
reviewHandles: JsonObject | JsonObject[];
|
|
1621
|
-
qaHandles: JsonObject | JsonObject[];
|
|
1622
|
-
implementationHandle: JsonObject;
|
|
1623
|
-
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
1624
|
-
const plan = await readUltragoalPlan(input.cwd);
|
|
1625
|
-
if (!plan) throw new Error("No ultragoal plan found. Run `skc ultragoal create-goals --brief ...` first.");
|
|
1626
|
-
const prior = plan.goals.find(goal => goal.id === input.priorGoalId);
|
|
1627
|
-
const next = plan.goals.find(goal => goal.id === input.nextGoalId);
|
|
1628
|
-
if (!prior || !next) throw new Error("start-pipeline-overlap requires existing prior and next goal ids");
|
|
1629
|
-
const reviewHandles = requireJsonObjectOrArrayValue(input.reviewHandles, "review handles");
|
|
1630
|
-
const qaHandles = requireJsonObjectOrArrayValue(input.qaHandles, "QA handles");
|
|
1631
|
-
requireJsonObjectValue(input.implementationHandle, "implementation handle");
|
|
1632
|
-
requirePipelineStartable(plan, prior, next);
|
|
1633
|
-
const now = new Date().toISOString();
|
|
1634
|
-
const overlapId = `pipeline-${crypto.randomUUID()}`;
|
|
1635
|
-
const priorMetadata = requireFreshPipelineMetadata(prior);
|
|
1636
|
-
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
1637
|
-
const priorOpenMetadata = {
|
|
1638
|
-
...priorMetadata,
|
|
1639
|
-
overlap: "open" as const,
|
|
1640
|
-
overlapId,
|
|
1641
|
-
priorGoalId: prior.id,
|
|
1642
|
-
nextGoalId: next.id,
|
|
1643
|
-
};
|
|
1644
|
-
const nextOpenMetadata = {
|
|
1645
|
-
...nextMetadata,
|
|
1646
|
-
overlap: "open" as const,
|
|
1647
|
-
overlapId,
|
|
1648
|
-
priorGoalId: prior.id,
|
|
1649
|
-
nextGoalId: next.id,
|
|
1650
|
-
};
|
|
1651
|
-
prior.pipelineMetadata = { ...priorOpenMetadata, metadataHash: hashPipelineMetadata(priorOpenMetadata) };
|
|
1652
|
-
next.pipelineMetadata = { ...nextOpenMetadata, metadataHash: hashPipelineMetadata(nextOpenMetadata) };
|
|
1653
|
-
prior.updatedAt = now;
|
|
1654
|
-
next.updatedAt = now;
|
|
1655
|
-
next.status = "active";
|
|
1656
|
-
next.startedAt = next.startedAt ?? now;
|
|
1657
|
-
plan.updatedAt = now;
|
|
1658
|
-
await writePlan(input.cwd, plan);
|
|
1659
|
-
const refs = pipelineEventRefs(prior, next);
|
|
1660
|
-
const expectedReviewHandleIds = handleIdsFromValue(reviewHandles, "review");
|
|
1661
|
-
const expectedQaHandleIds = handleIdsFromValue(qaHandles, "QA");
|
|
1662
|
-
await appendLedger(input.cwd, {
|
|
1663
|
-
event: "pipeline_overlap_started",
|
|
1664
|
-
eventId: crypto.randomUUID(),
|
|
1665
|
-
timestamp: now,
|
|
1666
|
-
schemaVersion: 1,
|
|
1667
|
-
overlapId,
|
|
1668
|
-
priorGoalId: prior.id,
|
|
1669
|
-
nextGoalId: next.id,
|
|
1670
|
-
reviewHandles,
|
|
1671
|
-
reviewHandleIds: expectedReviewHandleIds,
|
|
1672
|
-
qaHandles,
|
|
1673
|
-
qaHandleIds: expectedQaHandleIds,
|
|
1674
|
-
implementationHandle: input.implementationHandle,
|
|
1675
|
-
...refs,
|
|
1676
|
-
});
|
|
1677
|
-
await appendLedger(input.cwd, { event: "goal_started", goalId: next.id, pipelineOverlapId: overlapId });
|
|
1678
|
-
return pipelineReceipt(input.cwd, "pipeline_overlap_started", overlapId, prior, next);
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
function resultStatus(value: JsonObject, fieldName: string): string {
|
|
1682
|
-
const status = nonEmptyString(value.status) ?? nonEmptyString(value.verdict) ?? nonEmptyString(value.result);
|
|
1683
|
-
if (!status) throw new Error(`${fieldName} requires status, verdict, or result`);
|
|
1684
|
-
return status.toLowerCase();
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
function requireCleanPipelineResult(result: JsonObject, expectedHandleIds: readonly string[], fieldName: string): void {
|
|
1688
|
-
const status = resultStatus(result, fieldName);
|
|
1689
|
-
if (!["passed", "pass", "approved", "clear"].includes(status)) throw new Error(`${fieldName} did not pass`);
|
|
1690
|
-
const evidence = nonEmptyString(result.evidence);
|
|
1691
|
-
if (!evidence || !isSubstantiveEvidence(evidence)) throw new Error(`${fieldName} requires substantive evidence`);
|
|
1692
|
-
requireCoveredHandles(expectedHandleIds, resultHandleIds(result, fieldName), fieldName);
|
|
1693
|
-
if (collectPipelineBlockerFootprints(result, fieldName).length > 0)
|
|
1694
|
-
throw new Error(`${fieldName} cannot clean-join with blockers`);
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
function pipelineStartEventHandleIds(
|
|
1698
|
-
ledger: UltragoalLedgerEvent[],
|
|
1699
|
-
overlapId: string,
|
|
1700
|
-
): { review: string[]; qa: string[] } {
|
|
1701
|
-
const event = ledger.find(row => row.event === "pipeline_overlap_started" && row.overlapId === overlapId) as
|
|
1702
|
-
| JsonObject
|
|
1703
|
-
| undefined;
|
|
1704
|
-
if (!event) throw new Error(`No pipeline_overlap_started event found for ${overlapId}`);
|
|
1705
|
-
const review =
|
|
1706
|
-
stringArray(event.reviewHandleIds) ??
|
|
1707
|
-
handleIdsFromValue(requireJsonObjectOrArrayValue(event.reviewHandles, "review handles"), "review");
|
|
1708
|
-
const qa =
|
|
1709
|
-
stringArray(event.qaHandleIds) ??
|
|
1710
|
-
handleIdsFromValue(requireJsonObjectOrArrayValue(event.qaHandles, "QA handles"), "QA");
|
|
1711
|
-
return { review, qa };
|
|
1712
|
-
}
|
|
1713
|
-
|
|
1714
|
-
export async function joinUltragoalPipelineOverlap(input: {
|
|
1715
|
-
cwd: string;
|
|
1716
|
-
overlapId: string;
|
|
1717
|
-
reviewResult: JsonObject;
|
|
1718
|
-
qaResult: JsonObject;
|
|
1719
|
-
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
1720
|
-
const plan = await readUltragoalPlan(input.cwd);
|
|
1721
|
-
if (!plan) throw new Error("No ultragoal plan found. Run `skc ultragoal create-goals --brief ...` first.");
|
|
1722
|
-
const overlap = openPipelineOverlap(plan);
|
|
1723
|
-
if (!overlap || overlap.overlapId !== input.overlapId)
|
|
1724
|
-
throw new Error(`No open pipeline overlap found for ${input.overlapId}`);
|
|
1725
|
-
const { prior, next, overlapId } = overlap;
|
|
1726
|
-
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
1727
|
-
const ledger = await readUltragoalLedger(input.cwd);
|
|
1728
|
-
const expectedHandles = pipelineStartEventHandleIds(ledger, overlapId);
|
|
1729
|
-
const reviewBlockers = collectPipelineBlockerFootprints(input.reviewResult, "review result");
|
|
1730
|
-
const qaBlockers = collectPipelineBlockerFootprints(input.qaResult, "QA result");
|
|
1731
|
-
const blockerFootprints = [...reviewBlockers, ...qaBlockers];
|
|
1732
|
-
let state: UltragoalPipelineOverlapState;
|
|
1733
|
-
let event: UltragoalPipelineLedgerEventName;
|
|
1734
|
-
if (blockerFootprints.length === 0) {
|
|
1735
|
-
try {
|
|
1736
|
-
requireCleanPipelineResult(input.reviewResult, expectedHandles.review, "review result");
|
|
1737
|
-
requireCleanPipelineResult(input.qaResult, expectedHandles.qa, "QA result");
|
|
1738
|
-
state = "joined_clean";
|
|
1739
|
-
event = "pipeline_overlap_joined";
|
|
1740
|
-
} catch {
|
|
1741
|
-
state = "quarantine_required";
|
|
1742
|
-
event = "pipeline_overlap_quarantined";
|
|
1743
|
-
}
|
|
1744
|
-
} else if (blockerFootprints.every(footprint => !targetsOverlap(footprint, nextMetadata.targets))) {
|
|
1745
|
-
state = "blocked_disjoint_continue";
|
|
1746
|
-
event = "pipeline_overlap_joined";
|
|
1747
|
-
} else {
|
|
1748
|
-
state = "quarantine_required";
|
|
1749
|
-
event = "pipeline_overlap_quarantined";
|
|
1750
|
-
}
|
|
1751
|
-
const now = new Date().toISOString();
|
|
1752
|
-
for (const goal of [prior, next]) {
|
|
1753
|
-
const metadata = requireFreshPipelineMetadata(goal);
|
|
1754
|
-
const joinedMetadata = { ...metadata, overlap: state, blockerFootprints };
|
|
1755
|
-
goal.pipelineMetadata = { ...joinedMetadata, metadataHash: hashPipelineMetadata(joinedMetadata) };
|
|
1756
|
-
goal.updatedAt = now;
|
|
1757
|
-
}
|
|
1758
|
-
if (state === "quarantine_required") next.status = "blocked";
|
|
1759
|
-
plan.updatedAt = now;
|
|
1760
|
-
await writePlan(input.cwd, plan);
|
|
1761
|
-
await appendLedger(input.cwd, {
|
|
1762
|
-
event,
|
|
1763
|
-
eventId: crypto.randomUUID(),
|
|
1764
|
-
timestamp: now,
|
|
1765
|
-
schemaVersion: 1,
|
|
1766
|
-
overlapId,
|
|
1767
|
-
priorGoalId: prior.id,
|
|
1768
|
-
nextGoalId: next.id,
|
|
1769
|
-
status: state,
|
|
1770
|
-
reviewResult: input.reviewResult,
|
|
1771
|
-
qaResult: input.qaResult,
|
|
1772
|
-
blockerFootprints,
|
|
1773
|
-
...pipelineEventRefs(prior, next),
|
|
1774
|
-
});
|
|
1775
|
-
return pipelineReceipt(input.cwd, event, overlapId, prior, next);
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
export async function rebaselineUltragoalPipelineOverlap(input: {
|
|
1779
|
-
cwd: string;
|
|
1780
|
-
overlapId: string;
|
|
1781
|
-
goalId: string;
|
|
1782
|
-
evidence: string;
|
|
1783
|
-
targetState: JsonObject;
|
|
1784
|
-
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
1785
|
-
const plan = await readUltragoalPlan(input.cwd);
|
|
1786
|
-
if (!plan) throw new Error("No ultragoal plan found. Run `skc ultragoal create-goals --brief ...` first.");
|
|
1787
|
-
const goal = plan.goals.find(item => item.id === input.goalId);
|
|
1788
|
-
if (!goal) throw new Error(`No ultragoal goal found for ${input.goalId}.`);
|
|
1789
|
-
const evidence = input.evidence.trim();
|
|
1790
|
-
if (!isSubstantiveEvidence(evidence)) throw new Error("rebaseline-pipeline-overlap requires substantive evidence");
|
|
1791
|
-
const targetState = requireNonEmptyPipelineTargets(input.targetState, "target state");
|
|
1792
|
-
const metadata = requireFreshPipelineMetadata(goal);
|
|
1793
|
-
if (metadata.overlap !== "quarantine_required" || metadata.overlapId !== input.overlapId) {
|
|
1794
|
-
throw new Error(`Goal ${goal.id} is not quarantined for overlap ${input.overlapId}`);
|
|
1795
|
-
}
|
|
1796
|
-
for (const footprint of metadata.blockerFootprints ?? []) {
|
|
1797
|
-
if (targetsOverlap(footprint, targetState))
|
|
1798
|
-
throw new Error("rebaseline-pipeline-overlap target state overlaps unresolved blocker footprints");
|
|
1799
|
-
}
|
|
1800
|
-
const now = new Date().toISOString();
|
|
1801
|
-
const rebaselinedMetadata = { ...metadata, overlap: "rebaseline_complete" as const, targets: targetState };
|
|
1802
|
-
goal.pipelineMetadata = { ...rebaselinedMetadata, metadataHash: hashPipelineMetadata(rebaselinedMetadata) };
|
|
1803
|
-
goal.status = "active";
|
|
1804
|
-
goal.evidence = evidence;
|
|
1805
|
-
goal.updatedAt = now;
|
|
1806
|
-
plan.updatedAt = now;
|
|
1807
|
-
await writePlan(input.cwd, plan);
|
|
1808
|
-
const peer = pipelinePeer(plan, metadata);
|
|
1809
|
-
await appendLedger(input.cwd, {
|
|
1810
|
-
event: "pipeline_overlap_rebaselined",
|
|
1811
|
-
eventId: crypto.randomUUID(),
|
|
1812
|
-
timestamp: now,
|
|
1813
|
-
schemaVersion: 1,
|
|
1814
|
-
overlapId: input.overlapId,
|
|
1815
|
-
priorGoalId: metadata.priorGoalId ?? peer?.id ?? "",
|
|
1816
|
-
nextGoalId: metadata.nextGoalId ?? goal.id,
|
|
1817
|
-
goalId: goal.id,
|
|
1818
|
-
evidence,
|
|
1819
|
-
targetState,
|
|
1820
|
-
metadataHash: goal.pipelineMetadata.metadataHash,
|
|
1821
|
-
});
|
|
1822
|
-
const paths = getUltragoalPaths(input.cwd, currentUltragoalSessionId(input.cwd));
|
|
1823
|
-
return {
|
|
1824
|
-
ok: true,
|
|
1825
|
-
event: "pipeline_overlap_rebaselined",
|
|
1826
|
-
overlap_id: input.overlapId,
|
|
1827
|
-
prior_goal_id: metadata.priorGoalId ?? peer?.id ?? "",
|
|
1828
|
-
goal_id: goal.id,
|
|
1829
|
-
status: goal.pipelineMetadata.overlap,
|
|
1830
|
-
goals_path: paths.goalsPath,
|
|
1831
|
-
ledger_path: paths.ledgerPath,
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1602
|
+
export { joinUltragoalPipelineOverlap, rebaselineUltragoalPipelineOverlap, startUltragoalPipelineOverlap };
|
|
1834
1603
|
|
|
1835
1604
|
export function getUltragoalRunCompletionState(
|
|
1836
1605
|
plan: UltragoalPlan,
|
|
@@ -1885,11 +1654,11 @@ async function readStructuredValue(cwd: string, value: string): Promise<unknown>
|
|
|
1885
1654
|
throw error;
|
|
1886
1655
|
}
|
|
1887
1656
|
}
|
|
1888
|
-
function qualityGateObject(value: unknown): JsonObject | null {
|
|
1657
|
+
export function qualityGateObject(value: unknown): JsonObject | null {
|
|
1889
1658
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? (value as JsonObject) : null;
|
|
1890
1659
|
}
|
|
1891
1660
|
|
|
1892
|
-
function nonEmptyStringArray(value: unknown): string[] | null {
|
|
1661
|
+
export function nonEmptyStringArray(value: unknown): string[] | null {
|
|
1893
1662
|
if (!Array.isArray(value)) return null;
|
|
1894
1663
|
const strings = value.filter(item => typeof item === "string" && item.trim().length > 0);
|
|
1895
1664
|
return strings.length === value.length && strings.length > 0 ? strings : null;
|
|
@@ -1906,20 +1675,20 @@ function requireEmptyBlockers(value: unknown, fieldName: string): void {
|
|
|
1906
1675
|
throw new Error(`qualityGate ${fieldName} must be an empty blockers array`);
|
|
1907
1676
|
}
|
|
1908
1677
|
}
|
|
1909
|
-
function requireQualityGateObject(value: unknown, fieldName: string): JsonObject {
|
|
1678
|
+
export function requireQualityGateObject(value: unknown, fieldName: string): JsonObject {
|
|
1910
1679
|
const object = qualityGateObject(value);
|
|
1911
1680
|
if (!object) throw new Error(`qualityGate ${fieldName} must be an object`);
|
|
1912
1681
|
return object;
|
|
1913
1682
|
}
|
|
1914
1683
|
|
|
1915
|
-
function requireObjectArray(value: unknown, fieldName: string): JsonObject[] {
|
|
1684
|
+
export function requireObjectArray(value: unknown, fieldName: string): JsonObject[] {
|
|
1916
1685
|
if (!Array.isArray(value) || value.length === 0) {
|
|
1917
1686
|
throw new Error(`qualityGate ${fieldName} must be a non-empty object array`);
|
|
1918
1687
|
}
|
|
1919
1688
|
return value.map((item, index) => requireQualityGateObject(item, `${fieldName}[${index}]`));
|
|
1920
1689
|
}
|
|
1921
1690
|
|
|
1922
|
-
function requiredStringField(row: JsonObject, key: string, fieldName: string): string {
|
|
1691
|
+
export function requiredStringField(row: JsonObject, key: string, fieldName: string): string {
|
|
1923
1692
|
const value = row[key];
|
|
1924
1693
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1925
1694
|
const hint =
|
|
@@ -1968,7 +1737,7 @@ function requireSuccessfulRowOutcome(row: JsonObject, fieldName: string): void {
|
|
|
1968
1737
|
}
|
|
1969
1738
|
}
|
|
1970
1739
|
|
|
1971
|
-
function requireStringLinks(value: unknown, fieldName: string): string[] {
|
|
1740
|
+
export function requireStringLinks(value: unknown, fieldName: string): string[] {
|
|
1972
1741
|
const strings = nonEmptyStringArray(value);
|
|
1973
1742
|
if (!strings) throw new Error(`qualityGate ${fieldName} must be a non-empty string array`);
|
|
1974
1743
|
return strings.map(item => item.trim());
|
|
@@ -1989,7 +1758,7 @@ function buildRowIdMap(rows: JsonObject[], fieldName: string): Map<string, JsonO
|
|
|
1989
1758
|
return ids;
|
|
1990
1759
|
}
|
|
1991
1760
|
|
|
1992
|
-
function requireResolvedLinks(ids: string[], map: Map<string, JsonObject>, fieldName: string): void {
|
|
1761
|
+
export function requireResolvedLinks(ids: string[], map: Map<string, JsonObject>, fieldName: string): void {
|
|
1993
1762
|
for (const id of ids) {
|
|
1994
1763
|
if (!map.has(id)) throw new Error(`qualityGate ${fieldName} references unknown id ${id}`);
|
|
1995
1764
|
}
|
|
@@ -2005,11 +1774,11 @@ function successfulLinkedRows(ids: string[], map: Map<string, JsonObject>, field
|
|
|
2005
1774
|
return rows;
|
|
2006
1775
|
}
|
|
2007
1776
|
|
|
2008
|
-
function normalizedEvidenceKind(row: JsonObject): string {
|
|
1777
|
+
export function normalizedEvidenceKind(row: JsonObject): string {
|
|
2009
1778
|
return requiredStringField(row, "kind", "executorQa.artifactRefs[]").toLowerCase().replaceAll("_", "-");
|
|
2010
1779
|
}
|
|
2011
1780
|
|
|
2012
|
-
function evidenceKindMatches(kind: string, words: string[]): boolean {
|
|
1781
|
+
export function evidenceKindMatches(kind: string, words: string[]): boolean {
|
|
2013
1782
|
return words.some(word => kind.includes(word));
|
|
2014
1783
|
}
|
|
2015
1784
|
function formatActualArtifactKinds(artifactIds: string[], kinds: string[]): string {
|
|
@@ -2021,10 +1790,10 @@ function formatExpectedKindWords(words: string[]): string {
|
|
|
2021
1790
|
return words.map(word => `"${word}"`).join(", ");
|
|
2022
1791
|
}
|
|
2023
1792
|
|
|
2024
|
-
type SurfaceFamily = "web" | "cli" | "native" | "api-package" | "algorithm-math" | "unknown";
|
|
1793
|
+
export type SurfaceFamily = "web" | "cli" | "native" | "api-package" | "algorithm-math" | "unknown";
|
|
2025
1794
|
|
|
2026
|
-
type UltragoalChangeStatus = "added" | "modified" | "deleted" | "renamed" | "copied" | "unknown";
|
|
2027
|
-
type UltragoalChangeCategory =
|
|
1795
|
+
export type UltragoalChangeStatus = "added" | "modified" | "deleted" | "renamed" | "copied" | "unknown";
|
|
1796
|
+
export type UltragoalChangeCategory =
|
|
2028
1797
|
| "code"
|
|
2029
1798
|
| "generated-binding"
|
|
2030
1799
|
| "tool"
|
|
@@ -2032,13 +1801,13 @@ type UltragoalChangeCategory =
|
|
|
2032
1801
|
| "prompt-doc-behavior"
|
|
2033
1802
|
| "docs-static"
|
|
2034
1803
|
| "other";
|
|
2035
|
-
interface UltragoalChangeSetPath extends JsonObject {
|
|
1804
|
+
export interface UltragoalChangeSetPath extends JsonObject {
|
|
2036
1805
|
path: string;
|
|
2037
1806
|
status: UltragoalChangeStatus;
|
|
2038
1807
|
oldPath?: string;
|
|
2039
1808
|
category?: UltragoalChangeCategory;
|
|
2040
1809
|
}
|
|
2041
|
-
interface UltragoalChangeSet extends JsonObject {
|
|
1810
|
+
export interface UltragoalChangeSet extends JsonObject {
|
|
2042
1811
|
source: "checkpoint-git" | "review-pr" | "review-branch" | "review-worktree" | "review-spec";
|
|
2043
1812
|
baseRef?: string;
|
|
2044
1813
|
headRef?: string;
|
|
@@ -2060,7 +1829,7 @@ const MANDATORY_COMPUTER_CASE_IDS = [
|
|
|
2060
1829
|
] as const;
|
|
2061
1830
|
const TOOLS_INDEX_PATH = "packages/coding-agent/src/tools/index.ts";
|
|
2062
1831
|
|
|
2063
|
-
function normalizeRepoPath(value: string): string {
|
|
1832
|
+
export function normalizeRepoPath(value: string): string {
|
|
2064
1833
|
return value.replaceAll("\\\\", "/").replace(/^\.\//, "");
|
|
2065
1834
|
}
|
|
2066
1835
|
|
|
@@ -2068,7 +1837,7 @@ function isToolsIndexPath(value: string): boolean {
|
|
|
2068
1837
|
return normalizeRepoPath(value) === TOOLS_INDEX_PATH;
|
|
2069
1838
|
}
|
|
2070
1839
|
|
|
2071
|
-
function categorizeComputerChangePath(value: string): UltragoalChangeCategory {
|
|
1840
|
+
export function categorizeComputerChangePath(value: string): UltragoalChangeCategory {
|
|
2072
1841
|
const normalized = normalizeRepoPath(value);
|
|
2073
1842
|
if (normalized.startsWith("crates/pi-natives/src/computer/")) return "code";
|
|
2074
1843
|
if (/^packages\/natives\/native\/index\.(?:d\.ts|js)$/.test(normalized)) return "generated-binding";
|
|
@@ -2136,7 +1905,7 @@ function isComputerSpecificToolsIndexDiff(diff: string | undefined, targetPath:
|
|
|
2136
1905
|
/** Settings registry file that holds ALL settings, most of them unrelated to computer control. */
|
|
2137
1906
|
const SETTINGS_SCHEMA_PATH = "packages/coding-agent/src/config/settings-schema.ts";
|
|
2138
1907
|
|
|
2139
|
-
function isSettingsSchemaPath(value: string): boolean {
|
|
1908
|
+
export function isSettingsSchemaPath(value: string): boolean {
|
|
2140
1909
|
return normalizeRepoPath(value) === SETTINGS_SCHEMA_PATH;
|
|
2141
1910
|
}
|
|
2142
1911
|
|
|
@@ -2228,7 +1997,7 @@ export function surfaceFamily(value: string): SurfaceFamily {
|
|
|
2228
1997
|
return "unknown";
|
|
2229
1998
|
}
|
|
2230
1999
|
|
|
2231
|
-
function isLiveSurfaceFamily(family: SurfaceFamily): boolean {
|
|
2000
|
+
export function isLiveSurfaceFamily(family: SurfaceFamily): boolean {
|
|
2232
2001
|
return family === "web" || family === "cli" || family === "native";
|
|
2233
2002
|
}
|
|
2234
2003
|
|
|
@@ -2280,7 +2049,7 @@ function validateSurfaceArtifactCompatibility(
|
|
|
2280
2049
|
}
|
|
2281
2050
|
}
|
|
2282
2051
|
|
|
2283
|
-
function isSubstantiveEvidence(value: unknown): boolean {
|
|
2052
|
+
export function isSubstantiveEvidence(value: unknown): boolean {
|
|
2284
2053
|
if (typeof value !== "string") return false;
|
|
2285
2054
|
const trimmed = value.trim();
|
|
2286
2055
|
if (trimmed.length < MIN_SUBSTANTIVE_EVIDENCE_CHARS) return false;
|
|
@@ -2290,7 +2059,7 @@ function isSubstantiveEvidence(value: unknown): boolean {
|
|
|
2290
2059
|
return !["todo", "tbd", "n/a", "na", "none", "placeholder", "empty", "stub"].includes(normalized);
|
|
2291
2060
|
}
|
|
2292
2061
|
|
|
2293
|
-
function hasTypedVerifiedReceipt(value: unknown): boolean {
|
|
2062
|
+
export function hasTypedVerifiedReceipt(value: unknown): boolean {
|
|
2294
2063
|
const receipt = qualityGateObject(value);
|
|
2295
2064
|
if (!receipt) return false;
|
|
2296
2065
|
const type = nonEmptyString(receipt.type) ?? nonEmptyString(receipt.kind) ?? nonEmptyString(receipt.receiptType);
|
|
@@ -2299,7 +2068,7 @@ function hasTypedVerifiedReceipt(value: unknown): boolean {
|
|
|
2299
2068
|
return Boolean(type && id && (status === "verified" || status === "passed"));
|
|
2300
2069
|
}
|
|
2301
2070
|
|
|
2302
|
-
async function hasExistingNonEmptyArtifact(cwd: string, value: unknown): Promise<boolean> {
|
|
2071
|
+
export async function hasExistingNonEmptyArtifact(cwd: string, value: unknown): Promise<boolean> {
|
|
2303
2072
|
const artifactPath = nonEmptyString(value);
|
|
2304
2073
|
if (!artifactPath) return false;
|
|
2305
2074
|
const resolved = path.resolve(cwd, artifactPath);
|
|
@@ -2312,7 +2081,7 @@ async function hasExistingNonEmptyArtifact(cwd: string, value: unknown): Promise
|
|
|
2312
2081
|
}
|
|
2313
2082
|
}
|
|
2314
2083
|
|
|
2315
|
-
async function readArtifactBytes(cwd: string, row: JsonObject, fieldName: string): Promise<Buffer | null> {
|
|
2084
|
+
export async function readArtifactBytes(cwd: string, row: JsonObject, fieldName: string): Promise<Buffer | null> {
|
|
2316
2085
|
const artifactPath = nonEmptyString(row.path);
|
|
2317
2086
|
if (!artifactPath) return null;
|
|
2318
2087
|
const resolved = path.resolve(cwd, artifactPath);
|
|
@@ -2326,839 +2095,27 @@ async function readArtifactBytes(cwd: string, row: JsonObject, fieldName: string
|
|
|
2326
2095
|
}
|
|
2327
2096
|
}
|
|
2328
2097
|
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
if (!bytes.subarray(0, 8).equals(PNG_SIGNATURE)) return null;
|
|
2351
|
-
let offset = 8;
|
|
2352
|
-
let width = 0;
|
|
2353
|
-
let height = 0;
|
|
2354
|
-
let sawIhdr = false;
|
|
2355
|
-
let sawIdat = false;
|
|
2356
|
-
const idatChunks: Buffer[] = [];
|
|
2357
|
-
while (offset + 12 <= bytes.length) {
|
|
2358
|
-
const chunkStart = offset;
|
|
2359
|
-
const length = bytes.readUInt32BE(offset);
|
|
2360
|
-
offset += 4;
|
|
2361
|
-
const type = bytes.toString("ascii", offset, offset + 4);
|
|
2362
|
-
offset += 4;
|
|
2363
|
-
if (offset + length + 4 > bytes.length) return null;
|
|
2364
|
-
const data = bytes.subarray(offset, offset + length);
|
|
2365
|
-
offset += length;
|
|
2366
|
-
const expectedCrc = bytes.readUInt32BE(offset);
|
|
2367
|
-
offset += 4;
|
|
2368
|
-
if (pngCrc32(bytes.subarray(chunkStart + 4, offset - 4)) !== expectedCrc) return null;
|
|
2369
|
-
if (!sawIhdr) {
|
|
2370
|
-
if (type !== "IHDR" || length !== 13) return null;
|
|
2371
|
-
width = data.readUInt32BE(0);
|
|
2372
|
-
height = data.readUInt32BE(4);
|
|
2373
|
-
if (
|
|
2374
|
-
width === 0 ||
|
|
2375
|
-
height === 0 ||
|
|
2376
|
-
data[8] !== 8 ||
|
|
2377
|
-
![2, 6].includes(data[9]!) ||
|
|
2378
|
-
data[10] !== 0 ||
|
|
2379
|
-
data[11] !== 0 ||
|
|
2380
|
-
data[12] !== 0
|
|
2381
|
-
)
|
|
2382
|
-
return null;
|
|
2383
|
-
sawIhdr = true;
|
|
2384
|
-
} else if (type === "IHDR") return null;
|
|
2385
|
-
if (type === "IDAT") {
|
|
2386
|
-
if (!sawIhdr || length === 0) return null;
|
|
2387
|
-
sawIdat = true;
|
|
2388
|
-
idatChunks.push(data);
|
|
2389
|
-
}
|
|
2390
|
-
if (type === "IEND") {
|
|
2391
|
-
if (length !== 0 || !sawIhdr || !sawIdat || offset !== bytes.length) return null;
|
|
2392
|
-
try {
|
|
2393
|
-
return { width, height, headerBytes: 8, sampleBytes: inflateSync(Buffer.concat(idatChunks)) };
|
|
2394
|
-
} catch {
|
|
2395
|
-
return null;
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
}
|
|
2399
|
-
return null;
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
function parseJpegDimensions(
|
|
2403
|
-
bytes: Buffer,
|
|
2404
|
-
): { width: number; height: number; headerBytes: number; sampleBytes?: Buffer } | null {
|
|
2405
|
-
if (bytes.length < 8 || bytes[0] !== 0xff || bytes[1] !== JPEG_START_OF_IMAGE) return null;
|
|
2406
|
-
let offset = 2;
|
|
2407
|
-
let dimensions: { width: number; height: number; headerBytes: number } | null = null;
|
|
2408
|
-
let sawStartOfScan = false;
|
|
2409
|
-
let scanStart = -1;
|
|
2410
|
-
while (offset < bytes.length) {
|
|
2411
|
-
if (bytes[offset] !== 0xff) return null;
|
|
2412
|
-
while (offset < bytes.length && bytes[offset] === 0xff) offset++;
|
|
2413
|
-
if (offset >= bytes.length) return null;
|
|
2414
|
-
const marker = bytes[offset++];
|
|
2415
|
-
if (marker === 0x00) return null;
|
|
2416
|
-
if (marker === JPEG_END_OF_IMAGE) return null;
|
|
2417
|
-
if (JPEG_STANDALONE_MARKERS.has(marker)) continue;
|
|
2418
|
-
if (offset + 2 > bytes.length) return null;
|
|
2419
|
-
const segmentLength = bytes.readUInt16BE(offset);
|
|
2420
|
-
if (segmentLength < 2 || offset + segmentLength > bytes.length) return null;
|
|
2421
|
-
const segmentDataEnd = offset + segmentLength;
|
|
2422
|
-
if (marker === 0xc0 || marker === 0xc1 || marker === 0xc2) {
|
|
2423
|
-
if (segmentLength < 8) return null;
|
|
2424
|
-
dimensions = {
|
|
2425
|
-
width: bytes.readUInt16BE(offset + 5),
|
|
2426
|
-
height: bytes.readUInt16BE(offset + 3),
|
|
2427
|
-
headerBytes: offset + segmentLength,
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
if (marker === JPEG_START_OF_SCAN) {
|
|
2431
|
-
if (!dimensions || segmentDataEnd >= bytes.length) return null;
|
|
2432
|
-
sawStartOfScan = true;
|
|
2433
|
-
scanStart = segmentDataEnd;
|
|
2434
|
-
break;
|
|
2435
|
-
}
|
|
2436
|
-
offset += segmentLength;
|
|
2437
|
-
}
|
|
2438
|
-
if (!dimensions || !sawStartOfScan || scanStart < 0) return null;
|
|
2439
|
-
let scanOffset = scanStart;
|
|
2440
|
-
let entropyBytes = 0;
|
|
2441
|
-
while (scanOffset < bytes.length) {
|
|
2442
|
-
const byte = bytes[scanOffset++]!;
|
|
2443
|
-
if (byte !== 0xff) {
|
|
2444
|
-
entropyBytes++;
|
|
2445
|
-
continue;
|
|
2446
|
-
}
|
|
2447
|
-
if (scanOffset >= bytes.length) return null;
|
|
2448
|
-
const marker = bytes[scanOffset++]!;
|
|
2449
|
-
if (marker === 0x00) {
|
|
2450
|
-
entropyBytes++;
|
|
2451
|
-
continue;
|
|
2452
|
-
}
|
|
2453
|
-
if (JPEG_STANDALONE_MARKERS.has(marker)) continue;
|
|
2454
|
-
if (marker === JPEG_END_OF_IMAGE) {
|
|
2455
|
-
if (scanOffset !== bytes.length || entropyBytes < 32) return null;
|
|
2456
|
-
return { ...dimensions, sampleBytes: bytes.subarray(scanStart, scanOffset - 2) };
|
|
2457
|
-
}
|
|
2458
|
-
return null;
|
|
2459
|
-
}
|
|
2460
|
-
return null;
|
|
2461
|
-
}
|
|
2462
|
-
|
|
2463
|
-
function unsupportedScreenshotFormat(bytes: Buffer): string | null {
|
|
2464
|
-
if (bytes.toString("ascii", 0, 6) === "GIF87a" || bytes.toString("ascii", 0, 6) === "GIF89a") return "GIF";
|
|
2465
|
-
if (bytes.toString("ascii", 0, 2) === "BM") return "BMP";
|
|
2466
|
-
if (bytes.length >= 12 && bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP")
|
|
2467
|
-
return "WebP";
|
|
2468
|
-
return null;
|
|
2469
|
-
}
|
|
2470
|
-
|
|
2471
|
-
function parseImageDimensions(
|
|
2472
|
-
bytes: Buffer,
|
|
2473
|
-
): { width: number; height: number; headerBytes: number; sampleBytes?: Buffer } | null {
|
|
2474
|
-
return parsePngDimensions(bytes) ?? parseJpegDimensions(bytes);
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
function hasNonUniformImageBytes(bytes: Buffer, headerBytes: number, sampleBytes?: Buffer): boolean {
|
|
2478
|
-
const source = sampleBytes ?? bytes;
|
|
2479
|
-
const sampleStart = sampleBytes ? 0 : Math.min(Math.max(headerBytes, 0), source.length);
|
|
2480
|
-
const sampleLength = source.length - sampleStart;
|
|
2481
|
-
if (sampleLength < 32) return false;
|
|
2482
|
-
const windows: Buffer[] = [];
|
|
2483
|
-
for (let index = 0; index < 64; index++) {
|
|
2484
|
-
const offset = sampleStart + Math.floor(((sampleLength - 32) * index) / 63);
|
|
2485
|
-
windows.push(source.subarray(offset, offset + 32));
|
|
2486
|
-
}
|
|
2487
|
-
const byteCounts = new Map<number, number>();
|
|
2488
|
-
let total = 0;
|
|
2489
|
-
for (const window of windows) {
|
|
2490
|
-
for (const byte of window) {
|
|
2491
|
-
byteCounts.set(byte, (byteCounts.get(byte) ?? 0) + 1);
|
|
2492
|
-
total++;
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
const first = windows[0]!;
|
|
2496
|
-
const differingWindows = windows.slice(1).filter(window => !window.equals(first)).length;
|
|
2497
|
-
const maxCount = Math.max(...byteCounts.values());
|
|
2498
|
-
return byteCounts.size >= 16 && differingWindows >= 8 && maxCount / total <= 0.95;
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
async function validateScreenshotArtifact(cwd: string, row: JsonObject, fieldName: string): Promise<boolean> {
|
|
2502
|
-
const bytes = await readArtifactBytes(cwd, row, fieldName);
|
|
2503
|
-
if (!bytes) throw new Error(`qualityGate ${fieldName} screenshot artifact path must resolve to an existing file`);
|
|
2504
|
-
if (bytes.length < 4096) throw new Error(`qualityGate ${fieldName} screenshot artifact must be at least 4096 bytes`);
|
|
2505
|
-
const unsupportedFormat = unsupportedScreenshotFormat(bytes);
|
|
2506
|
-
if (unsupportedFormat) {
|
|
2507
|
-
throw new Error(
|
|
2508
|
-
`qualityGate ${fieldName} unsupported/undecodable screenshot format ${unsupportedFormat}; use PNG or fully marker-validated JPEG`,
|
|
2509
|
-
);
|
|
2510
|
-
}
|
|
2511
|
-
const dimensions = parseImageDimensions(bytes);
|
|
2512
|
-
if (!dimensions)
|
|
2513
|
-
throw new Error(`qualityGate ${fieldName} screenshot artifact must be a decodable PNG or JPEG image`);
|
|
2514
|
-
if (dimensions.width < 320 || dimensions.height < 180) {
|
|
2515
|
-
throw new Error(`qualityGate ${fieldName} screenshot artifact must be at least 320x180 pixels`);
|
|
2516
|
-
}
|
|
2517
|
-
if (!hasNonUniformImageBytes(bytes, dimensions.headerBytes, dimensions.sampleBytes)) {
|
|
2518
|
-
throw new Error(
|
|
2519
|
-
`qualityGate ${fieldName} screenshot artifact must be non-uniform, not blank, solid, tiny, or placeholder imagery`,
|
|
2520
|
-
);
|
|
2521
|
-
}
|
|
2522
|
-
return true;
|
|
2523
|
-
}
|
|
2524
|
-
|
|
2525
|
-
function normalizeTranscriptTimestamp(value: unknown): number | null {
|
|
2526
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2527
|
-
if (typeof value !== "string" || value.trim().length === 0) return null;
|
|
2528
|
-
const numeric = Number(value);
|
|
2529
|
-
if (Number.isFinite(numeric)) return numeric;
|
|
2530
|
-
const parsed = Date.parse(value);
|
|
2531
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
|
-
function transcriptSurfaceCompatible(value: unknown, family: SurfaceFamily): boolean {
|
|
2535
|
-
const surface = nonEmptyString(value);
|
|
2536
|
-
return !surface || family === "unknown" || surfaceFamily(surface) === family;
|
|
2537
|
-
}
|
|
2538
|
-
|
|
2539
|
-
function actionSelectorRequired(type: string): boolean {
|
|
2540
|
-
return ["click", "fill", "press", "assert", "screenshot", "observe"].includes(type);
|
|
2541
|
-
}
|
|
2542
|
-
|
|
2543
|
-
async function validateAutomationTranscriptArtifact(
|
|
2544
|
-
cwd: string,
|
|
2545
|
-
row: JsonObject,
|
|
2546
|
-
fieldName: string,
|
|
2547
|
-
options: { surfaceFamily: SurfaceFamily },
|
|
2548
|
-
): Promise<boolean> {
|
|
2549
|
-
const bytes = await readArtifactBytes(cwd, row, fieldName);
|
|
2550
|
-
if (!bytes) throw new Error(`qualityGate ${fieldName} automation transcript path must resolve to an existing file`);
|
|
2551
|
-
let transcript: JsonObject;
|
|
2552
|
-
try {
|
|
2553
|
-
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
2554
|
-
transcript = requireQualityGateObject(parsed, `${fieldName}.transcript`);
|
|
2555
|
-
} catch (error) {
|
|
2556
|
-
throw new Error(`qualityGate ${fieldName} automation transcript must be valid JSON: ${String(error)}`);
|
|
2557
|
-
}
|
|
2558
|
-
if (transcript.schemaVersion !== 1)
|
|
2559
|
-
throw new Error(`qualityGate ${fieldName} automation transcript schemaVersion must be 1`);
|
|
2560
|
-
if (!transcriptSurfaceCompatible(transcript.surface, options.surfaceFamily)) {
|
|
2561
|
-
throw new Error(
|
|
2562
|
-
`qualityGate ${fieldName} automation transcript surface is not compatible with ${options.surfaceFamily}`,
|
|
2563
|
-
);
|
|
2564
|
-
}
|
|
2565
|
-
if (!nonEmptyString(transcript.tool))
|
|
2566
|
-
throw new Error(`qualityGate ${fieldName} automation transcript tool must be non-empty`);
|
|
2567
|
-
const actions = requireObjectArray(transcript.actions, `${fieldName}.actions`);
|
|
2568
|
-
if (actions.length < 1) throw new Error(`qualityGate ${fieldName} automation transcript actions must be non-empty`);
|
|
2569
|
-
const assertionsValue = transcript.assertions;
|
|
2570
|
-
const assertions =
|
|
2571
|
-
assertionsValue === undefined ? [] : requireObjectArray(assertionsValue, `${fieldName}.assertions`);
|
|
2572
|
-
const timestamps: number[] = [];
|
|
2573
|
-
let hasSelectorBearingEntry = false;
|
|
2574
|
-
for (const [index, action] of actions.entries()) {
|
|
2575
|
-
const actionField = `${fieldName}.actions[${index}]`;
|
|
2576
|
-
const type = requiredStringField(action, "type", actionField).toLowerCase();
|
|
2577
|
-
const timestamp = normalizeTranscriptTimestamp(action.timestamp);
|
|
2578
|
-
if (timestamp === null) throw new Error(`qualityGate ${actionField}.timestamp must be present and parseable`);
|
|
2579
|
-
timestamps.push(timestamp);
|
|
2580
|
-
const selector = nonEmptyString(action.selector);
|
|
2581
|
-
if (actionSelectorRequired(type) && !selector)
|
|
2582
|
-
throw new Error(`qualityGate ${actionField}.selector must be non-empty`);
|
|
2583
|
-
if (type === "goto" && !nonEmptyString(action.url))
|
|
2584
|
-
throw new Error(`qualityGate ${actionField}.url must be non-empty`);
|
|
2585
|
-
if (type === "custom" && !selector && !nonEmptyString(action.target)) {
|
|
2586
|
-
throw new Error(`qualityGate ${actionField}.selector or target must be non-empty`);
|
|
2587
|
-
}
|
|
2588
|
-
if (selector) hasSelectorBearingEntry = true;
|
|
2589
|
-
}
|
|
2590
|
-
for (const [index, assertion] of assertions.entries()) {
|
|
2591
|
-
const assertionField = `${fieldName}.assertions[${index}]`;
|
|
2592
|
-
const timestamp = normalizeTranscriptTimestamp(assertion.timestamp);
|
|
2593
|
-
if (timestamp === null) throw new Error(`qualityGate ${assertionField}.timestamp must be present and parseable`);
|
|
2594
|
-
timestamps.push(timestamp);
|
|
2595
|
-
if (nonEmptyString(assertion.status)?.toLowerCase() !== PASSED_STATUS) {
|
|
2596
|
-
throw new Error(`qualityGate ${assertionField}.status must be passed`);
|
|
2597
|
-
}
|
|
2598
|
-
if (nonEmptyString(assertion.selector)) hasSelectorBearingEntry = true;
|
|
2599
|
-
}
|
|
2600
|
-
for (let index = 1; index < timestamps.length; index++) {
|
|
2601
|
-
if (timestamps[index]! < timestamps[index - 1]!) {
|
|
2602
|
-
throw new Error(`qualityGate ${fieldName} automation transcript timestamps must be monotonic non-decreasing`);
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
if (!hasSelectorBearingEntry) {
|
|
2606
|
-
throw new Error(
|
|
2607
|
-
`qualityGate ${fieldName} automation transcript must include at least one selector-bearing action or assertion`,
|
|
2608
|
-
);
|
|
2609
|
-
}
|
|
2610
|
-
return true;
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
async function validatePtyCaptureArtifact(cwd: string, row: JsonObject, fieldName: string): Promise<boolean> {
|
|
2614
|
-
const bytes = await readArtifactBytes(cwd, row, fieldName);
|
|
2615
|
-
if (!bytes) throw new Error(`qualityGate ${fieldName} PTY capture path must resolve to an existing file`);
|
|
2616
|
-
if (bytes.length < 512) throw new Error(`qualityGate ${fieldName} PTY capture must be at least 512 bytes`);
|
|
2617
|
-
const text = bytes.toString("utf8");
|
|
2618
|
-
const hasCsi = /\x1b\[[0-?]*[ -/]*[@-~]/.test(text);
|
|
2619
|
-
const hasOsc = /\x1b\][^\x07]*(?:\x07|\x1b\\)/.test(text);
|
|
2620
|
-
const hasAltOrCursor = /\x1b\[\?1049[hl]|\x1b\[H|\x1b\[2J/.test(text);
|
|
2621
|
-
const hasRedraw = /[\r\b]/.test(text) && hasCsi;
|
|
2622
|
-
if (!hasCsi && !hasOsc && !hasAltOrCursor && !hasRedraw) {
|
|
2623
|
-
throw new Error(`qualityGate ${fieldName} PTY capture must contain terminal control sequences`);
|
|
2624
|
-
}
|
|
2625
|
-
if (!/[\x20-\x7e]{10,}/.test(text)) {
|
|
2626
|
-
throw new Error(
|
|
2627
|
-
`qualityGate ${fieldName} PTY capture must contain a printable text run of at least 10 characters`,
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
return true;
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
|
-
function structuralArtifactKind(row: JsonObject): "screenshot" | "automation" | "pty" | null {
|
|
2634
|
-
const kind = normalizedEvidenceKind(row);
|
|
2635
|
-
if (evidenceKindMatches(kind, ["screenshot", "image", "visual"])) return "screenshot";
|
|
2636
|
-
if (evidenceKindMatches(kind, ["browser", "playwright", "pandawright", "automation", "app-automation"]))
|
|
2637
|
-
return "automation";
|
|
2638
|
-
if (evidenceKindMatches(kind, ["pty", "tui", "terminal-capture"])) return "pty";
|
|
2639
|
-
return null;
|
|
2640
|
-
}
|
|
2641
|
-
|
|
2642
|
-
async function validateStructuralArtifact(
|
|
2643
|
-
cwd: string,
|
|
2644
|
-
row: JsonObject,
|
|
2645
|
-
fieldName: string,
|
|
2646
|
-
options: { surfaceFamily: SurfaceFamily; live: boolean },
|
|
2647
|
-
): Promise<boolean> {
|
|
2648
|
-
void options.live;
|
|
2649
|
-
const kind = structuralArtifactKind(row);
|
|
2650
|
-
if (!kind) return false;
|
|
2651
|
-
if (kind === "screenshot") return validateScreenshotArtifact(cwd, row, fieldName);
|
|
2652
|
-
if (kind === "automation") return validateAutomationTranscriptArtifact(cwd, row, fieldName, options);
|
|
2653
|
-
if (kind === "pty") return validatePtyCaptureArtifact(cwd, row, fieldName);
|
|
2654
|
-
return false;
|
|
2655
|
-
}
|
|
2656
|
-
|
|
2657
|
-
const CLI_REPLAY_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
2658
|
-
const CLI_REPLAY_DEFAULT_TIMEOUT_MS = 10_000;
|
|
2659
|
-
const CLI_REPLAY_MIN_TIMEOUT_MS = 1_000;
|
|
2660
|
-
const CLI_REPLAY_MAX_TIMEOUT_MS = 30_000;
|
|
2661
|
-
const CLI_REPLAY_EXEMPT_REASON_CODES = [
|
|
2662
|
-
"unsafe_side_effect",
|
|
2663
|
-
"requires_credentials",
|
|
2664
|
-
"requires_network",
|
|
2665
|
-
"non_deterministic_external",
|
|
2666
|
-
"destructive",
|
|
2667
|
-
"interactive_only",
|
|
2668
|
-
"platform_unavailable",
|
|
2669
|
-
] as const;
|
|
2670
|
-
const CLI_REPLAY_EXEMPT_REASON_CODE_SET = new Set<string>(CLI_REPLAY_EXEMPT_REASON_CODES);
|
|
2671
|
-
const CLI_REPLAY_ENV_BASE: Record<string, string> = { CI: "1", NO_COLOR: "1", SKC_ULTRAGOAL_REPLAY: "1" };
|
|
2672
|
-
const CLI_REPLAY_EXEMPT_REASON_CODE_LIST = CLI_REPLAY_EXEMPT_REASON_CODES.join(", ");
|
|
2673
|
-
const CLI_REPLAY_SAFE_ENV_NAMES = new Set(["LANG", "LC_ALL", "LC_CTYPE", "TZ"]);
|
|
2674
|
-
const CLI_REPLAY_DANGEROUS_ENV_NAME_PATTERN =
|
|
2675
|
-
/^(?:NODE_OPTIONS|GIT_EXTERNAL_DIFF|GIT_SSH|GIT_SSH_COMMAND|GIT_PAGER|PATH|LD_PRELOAD|LD_LIBRARY_PATH)$|^(?:GIT_CONFIG|DYLD_|BUN_|NPM_CONFIG_)|(?:^|_)OPTIONS$|PRELOAD$/;
|
|
2676
|
-
const ANSI_ESCAPE_PATTERN = /\x1b(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1b\\)|[@-Z\\-_])/g;
|
|
2677
|
-
|
|
2678
|
-
function clampCliReplayTimeout(value: unknown): number {
|
|
2679
|
-
if (value === undefined) return CLI_REPLAY_DEFAULT_TIMEOUT_MS;
|
|
2680
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2681
|
-
throw new Error("qualityGate CLI replay timeoutMs must be a finite number");
|
|
2682
|
-
}
|
|
2683
|
-
return Math.min(CLI_REPLAY_MAX_TIMEOUT_MS, Math.max(CLI_REPLAY_MIN_TIMEOUT_MS, Math.trunc(value)));
|
|
2684
|
-
}
|
|
2685
|
-
|
|
2686
|
-
function basenameCommand(value: string): string {
|
|
2687
|
-
return path.basename(value).toLowerCase();
|
|
2688
|
-
}
|
|
2689
|
-
|
|
2690
|
-
function isDeterministicConsoleLogReplay(code: string): boolean {
|
|
2691
|
-
let remaining = code.trim();
|
|
2692
|
-
if (remaining.length === 0) return false;
|
|
2693
|
-
let matched = false;
|
|
2694
|
-
while (remaining.length > 0) {
|
|
2695
|
-
const match =
|
|
2696
|
-
/^console\.log\(\s*("(?:\\[\s\S]|[^"\\])*"|'(?:\\[\s\S]|[^'\\])*'|`(?:\\[\s\S]|[^`\\$])*`)\s*\)\s*;?\s*/.exec(
|
|
2697
|
-
remaining,
|
|
2698
|
-
);
|
|
2699
|
-
if (!match) return false;
|
|
2700
|
-
const statement = match[0]!;
|
|
2701
|
-
const literal = match[1]!;
|
|
2702
|
-
if (literal.startsWith("`") && literal.includes("${")) return false;
|
|
2703
|
-
matched = true;
|
|
2704
|
-
remaining = remaining.slice(statement.length);
|
|
2705
|
-
}
|
|
2706
|
-
return matched;
|
|
2707
|
-
}
|
|
2708
|
-
|
|
2709
|
-
function hasShellRedirectionToken(value: string): boolean {
|
|
2710
|
-
return /^(?:[<>]|\d?[<>]|\d?>&\d|\|\|?|&&|;)$/.test(value) || /(?:^|[^\w])-?>/.test(value);
|
|
2711
|
-
}
|
|
2712
|
-
|
|
2713
|
-
function isSafeRefOrPathspec(value: string): boolean {
|
|
2714
|
-
return value.length > 0 && !value.startsWith("-") && !/[\0\n\r]/.test(value) && !hasShellRedirectionToken(value);
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
function isAllowedGitReplayCommand(args: readonly string[]): boolean {
|
|
2718
|
-
const subcommand = args[0];
|
|
2719
|
-
const rest = args.slice(1);
|
|
2720
|
-
if (subcommand === "status") return rest.every(arg => ["--short", "--porcelain", "--branch"].includes(arg));
|
|
2721
|
-
if (subcommand === "rev-parse" || subcommand === "merge-base")
|
|
2722
|
-
return rest.length > 0 && rest.every(isSafeRefOrPathspec);
|
|
2723
|
-
if (subcommand !== "diff" && subcommand !== "show" && subcommand !== "log") return false;
|
|
2724
|
-
let pathspecMode = false;
|
|
2725
|
-
for (const arg of rest) {
|
|
2726
|
-
if (arg === "--") {
|
|
2727
|
-
pathspecMode = true;
|
|
2728
|
-
continue;
|
|
2729
|
-
}
|
|
2730
|
-
if (pathspecMode) {
|
|
2731
|
-
if (!isSafeRefOrPathspec(arg)) return false;
|
|
2732
|
-
continue;
|
|
2733
|
-
}
|
|
2734
|
-
if (["--stat", "--name-only", "--oneline", "--no-ext-diff"].includes(arg)) continue;
|
|
2735
|
-
if (!isSafeRefOrPathspec(arg)) return false;
|
|
2736
|
-
}
|
|
2737
|
-
return true;
|
|
2738
|
-
}
|
|
2739
|
-
|
|
2740
|
-
function isBareExecutableName(value: string): boolean {
|
|
2741
|
-
// The allowlist is keyed on the basename, but the raw command[0] is what gets spawned.
|
|
2742
|
-
// Reject path-qualified or case-spoofed executables (e.g. ./git, /tmp/npm, scripts/node, GIT)
|
|
2743
|
-
// so an attacker-controlled binary cannot impersonate a trusted tool.
|
|
2744
|
-
return (
|
|
2745
|
-
value.length > 0 &&
|
|
2746
|
-
!value.includes("/") &&
|
|
2747
|
-
!value.includes("\\") &&
|
|
2748
|
-
value === path.basename(value) &&
|
|
2749
|
-
value === value.toLowerCase()
|
|
2750
|
-
);
|
|
2751
|
-
}
|
|
2752
|
-
|
|
2753
|
-
function isAllowedCliReplayCommand(command: readonly string[]): boolean {
|
|
2754
|
-
if (
|
|
2755
|
-
command.length === 0 ||
|
|
2756
|
-
command.some(arg => arg.trim() !== arg || arg.length === 0 || hasShellRedirectionToken(arg))
|
|
2757
|
-
)
|
|
2758
|
-
return false;
|
|
2759
|
-
if (!isBareExecutableName(command[0]!)) return false;
|
|
2760
|
-
const executable = basenameCommand(command[0]!);
|
|
2761
|
-
const args = command.slice(1);
|
|
2762
|
-
if (executable === "bun" || executable === "node") {
|
|
2763
|
-
if (args.length === 1 && args[0] === "--version") return true;
|
|
2764
|
-
return args.length === 2 && args[0] === "-e" && isDeterministicConsoleLogReplay(args[1]!);
|
|
2765
|
-
}
|
|
2766
|
-
if (executable === "npm" || executable === "pnpm" || executable === "yarn") {
|
|
2767
|
-
return (args.length === 1 && args[0] === "--version") || (args.length === 1 && args[0] === "list");
|
|
2768
|
-
}
|
|
2769
|
-
if (executable === "git") return isAllowedGitReplayCommand(args);
|
|
2770
|
-
if (executable === "skc") return args.length === 1 && ["read", "status"].includes(args[0] ?? "");
|
|
2771
|
-
return false;
|
|
2772
|
-
}
|
|
2773
|
-
function summarizeBlockedCliReplayCommand(command: readonly string[]): string {
|
|
2774
|
-
const executable = command[0] ? basenameCommand(command[0]) : "<missing>";
|
|
2775
|
-
const argCount = Math.max(0, command.length - 1);
|
|
2776
|
-
return `${JSON.stringify(executable)} with ${argCount} arg${argCount === 1 ? "" : "s"}`;
|
|
2777
|
-
}
|
|
2778
|
-
|
|
2779
|
-
function cliReplayAllowlistDescription(): string {
|
|
2780
|
-
return [
|
|
2781
|
-
'`bun --version`, `node --version`, or deterministic `bun/node -e "console.log(...)"`',
|
|
2782
|
-
"`npm|pnpm|yarn --version` or `npm|pnpm|yarn list`",
|
|
2783
|
-
"read-only `git status|rev-parse|merge-base|diff|show|log` with safe args",
|
|
2784
|
-
"`skc read` or `skc status`",
|
|
2785
|
-
].join("; ");
|
|
2786
|
-
}
|
|
2787
|
-
|
|
2788
|
-
function resolveCliReplayCommand(command: string[]): string[] {
|
|
2789
|
-
if (basenameCommand(command[0]!) === "bun") return [process.execPath, ...command.slice(1)];
|
|
2790
|
-
return command;
|
|
2791
|
-
}
|
|
2792
|
-
|
|
2793
|
-
function resolveUnderCwd(cwd: string, replayCwd: unknown, fieldName: string): string {
|
|
2794
|
-
const relative = replayCwd === undefined ? "." : nonEmptyString(replayCwd);
|
|
2795
|
-
if (!relative) throw new Error(`qualityGate ${fieldName}.cwd must be a non-empty string when provided`);
|
|
2796
|
-
const root = path.resolve(cwd);
|
|
2797
|
-
const resolved = path.resolve(root, relative);
|
|
2798
|
-
const relativeToRoot = path.relative(root, resolved);
|
|
2799
|
-
if (relativeToRoot === ".." || relativeToRoot.startsWith(`..${path.sep}`) || path.isAbsolute(relativeToRoot)) {
|
|
2800
|
-
throw new Error(`qualityGate ${fieldName}.cwd must resolve under the repository cwd`);
|
|
2801
|
-
}
|
|
2802
|
-
return resolved;
|
|
2803
|
-
}
|
|
2804
|
-
|
|
2805
|
-
function buildCliReplayEnv(value: unknown, fieldName: string): Record<string, string> {
|
|
2806
|
-
const env: Record<string, string> = { ...CLI_REPLAY_ENV_BASE };
|
|
2807
|
-
if (value === undefined) return env;
|
|
2808
|
-
const object = requireQualityGateObject(value, `${fieldName}.env`);
|
|
2809
|
-
for (const [key, envValue] of Object.entries(object)) {
|
|
2810
|
-
if (!/^[A-Z_][A-Z0-9_]*$/.test(key))
|
|
2811
|
-
throw new Error(`qualityGate ${fieldName}.env.${key} must be an uppercase environment key`);
|
|
2812
|
-
if (CLI_REPLAY_DANGEROUS_ENV_NAME_PATTERN.test(key) || !CLI_REPLAY_SAFE_ENV_NAMES.has(key)) {
|
|
2813
|
-
throw new Error(`qualityGate ${fieldName}.env.${key} is not in the CLI replay safe environment allowlist`);
|
|
2814
|
-
}
|
|
2815
|
-
if (typeof envValue !== "string") throw new Error(`qualityGate ${fieldName}.env.${key} must be a string`);
|
|
2816
|
-
env[key] = envValue;
|
|
2817
|
-
}
|
|
2818
|
-
return env;
|
|
2819
|
-
}
|
|
2820
|
-
|
|
2821
|
-
function normalizeCliReplayOutput(value: string, cwd: string): string {
|
|
2822
|
-
let normalized = value.replace(ANSI_ESCAPE_PATTERN, "").replace(/\r\n?/g, "\n");
|
|
2823
|
-
const home = process.env.HOME;
|
|
2824
|
-
const replacements: Array<[RegExp, string]> = [
|
|
2825
|
-
[/\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\b/g, "<TIMESTAMP>"],
|
|
2826
|
-
[/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, "<UUID>"],
|
|
2827
|
-
[/\b[0-9a-f]{7,}\b/gi, "<HASH>"],
|
|
2828
|
-
[/(?:\/private)?\/var\/folders\/[^\s"']+|\/tmp\/[^\s"']+|\/var\/tmp\/[^\s"']+/g, "<TMP>"],
|
|
2829
|
-
];
|
|
2830
|
-
for (const candidate of [path.resolve(cwd), home]) {
|
|
2831
|
-
if (!candidate) continue;
|
|
2832
|
-
const escaped = candidate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2833
|
-
normalized = normalized.replace(new RegExp(escaped, "g"), candidate === home ? "<HOME>" : "<CWD>");
|
|
2834
|
-
}
|
|
2835
|
-
for (const [pattern, replacement] of replacements) normalized = normalized.replace(pattern, replacement);
|
|
2836
|
-
const lines = normalized.split("\n").map(line => line.replace(/[ \t]+$/g, ""));
|
|
2837
|
-
while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
2838
|
-
return lines.join("\n");
|
|
2839
|
-
}
|
|
2840
|
-
|
|
2841
|
-
async function readCliReplayRecord(cwd: string, row: JsonObject, fieldName: string): Promise<JsonObject | null> {
|
|
2842
|
-
const inline = qualityGateObject(row.replay) ?? (row.kind === "cli-replay" ? row : null);
|
|
2843
|
-
if (inline) return inline;
|
|
2844
|
-
if (!evidenceKindMatches(normalizedEvidenceKind(row), ["cli-replay", "command-replay"])) return null;
|
|
2845
|
-
const bytes = await readArtifactBytes(cwd, row, fieldName);
|
|
2846
|
-
if (!bytes) return null;
|
|
2847
|
-
try {
|
|
2848
|
-
return requireQualityGateObject(JSON.parse(bytes.toString("utf8")), `${fieldName}.replay`);
|
|
2849
|
-
} catch (error) {
|
|
2850
|
-
throw new Error(`qualityGate ${fieldName} CLI replay artifact must be valid JSON: ${String(error)}`);
|
|
2851
|
-
}
|
|
2852
|
-
}
|
|
2853
|
-
|
|
2854
|
-
function parseCliReplayRecord(
|
|
2855
|
-
record: JsonObject,
|
|
2856
|
-
fieldName: string,
|
|
2857
|
-
): {
|
|
2858
|
-
command: string[];
|
|
2859
|
-
replayCwd: unknown;
|
|
2860
|
-
env: Record<string, string>;
|
|
2861
|
-
timeoutMs: number;
|
|
2862
|
-
expectedExitCode: number;
|
|
2863
|
-
recordedStdout: string;
|
|
2864
|
-
invariants: JsonObject[];
|
|
2865
|
-
} {
|
|
2866
|
-
if (record.schemaVersion !== 1) throw new Error(`qualityGate ${fieldName}.schemaVersion must be 1`);
|
|
2867
|
-
if (record.kind !== "cli-replay") throw new Error(`qualityGate ${fieldName}.kind must be cli-replay`);
|
|
2868
|
-
if (record.command !== undefined && typeof record.command === "string") {
|
|
2869
|
-
throw new Error(`qualityGate ${fieldName}.command must be an argv string array, not a shell string`);
|
|
2870
|
-
}
|
|
2871
|
-
const command = nonEmptyStringArray(record.command);
|
|
2872
|
-
if (!command) throw new Error(`qualityGate ${fieldName}.command must be a non-empty string array`);
|
|
2873
|
-
if (record.replaySafe !== true)
|
|
2874
|
-
throw new Error(`qualityGate ${fieldName}.replaySafe must be true before CLI replay executes`);
|
|
2875
|
-
if (!isAllowedCliReplayCommand(command)) {
|
|
2876
|
-
throw new Error(
|
|
2877
|
-
`qualityGate ${fieldName}.command is not in the conservative CLI replay allowlist; command ${summarizeBlockedCliReplayCommand(command)} is blocked. Allowed replay commands: ${cliReplayAllowlistDescription()}. For other commands, provide audited replayExempt metadata with reasonCode, reason, approvedBy, and fallbackArtifactRefs that point to a structurally valid fallback artifact.`,
|
|
2878
|
-
);
|
|
2879
|
-
}
|
|
2880
|
-
if (record.normalization !== undefined && record.normalization !== "default") {
|
|
2881
|
-
throw new Error(`qualityGate ${fieldName}.normalization must be default when provided`);
|
|
2882
|
-
}
|
|
2883
|
-
if (typeof record.recordedStdout !== "string")
|
|
2884
|
-
throw new Error(`qualityGate ${fieldName}.recordedStdout must be a string`);
|
|
2885
|
-
if (record.recordedStderr !== undefined && typeof record.recordedStderr !== "string") {
|
|
2886
|
-
throw new Error(`qualityGate ${fieldName}.recordedStderr must be a string when provided`);
|
|
2887
|
-
}
|
|
2888
|
-
const expectedExitCode = record.expectedExitCode === undefined ? 0 : record.expectedExitCode;
|
|
2889
|
-
if (typeof expectedExitCode !== "number" || !Number.isInteger(expectedExitCode)) {
|
|
2890
|
-
throw new Error(`qualityGate ${fieldName}.expectedExitCode must be an integer`);
|
|
2891
|
-
}
|
|
2892
|
-
const invariants =
|
|
2893
|
-
record.invariants === undefined ? [] : requireObjectArray(record.invariants, `${fieldName}.invariants`);
|
|
2894
|
-
return {
|
|
2895
|
-
command: command.map(item => item.trim()),
|
|
2896
|
-
replayCwd: record.cwd,
|
|
2897
|
-
env: buildCliReplayEnv(record.env, fieldName),
|
|
2898
|
-
timeoutMs: clampCliReplayTimeout(record.timeoutMs),
|
|
2899
|
-
expectedExitCode,
|
|
2900
|
-
recordedStdout: record.recordedStdout,
|
|
2901
|
-
invariants,
|
|
2902
|
-
};
|
|
2903
|
-
}
|
|
2904
|
-
|
|
2905
|
-
function validateCliReplayInvariants(invariants: JsonObject[], stdout: string, fieldName: string): void {
|
|
2906
|
-
for (const [index, invariant] of invariants.entries()) {
|
|
2907
|
-
const invariantField = `${fieldName}.invariants[${index}]`;
|
|
2908
|
-
const type = requiredStringField(invariant, "type", invariantField);
|
|
2909
|
-
const value = requiredStringField(invariant, "value", invariantField);
|
|
2910
|
-
if (type === "substring" && !stdout.includes(value))
|
|
2911
|
-
throw new Error(`qualityGate ${invariantField} substring invariant did not match stdout`);
|
|
2912
|
-
else if (type === "not_substring" && stdout.includes(value))
|
|
2913
|
-
throw new Error(`qualityGate ${invariantField} not_substring invariant matched stdout`);
|
|
2914
|
-
else if (type === "regex") {
|
|
2915
|
-
const flags = invariant.flags === undefined ? "" : requiredStringField(invariant, "flags", invariantField);
|
|
2916
|
-
if (!/^[im]*$/.test(flags)) throw new Error(`qualityGate ${invariantField}.flags may only contain i and m`);
|
|
2917
|
-
if (!new RegExp(value, flags).test(stdout))
|
|
2918
|
-
throw new Error(`qualityGate ${invariantField} regex invariant did not match stdout`);
|
|
2919
|
-
} else if (type !== "substring" && type !== "not_substring") {
|
|
2920
|
-
throw new Error(`qualityGate ${invariantField}.type must be substring, regex, or not_substring`);
|
|
2921
|
-
}
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
async function collectCliReplayOutput(
|
|
2926
|
-
stream: ReadableStream<Uint8Array> | null,
|
|
2927
|
-
): Promise<{ text: string; truncated: boolean }> {
|
|
2928
|
-
if (!stream) return { text: "", truncated: false };
|
|
2929
|
-
const reader = stream.getReader();
|
|
2930
|
-
const chunks: Buffer[] = [];
|
|
2931
|
-
let size = 0;
|
|
2932
|
-
let truncated = false;
|
|
2933
|
-
try {
|
|
2934
|
-
while (true) {
|
|
2935
|
-
const { done, value } = await reader.read();
|
|
2936
|
-
if (done) break;
|
|
2937
|
-
if (size < CLI_REPLAY_MAX_OUTPUT_BYTES) {
|
|
2938
|
-
const remaining = CLI_REPLAY_MAX_OUTPUT_BYTES - size;
|
|
2939
|
-
const chunk = Buffer.from(value.subarray(0, remaining));
|
|
2940
|
-
chunks.push(chunk);
|
|
2941
|
-
size += chunk.length;
|
|
2942
|
-
}
|
|
2943
|
-
if (value.length > 0 && size >= CLI_REPLAY_MAX_OUTPUT_BYTES) {
|
|
2944
|
-
truncated = true;
|
|
2945
|
-
await reader.cancel().catch(() => undefined);
|
|
2946
|
-
break;
|
|
2947
|
-
}
|
|
2948
|
-
}
|
|
2949
|
-
} finally {
|
|
2950
|
-
reader.releaseLock();
|
|
2951
|
-
}
|
|
2952
|
-
return { text: Buffer.concat(chunks).toString("utf8"), truncated };
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
export interface ReplayProcessHandle {
|
|
2956
|
-
readonly exited: Promise<number>;
|
|
2957
|
-
kill(signal?: number | NodeJS.Signals): void;
|
|
2958
|
-
}
|
|
2959
|
-
|
|
2960
|
-
export async function waitForReplayProcessWithTimeout(
|
|
2961
|
-
process: ReplayProcessHandle,
|
|
2962
|
-
timeoutMs: number,
|
|
2963
|
-
graceMs = 2000,
|
|
2964
|
-
): Promise<number> {
|
|
2965
|
-
let timeoutTimer: NodeJS.Timeout | undefined;
|
|
2966
|
-
let graceTimer: NodeJS.Timeout | undefined;
|
|
2967
|
-
const timedOut = Symbol("timedOut");
|
|
2968
|
-
const timeout = new Promise<typeof timedOut>(resolve => {
|
|
2969
|
-
timeoutTimer = setTimeout(() => resolve(timedOut), timeoutMs);
|
|
2970
|
-
});
|
|
2971
|
-
const first = await Promise.race([process.exited, timeout]);
|
|
2972
|
-
if (first !== timedOut) {
|
|
2973
|
-
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
2974
|
-
return first;
|
|
2975
|
-
}
|
|
2976
|
-
process.kill("SIGTERM");
|
|
2977
|
-
const killed = Symbol("killed");
|
|
2978
|
-
const grace = new Promise<typeof killed>(resolve => {
|
|
2979
|
-
graceTimer = setTimeout(() => {
|
|
2980
|
-
process.kill("SIGKILL");
|
|
2981
|
-
resolve(killed);
|
|
2982
|
-
}, graceMs);
|
|
2983
|
-
});
|
|
2984
|
-
await Promise.race([process.exited, grace]);
|
|
2985
|
-
await process.exited.catch(() => undefined);
|
|
2986
|
-
if (timeoutTimer) clearTimeout(timeoutTimer);
|
|
2987
|
-
if (graceTimer) clearTimeout(graceTimer);
|
|
2988
|
-
throw new Error("timeout");
|
|
2989
|
-
}
|
|
2990
|
-
|
|
2991
|
-
async function validateReplayExemptFallback(
|
|
2992
|
-
cwd: string,
|
|
2993
|
-
record: JsonObject,
|
|
2994
|
-
fieldName: string,
|
|
2995
|
-
artifactRefs: Map<string, JsonObject>,
|
|
2996
|
-
options: { surfaceFamily: SurfaceFamily; live: boolean },
|
|
2997
|
-
): Promise<boolean> {
|
|
2998
|
-
const exempt = qualityGateObject(record.replayExempt);
|
|
2999
|
-
if (!exempt) return false;
|
|
3000
|
-
const reasonCode = requiredStringField(exempt, "reasonCode", `${fieldName}.replayExempt`);
|
|
3001
|
-
if (!CLI_REPLAY_EXEMPT_REASON_CODE_SET.has(reasonCode))
|
|
3002
|
-
throw new Error(
|
|
3003
|
-
`qualityGate ${fieldName}.replayExempt.reasonCode must be one of: ${CLI_REPLAY_EXEMPT_REASON_CODE_LIST}`,
|
|
3004
|
-
);
|
|
3005
|
-
const reason = requiredStringField(exempt, "reason", `${fieldName}.replayExempt`);
|
|
3006
|
-
if (!isSubstantiveEvidence(reason) || reason.length < 30)
|
|
3007
|
-
throw new Error(`qualityGate ${fieldName}.replayExempt.reason must be audited and substantive`);
|
|
3008
|
-
requiredStringField(exempt, "approvedBy", `${fieldName}.replayExempt`);
|
|
3009
|
-
const fallbackRefs = requireStringLinks(
|
|
3010
|
-
exempt.fallbackArtifactRefs,
|
|
3011
|
-
`${fieldName}.replayExempt.fallbackArtifactRefs`,
|
|
3012
|
-
);
|
|
3013
|
-
requireResolvedLinks(fallbackRefs, artifactRefs, `${fieldName}.replayExempt.fallbackArtifactRefs`);
|
|
3014
|
-
let validFallback = false;
|
|
3015
|
-
for (const fallbackRef of fallbackRefs) {
|
|
3016
|
-
if (fallbackRef === requiredStringField(record, "id", fieldName)) {
|
|
3017
|
-
throw new Error(`qualityGate ${fieldName}.replayExempt fallback must not reference the replay record itself`);
|
|
3018
|
-
}
|
|
3019
|
-
const fallback = artifactRefs.get(fallbackRef)!;
|
|
3020
|
-
if (await validateStructuralArtifact(cwd, fallback, `executorQa.artifactRefs.${fallbackRef}`, options))
|
|
3021
|
-
validFallback = true;
|
|
3022
|
-
}
|
|
3023
|
-
if (!validFallback)
|
|
3024
|
-
throw new Error(
|
|
3025
|
-
`qualityGate ${fieldName}.replayExempt requires at least one structurally-valid fallback artifact`,
|
|
3026
|
-
);
|
|
3027
|
-
return true;
|
|
3028
|
-
}
|
|
3029
|
-
async function validateCliReplay(
|
|
3030
|
-
cwd: string,
|
|
3031
|
-
row: JsonObject,
|
|
3032
|
-
fieldName: string,
|
|
3033
|
-
options: { live: boolean },
|
|
3034
|
-
): Promise<boolean> {
|
|
3035
|
-
const record = await readCliReplayRecord(cwd, row, fieldName);
|
|
3036
|
-
if (!record) return false;
|
|
3037
|
-
if (record.replayExempt !== undefined) {
|
|
3038
|
-
throw new Error(
|
|
3039
|
-
`qualityGate ${fieldName}.replayExempt can only be validated from surfaceEvidence with fallback context`,
|
|
3040
|
-
);
|
|
3041
|
-
}
|
|
3042
|
-
void options.live;
|
|
3043
|
-
const replay = parseCliReplayRecord(record, fieldName);
|
|
3044
|
-
const replayCwd = resolveUnderCwd(cwd, replay.replayCwd, fieldName);
|
|
3045
|
-
const process = Bun.spawn(resolveCliReplayCommand(replay.command), {
|
|
3046
|
-
cwd: replayCwd,
|
|
3047
|
-
env: replay.env,
|
|
3048
|
-
stdout: "pipe",
|
|
3049
|
-
stderr: "pipe",
|
|
3050
|
-
});
|
|
3051
|
-
try {
|
|
3052
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
3053
|
-
collectCliReplayOutput(process.stdout),
|
|
3054
|
-
collectCliReplayOutput(process.stderr),
|
|
3055
|
-
waitForReplayProcessWithTimeout(process, replay.timeoutMs),
|
|
3056
|
-
]);
|
|
3057
|
-
if (stdout.truncated || stderr.truncated)
|
|
3058
|
-
throw new Error(`qualityGate ${fieldName} CLI replay output exceeded 1 MiB buffer cap`);
|
|
3059
|
-
if (exitCode !== replay.expectedExitCode) {
|
|
3060
|
-
throw new Error(
|
|
3061
|
-
`qualityGate ${fieldName} CLI replay exit code ${exitCode} did not match expected ${replay.expectedExitCode}`,
|
|
3062
|
-
);
|
|
3063
|
-
}
|
|
3064
|
-
const actualStdout = normalizeCliReplayOutput(stdout.text, cwd);
|
|
3065
|
-
const recordedStdout = normalizeCliReplayOutput(replay.recordedStdout, cwd);
|
|
3066
|
-
if (replay.invariants.length > 0) {
|
|
3067
|
-
validateCliReplayInvariants(replay.invariants, actualStdout, fieldName);
|
|
3068
|
-
} else if (actualStdout !== recordedStdout) {
|
|
3069
|
-
throw new Error(`qualityGate ${fieldName} CLI replay stdout did not match recordedStdout after normalization`);
|
|
3070
|
-
}
|
|
3071
|
-
return true;
|
|
3072
|
-
} catch (error) {
|
|
3073
|
-
if (error instanceof Error && error.message === "timeout") {
|
|
3074
|
-
throw new Error(`qualityGate ${fieldName} CLI replay timed out after ${replay.timeoutMs}ms`);
|
|
3075
|
-
}
|
|
3076
|
-
throw error;
|
|
3077
|
-
}
|
|
3078
|
-
}
|
|
3079
|
-
|
|
3080
|
-
async function hasLiveProofPresence(
|
|
3081
|
-
cwd: string,
|
|
3082
|
-
row: JsonObject,
|
|
3083
|
-
fieldName: string,
|
|
3084
|
-
family: SurfaceFamily,
|
|
3085
|
-
): Promise<boolean> {
|
|
3086
|
-
if (await hasExistingNonEmptyArtifact(cwd, row.path)) return true;
|
|
3087
|
-
if (family === "cli") {
|
|
3088
|
-
const record = await readCliReplayRecord(cwd, row, fieldName);
|
|
3089
|
-
if (record) return true;
|
|
3090
|
-
}
|
|
3091
|
-
return false;
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
async function validateLiveSurfaceProofPresence(
|
|
3095
|
-
cwd: string,
|
|
3096
|
-
family: SurfaceFamily,
|
|
3097
|
-
artifactIds: string[],
|
|
3098
|
-
artifactRefs: Map<string, JsonObject>,
|
|
3099
|
-
): Promise<void> {
|
|
3100
|
-
if (!isLiveSurfaceFamily(family)) return;
|
|
3101
|
-
for (const artifactId of artifactIds) {
|
|
3102
|
-
if (
|
|
3103
|
-
await hasLiveProofPresence(cwd, artifactRefs.get(artifactId)!, `executorQa.artifactRefs.${artifactId}`, family)
|
|
3104
|
-
)
|
|
3105
|
-
return;
|
|
3106
|
-
}
|
|
3107
|
-
throw new Error(
|
|
3108
|
-
`qualityGate ${artifactIds.map(id => `executorQa.artifactRefs.${id}`).join(", ")} must reference a live proof artifact, structural capture, or CLI replay; inlineEvidence and typed verifiedReceipt do not prove live surfaces`,
|
|
3109
|
-
);
|
|
3110
|
-
}
|
|
3111
|
-
async function validateSurfaceStructuralRequirement(
|
|
3112
|
-
cwd: string,
|
|
3113
|
-
family: SurfaceFamily,
|
|
3114
|
-
artifactIds: string[],
|
|
3115
|
-
artifactRefs: Map<string, JsonObject>,
|
|
3116
|
-
fieldName: string,
|
|
3117
|
-
): Promise<void> {
|
|
3118
|
-
if (family !== "web" && family !== "native") return;
|
|
3119
|
-
let hasScreenshot = false;
|
|
3120
|
-
let hasAutomation = false;
|
|
3121
|
-
let hasPty = false;
|
|
3122
|
-
for (const artifactId of artifactIds) {
|
|
3123
|
-
const artifact = artifactRefs.get(artifactId)!;
|
|
3124
|
-
const kind = structuralArtifactKind(artifact);
|
|
3125
|
-
if (!kind) continue;
|
|
3126
|
-
const valid = await validateStructuralArtifact(cwd, artifact, `executorQa.artifactRefs.${artifactId}`, {
|
|
3127
|
-
surfaceFamily: family,
|
|
3128
|
-
live: true,
|
|
3129
|
-
});
|
|
3130
|
-
if (kind === "screenshot" && valid) hasScreenshot = true;
|
|
3131
|
-
if (kind === "automation" && valid) hasAutomation = true;
|
|
3132
|
-
if (kind === "pty" && valid) hasPty = true;
|
|
3133
|
-
}
|
|
3134
|
-
if (family === "web" && (!hasScreenshot || !hasAutomation)) {
|
|
3135
|
-
throw new Error(
|
|
3136
|
-
`qualityGate ${fieldName} for GUI/web surfaces must include a valid automation transcript and non-uniform screenshot`,
|
|
3137
|
-
);
|
|
3138
|
-
}
|
|
3139
|
-
if (family === "native" && !hasScreenshot && !hasAutomation && !hasPty) {
|
|
3140
|
-
throw new Error(
|
|
3141
|
-
`qualityGate ${fieldName} for native surfaces must include a valid screenshot, PTY capture, or app-automation transcript`,
|
|
3142
|
-
);
|
|
3143
|
-
}
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
async function validateArtifactProof(
|
|
3147
|
-
cwd: string,
|
|
3148
|
-
row: JsonObject,
|
|
3149
|
-
fieldName: string,
|
|
3150
|
-
options: { surfaceFamily: SurfaceFamily; live: boolean },
|
|
3151
|
-
): Promise<void> {
|
|
3152
|
-
if (await hasExistingNonEmptyArtifact(cwd, row.path)) return;
|
|
3153
|
-
if (await validateStructuralArtifact(cwd, row, fieldName, options)) return;
|
|
3154
|
-
if (options.surfaceFamily === "cli" && (await validateCliReplay(cwd, row, fieldName, { live: options.live })))
|
|
3155
|
-
return;
|
|
3156
|
-
if (!options.live && (hasTypedVerifiedReceipt(row.verifiedReceipt) || hasTypedVerifiedReceipt(row.receipt))) return;
|
|
3157
|
-
const proofLabel = options.live
|
|
3158
|
-
? "a live proof artifact, structural capture, or CLI replay; inlineEvidence and typed verifiedReceipt do not prove live surfaces"
|
|
3159
|
-
: "an existing non-empty artifact path or a typed verifiedReceipt; inlineEvidence alone is not sufficient";
|
|
3160
|
-
throw new Error(`qualityGate ${fieldName} must reference ${proofLabel}`);
|
|
3161
|
-
}
|
|
2098
|
+
import {
|
|
2099
|
+
readCliReplayRecord,
|
|
2100
|
+
validateArtifactProof,
|
|
2101
|
+
validateCliReplay,
|
|
2102
|
+
validateLiveSurfaceProofPresence,
|
|
2103
|
+
validateReplayExemptFallback,
|
|
2104
|
+
validateStructuralArtifact,
|
|
2105
|
+
validateSurfaceStructuralRequirement,
|
|
2106
|
+
waitForReplayProcessWithTimeout,
|
|
2107
|
+
} from "./ultragoal-evidence";
|
|
2108
|
+
|
|
2109
|
+
export type { ReplayProcessHandle } from "./ultragoal-evidence";
|
|
2110
|
+
export {
|
|
2111
|
+
validateArtifactProof,
|
|
2112
|
+
validateCliReplay,
|
|
2113
|
+
validateLiveSurfaceProofPresence,
|
|
2114
|
+
validateReplayExemptFallback,
|
|
2115
|
+
validateStructuralArtifact,
|
|
2116
|
+
validateSurfaceStructuralRequirement,
|
|
2117
|
+
waitForReplayProcessWithTimeout,
|
|
2118
|
+
};
|
|
3162
2119
|
|
|
3163
2120
|
async function validateArtifactRefs(cwd: string, executorQa: JsonObject): Promise<Map<string, JsonObject>> {
|
|
3164
2121
|
void cwd;
|
|
@@ -3451,7 +2408,12 @@ function canonicalChangeSetRows(value: unknown, fieldName: string): UltragoalCha
|
|
|
3451
2408
|
if ("goalId" in record) throw new Error(`${fieldName}[${index}] must not contain goalId attribution`);
|
|
3452
2409
|
const status = nonEmptyString(record.status);
|
|
3453
2410
|
if (!status) throw new Error(`${fieldName}[${index}].status is required`);
|
|
3454
|
-
|
|
2411
|
+
const oldPath = nonEmptyString(record.oldPath);
|
|
2412
|
+
return {
|
|
2413
|
+
path: normalizeRepoPath(pathValue),
|
|
2414
|
+
status: status as UltragoalChangeStatus,
|
|
2415
|
+
...(oldPath ? { oldPath: normalizeRepoPath(oldPath) } : {}),
|
|
2416
|
+
};
|
|
3455
2417
|
});
|
|
3456
2418
|
}
|
|
3457
2419
|
|
|
@@ -3711,6 +2673,140 @@ function validateBatchCloseQualityGate(
|
|
|
3711
2673
|
throw new Error("validationBatchClose.unionChangeSet.unionHash does not match declared union");
|
|
3712
2674
|
requireNonEmptyString(close.coverageEvidence, "validationBatchClose.coverageEvidence");
|
|
3713
2675
|
}
|
|
2676
|
+
|
|
2677
|
+
function hydrateReviewedBatchReplacementClose(input: {
|
|
2678
|
+
gate: JsonObject;
|
|
2679
|
+
plan: UltragoalPlan;
|
|
2680
|
+
goal: UltragoalGoal;
|
|
2681
|
+
metadata: UltragoalValidationBatchMetadata;
|
|
2682
|
+
ledger: readonly UltragoalLedgerEvent[];
|
|
2683
|
+
changeSet?: UltragoalChangeSet;
|
|
2684
|
+
}): JsonObject {
|
|
2685
|
+
const requested = qualityGateObject(input.gate.validationBatchClose);
|
|
2686
|
+
if (requested?.kind !== "review-blocker-replacement-close") return input.gate;
|
|
2687
|
+
const expectedKeys = new Set(["schemaVersion", "kind", "replacementGoalId", "coverageEvidence"]);
|
|
2688
|
+
if (Object.keys(requested).some(key => !expectedKeys.has(key)))
|
|
2689
|
+
throw new Error("review-blocker replacement close contains unsupported fields");
|
|
2690
|
+
const replacementGoalId = nonEmptyString(requested.replacementGoalId);
|
|
2691
|
+
const coverageEvidence = nonEmptyString(requested.coverageEvidence);
|
|
2692
|
+
if (requested.schemaVersion !== 1 || !replacementGoalId || !coverageEvidence)
|
|
2693
|
+
throw new Error("review-blocker replacement close is malformed");
|
|
2694
|
+
if (input.goal.status !== "active" || input.metadata.finalGoalId !== input.goal.id)
|
|
2695
|
+
throw new Error("review-blocker replacement close requires the active durable validation-batch final goal");
|
|
2696
|
+
if (!input.changeSet) throw new Error("review-blocker replacement close requires a current cumulative change set");
|
|
2697
|
+
|
|
2698
|
+
const replacements = input.plan.goals.filter(
|
|
2699
|
+
goal => goal.steering?.kind === "review_blocker" && goal.steering.blockedGoalId === input.goal.id,
|
|
2700
|
+
);
|
|
2701
|
+
if (replacements.length !== 1 || replacements[0]?.id !== replacementGoalId)
|
|
2702
|
+
throw new Error("review-blocker replacement close requires exactly the declared replacement");
|
|
2703
|
+
const replacement = replacements[0]!;
|
|
2704
|
+
const replacementReceipt = replacement.completionVerification;
|
|
2705
|
+
if (replacement.status !== "complete" || replacementReceipt?.receiptKind !== "per-goal")
|
|
2706
|
+
throw new Error("review-blocker replacement close requires a completed per-goal replacement receipt");
|
|
2707
|
+
const replacementDiagnostic = validateReceiptFreshBase({
|
|
2708
|
+
plan: input.plan,
|
|
2709
|
+
ledger: input.ledger,
|
|
2710
|
+
goal: replacement,
|
|
2711
|
+
receipt: replacementReceipt,
|
|
2712
|
+
receiptKind: "per-goal",
|
|
2713
|
+
});
|
|
2714
|
+
if (replacementDiagnostic) throw new Error(`review-blocker replacement close ${replacementDiagnostic.message}`);
|
|
2715
|
+
const replacementCheckpointIndex = input.ledger.findIndex(
|
|
2716
|
+
event => event.eventId === replacementReceipt.checkpointLedgerEventId,
|
|
2717
|
+
);
|
|
2718
|
+
const reviewRecordedIndex = input.ledger.findIndex(
|
|
2719
|
+
event =>
|
|
2720
|
+
event.event === "review_blockers_recorded" &&
|
|
2721
|
+
event.goalId === input.goal.id &&
|
|
2722
|
+
event.blockerGoalId === replacement.id,
|
|
2723
|
+
);
|
|
2724
|
+
const reactivatedIndex = input.ledger.findIndex(
|
|
2725
|
+
(event, index) =>
|
|
2726
|
+
index > replacementCheckpointIndex &&
|
|
2727
|
+
event.event === "goal_checkpointed" &&
|
|
2728
|
+
event.goalId === input.goal.id &&
|
|
2729
|
+
event.status === "active",
|
|
2730
|
+
);
|
|
2731
|
+
if (reviewRecordedIndex < 0 || reviewRecordedIndex >= replacementCheckpointIndex || reactivatedIndex < 0)
|
|
2732
|
+
throw new Error("review-blocker replacement close lacks durable supersession and reopening evidence");
|
|
2733
|
+
|
|
2734
|
+
const historicalRequiredGoalIds = input.plan.goals
|
|
2735
|
+
.filter(goal => goal.id !== input.goal.id && goal.status !== "superseded")
|
|
2736
|
+
.map(goal => goal.id);
|
|
2737
|
+
const aggregateGoals = input.plan.goals.filter(goal => {
|
|
2738
|
+
const receipt = goal.completionVerification;
|
|
2739
|
+
return goal.status === "complete" && receipt?.receiptKind === "final-aggregate";
|
|
2740
|
+
});
|
|
2741
|
+
const aggregateGoal = aggregateGoals.find(goal => {
|
|
2742
|
+
const receipt = goal.completionVerification!;
|
|
2743
|
+
const event = findLedgerReceiptEvent(input.ledger, receipt);
|
|
2744
|
+
return (
|
|
2745
|
+
event !== null &&
|
|
2746
|
+
hashStructuredValue(event.qualityGateJson) === receipt.qualityGateHash &&
|
|
2747
|
+
goal.updatedAt === receipt.verifiedAt &&
|
|
2748
|
+
receipt.basis.relevantGoalIdsBeforeCheckpoint.length === historicalRequiredGoalIds.length &&
|
|
2749
|
+
receipt.basis.relevantGoalIdsBeforeCheckpoint.every(
|
|
2750
|
+
(goalId, index) => goalId === historicalRequiredGoalIds[index],
|
|
2751
|
+
)
|
|
2752
|
+
);
|
|
2753
|
+
});
|
|
2754
|
+
if (!aggregateGoal)
|
|
2755
|
+
throw new Error(
|
|
2756
|
+
"review-blocker replacement close requires a fresh final-aggregate receipt covering required goals",
|
|
2757
|
+
);
|
|
2758
|
+
|
|
2759
|
+
const memberMetadataHashes: Record<string, string> = {};
|
|
2760
|
+
const memberChangeSetHashes: Record<string, string> = {};
|
|
2761
|
+
const memberReceipts: JsonObject[] = [];
|
|
2762
|
+
for (const memberId of input.metadata.memberIds) {
|
|
2763
|
+
const member = input.plan.goals.find(goal => goal.id === memberId);
|
|
2764
|
+
if (!member?.validationBatch)
|
|
2765
|
+
throw new Error(`review-blocker replacement close references missing batch member ${memberId}`);
|
|
2766
|
+
memberMetadataHashes[memberId] = member.validationBatch.metadataHash;
|
|
2767
|
+
if (memberId === input.goal.id) continue;
|
|
2768
|
+
const receipt = requireDeferredMemberReceiptFresh(
|
|
2769
|
+
input.plan,
|
|
2770
|
+
input.ledger,
|
|
2771
|
+
member,
|
|
2772
|
+
"review-blocker replacement close",
|
|
2773
|
+
);
|
|
2774
|
+
memberChangeSetHashes[memberId] = receipt.validationBatch.changeSetHash;
|
|
2775
|
+
memberReceipts.push({
|
|
2776
|
+
goalId: memberId,
|
|
2777
|
+
receiptId: receipt.receiptId,
|
|
2778
|
+
checkpointLedgerEventId: receipt.checkpointLedgerEventId,
|
|
2779
|
+
qualityGateHash: receipt.qualityGateHash,
|
|
2780
|
+
changeSetHash: receipt.validationBatch.changeSetHash,
|
|
2781
|
+
role: "deferred-member",
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
const paths = input.changeSet.paths.map(row => ({ ...row }));
|
|
2785
|
+
memberChangeSetHashes[input.goal.id] = changeSetHashForPaths(paths);
|
|
2786
|
+
const unionHash = hashStructuredValue({
|
|
2787
|
+
memberChangeSetHashes,
|
|
2788
|
+
paths: paths.map(row => ({ path: row.path, status: row.status, oldPath: row.oldPath })),
|
|
2789
|
+
});
|
|
2790
|
+
return {
|
|
2791
|
+
...input.gate,
|
|
2792
|
+
validationBatchClose: {
|
|
2793
|
+
schemaVersion: 1,
|
|
2794
|
+
kind: "validation-batch-close",
|
|
2795
|
+
batchId: input.metadata.batchId,
|
|
2796
|
+
finalGoalId: input.metadata.finalGoalId,
|
|
2797
|
+
memberIds: [...input.metadata.memberIds],
|
|
2798
|
+
memberMetadataHashes,
|
|
2799
|
+
memberReceipts,
|
|
2800
|
+
unionChangeSet: {
|
|
2801
|
+
source: "validation-batch",
|
|
2802
|
+
memberChangeSetHashes,
|
|
2803
|
+
paths,
|
|
2804
|
+
unionHash,
|
|
2805
|
+
},
|
|
2806
|
+
coverageEvidence,
|
|
2807
|
+
},
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
3714
2810
|
async function readRequiredCompletionQualityGate(
|
|
3715
2811
|
cwd: string,
|
|
3716
2812
|
value: string | undefined,
|
|
@@ -3729,13 +2825,25 @@ async function readRequiredCompletionQualityGate(
|
|
|
3729
2825
|
const gate = await readStructuredValue(cwd, value);
|
|
3730
2826
|
const gateObject = qualityGateObject(gate);
|
|
3731
2827
|
if (!gateObject) throw new Error("qualityGate must be a JSON object");
|
|
3732
|
-
|
|
2828
|
+
const validationBatch = options.goal ? requireFreshValidationBatchMetadata(options.goal) : undefined;
|
|
2829
|
+
const hydratedGate =
|
|
2830
|
+
validationBatch && options.plan && options.goal && options.ledger
|
|
2831
|
+
? hydrateReviewedBatchReplacementClose({
|
|
2832
|
+
gate: gateObject,
|
|
2833
|
+
plan: options.plan,
|
|
2834
|
+
goal: options.goal,
|
|
2835
|
+
metadata: validationBatch,
|
|
2836
|
+
ledger: options.ledger,
|
|
2837
|
+
changeSet: options.changeSet,
|
|
2838
|
+
})
|
|
2839
|
+
: gateObject;
|
|
2840
|
+
await validateCompletionQualityGate(cwd, hydratedGate, {
|
|
3733
2841
|
changeSet: options.changeSet,
|
|
3734
2842
|
plan: options.plan,
|
|
3735
2843
|
goal: options.goal,
|
|
3736
2844
|
ledger: options.ledger,
|
|
3737
2845
|
});
|
|
3738
|
-
return
|
|
2846
|
+
return hydratedGate;
|
|
3739
2847
|
}
|
|
3740
2848
|
|
|
3741
2849
|
function validatePipelineCheckpointSafety(
|
|
@@ -3812,13 +2920,21 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
3812
2920
|
const evidence = input.evidence.trim();
|
|
3813
2921
|
if (!evidence) throw new Error("checkpoint evidence is required");
|
|
3814
2922
|
const ledgerBefore = await readUltragoalLedger(input.cwd);
|
|
3815
|
-
const
|
|
2923
|
+
const matchingIdempotentEvents = ledgerBefore.filter(
|
|
3816
2924
|
event =>
|
|
3817
2925
|
event.event === "goal_checkpointed" &&
|
|
3818
2926
|
event.goalId === goal.id &&
|
|
3819
2927
|
event.status === input.status &&
|
|
3820
2928
|
event.evidence === evidence,
|
|
3821
2929
|
);
|
|
2930
|
+
// Re-verification replays legitimately append repeated same-status,
|
|
2931
|
+
// same-evidence checkpoint events for one goal. The recorded receipt must
|
|
2932
|
+
// be compared against ITS OWN checkpoint event — resolved by the receipt's
|
|
2933
|
+
// checkpointLedgerEventId, falling back to the latest match — never the
|
|
2934
|
+
// oldest duplicate, which would wrongly report the current receipt stale.
|
|
2935
|
+
const matchingIdempotentEvent =
|
|
2936
|
+
matchingIdempotentEvents.find(event => event.eventId === goal.completionVerification?.checkpointLedgerEventId) ??
|
|
2937
|
+
matchingIdempotentEvents.at(-1);
|
|
3822
2938
|
const batchMetadata = input.status === "complete" ? requireFreshValidationBatchMetadata(goal) : undefined;
|
|
3823
2939
|
if (batchMetadata && goal.completionVerification?.validationBatch) {
|
|
3824
2940
|
const receiptBatch = goal.completionVerification.validationBatch;
|
|
@@ -3835,7 +2951,35 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
3835
2951
|
if (input.status === "complete" && goal.completionVerification?.validationBatch && !batchMetadata) {
|
|
3836
2952
|
throw new Error(`Goal ${goal.id} has stale validation batch completion receipt`);
|
|
3837
2953
|
}
|
|
3838
|
-
|
|
2954
|
+
// An identical-evidence complete replay is only a no-op while the recorded
|
|
2955
|
+
// receipt still validates fresh. When the goal row itself is untouched
|
|
2956
|
+
// (updatedAt still matches the receipt's verifiedAt) but later goal-tagged
|
|
2957
|
+
// ledger events (e.g. blocker classifications) or plan growth staled the
|
|
2958
|
+
// receipt, the replay is a genuine re-verification: it must run the full
|
|
2959
|
+
// quality gate and mint a fresh receipt, otherwise a completed goal with a
|
|
2960
|
+
// context-staled receipt can never be repaired (different evidence is
|
|
2961
|
+
// rejected on complete goals by design). A mutated goal row keeps the
|
|
2962
|
+
// fail-loud tamper handling in the idempotent branch below.
|
|
2963
|
+
const staleCompleteReceiptReplay =
|
|
2964
|
+
input.status === "complete" &&
|
|
2965
|
+
goal.status === "complete" &&
|
|
2966
|
+
goal.evidence === evidence &&
|
|
2967
|
+
Boolean(matchingIdempotentEvent) &&
|
|
2968
|
+
(!goal.completionVerification ||
|
|
2969
|
+
(goal.completionVerification.verifiedAt === goal.updatedAt &&
|
|
2970
|
+
validateReceiptFreshBase({
|
|
2971
|
+
plan,
|
|
2972
|
+
ledger: ledgerBefore,
|
|
2973
|
+
goal,
|
|
2974
|
+
receipt: goal.completionVerification,
|
|
2975
|
+
receiptKind: goal.completionVerification.receiptKind,
|
|
2976
|
+
}) !== null));
|
|
2977
|
+
if (
|
|
2978
|
+
goal.status === input.status &&
|
|
2979
|
+
goal.evidence === evidence &&
|
|
2980
|
+
matchingIdempotentEvent &&
|
|
2981
|
+
!staleCompleteReceiptReplay
|
|
2982
|
+
) {
|
|
3839
2983
|
if (batchMetadata) {
|
|
3840
2984
|
const receipt = goal.completionVerification;
|
|
3841
2985
|
const receiptBatch = receipt?.validationBatch;
|
|
@@ -3857,6 +3001,18 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
3857
3001
|
)
|
|
3858
3002
|
throw new Error(`Goal ${goal.id} has stale validation batch metadata hash in close receipt`);
|
|
3859
3003
|
}
|
|
3004
|
+
// A complete goal whose row changed after its receipt was verified is
|
|
3005
|
+
// neither a clean no-op nor a repairable context-stale replay: fail loud
|
|
3006
|
+
// instead of silently laundering a tampered/inconsistent durable row.
|
|
3007
|
+
if (
|
|
3008
|
+
input.status === "complete" &&
|
|
3009
|
+
goal.completionVerification &&
|
|
3010
|
+
goal.completionVerification.verifiedAt !== goal.updatedAt
|
|
3011
|
+
) {
|
|
3012
|
+
throw new Error(
|
|
3013
|
+
`Goal ${goal.id} changed after its completion receipt was verified; refusing idempotent replay. Investigate the durable goals.json row before re-checkpointing.`,
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3860
3016
|
// Idempotent re-checkpoint: this goal is already recorded in the target status with the same
|
|
3861
3017
|
// evidence, so skip the plan rewrite and ledger append to avoid duplicate goal_checkpointed
|
|
3862
3018
|
// events. The ledger is the dedup source of truth because it is exactly what a duplicate write
|
|
@@ -3868,7 +3024,7 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
3868
3024
|
const changeSet = input.status === "complete" ? await computeCheckpointChangeSet(input.cwd) : undefined;
|
|
3869
3025
|
if (input.status === "complete") {
|
|
3870
3026
|
validatePipelineCheckpointSafety(plan, goal, changeSet);
|
|
3871
|
-
validateCompleteCheckpointTargetGoal(goal);
|
|
3027
|
+
if (!staleCompleteReceiptReplay) validateCompleteCheckpointTargetGoal(goal);
|
|
3872
3028
|
}
|
|
3873
3029
|
const qualityGateJson =
|
|
3874
3030
|
input.status === "complete"
|
|
@@ -3895,7 +3051,7 @@ export async function checkpointUltragoalGoal(input: {
|
|
|
3895
3051
|
blockedGoal.updatedAt = now;
|
|
3896
3052
|
}
|
|
3897
3053
|
}
|
|
3898
|
-
const receiptKind = input.status === "complete" ? chooseReceiptKind(plan, goal, input.status) : null;
|
|
3054
|
+
const receiptKind = input.status === "complete" ? chooseReceiptKind(plan, ledgerBefore, goal, input.status) : null;
|
|
3899
3055
|
const pendingCheckpointEventId = crypto.randomUUID();
|
|
3900
3056
|
if (input.status === "complete" && receiptKind && qualityGateJson && !Array.isArray(qualityGateJson)) {
|
|
3901
3057
|
goal.completionVerification = buildCompletionReceipt({
|
|
@@ -4466,168 +3622,15 @@ async function readOptionalExecutorQa(cwd: string, value: string | undefined): P
|
|
|
4466
3622
|
return structured as JsonObject;
|
|
4467
3623
|
}
|
|
4468
3624
|
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
new Response(proc.stderr).text(),
|
|
4479
|
-
proc.exited,
|
|
4480
|
-
]);
|
|
4481
|
-
clearTimeout(timeout);
|
|
4482
|
-
return { ok: exitCode === 0, stdout, stderr };
|
|
4483
|
-
} catch (error) {
|
|
4484
|
-
return { ok: false, stdout: "", stderr: error instanceof Error ? error.message : String(error) };
|
|
4485
|
-
}
|
|
4486
|
-
}
|
|
4487
|
-
|
|
4488
|
-
export async function resolveGitBase(cwd: string, branch?: string): Promise<string> {
|
|
4489
|
-
if (branch) {
|
|
4490
|
-
const exists = await spawnText(["git", "rev-parse", "--verify", branch], { cwd, timeoutMs: 3000 });
|
|
4491
|
-
if (exists.ok) return branch;
|
|
4492
|
-
} else {
|
|
4493
|
-
// Prefer the NEAREST integration base (the branch this work actually forks
|
|
4494
|
-
// from) rather than always `main`. A branch opened against `dev` must be
|
|
4495
|
-
// scoped to `dev`; using a stale `main` sweeps in unrelated trunk history
|
|
4496
|
-
// and mis-attributes other people's changes to this story (e.g. falsely
|
|
4497
|
-
// tripping change-scoped gates). Among existing candidates, pick the one
|
|
4498
|
-
// whose merge-base with HEAD is closest to HEAD (fewest commits ahead).
|
|
4499
|
-
const candidates = ["origin/dev", "dev", "origin/main", "origin/master", "main", "master"];
|
|
4500
|
-
let best: { ref: string; ahead: number } | undefined;
|
|
4501
|
-
for (const candidate of candidates) {
|
|
4502
|
-
const exists = await spawnText(["git", "rev-parse", "--verify", candidate], { cwd, timeoutMs: 3000 });
|
|
4503
|
-
if (!exists.ok) continue;
|
|
4504
|
-
const mergeBase = await spawnText(["git", "merge-base", "HEAD", candidate], { cwd, timeoutMs: 3000 });
|
|
4505
|
-
if (!mergeBase.ok || !mergeBase.stdout.trim()) continue;
|
|
4506
|
-
const count = await spawnText(["git", "rev-list", "--count", `${mergeBase.stdout.trim()}..HEAD`], {
|
|
4507
|
-
cwd,
|
|
4508
|
-
timeoutMs: 3000,
|
|
4509
|
-
});
|
|
4510
|
-
const ahead = Number.parseInt(count.stdout.trim(), 10);
|
|
4511
|
-
if (!Number.isFinite(ahead)) continue;
|
|
4512
|
-
if (!best || ahead < best.ahead) best = { ref: candidate, ahead };
|
|
4513
|
-
}
|
|
4514
|
-
if (best) return best.ref;
|
|
4515
|
-
}
|
|
4516
|
-
const mergeBase = await spawnText(["git", "merge-base", "HEAD", "origin/main"], { cwd, timeoutMs: 3000 });
|
|
4517
|
-
if (mergeBase.ok && mergeBase.stdout.trim()) return mergeBase.stdout.trim();
|
|
4518
|
-
return "HEAD~1";
|
|
4519
|
-
}
|
|
4520
|
-
|
|
4521
|
-
function parseGitNameStatus(output: string): UltragoalChangeSetPath[] {
|
|
4522
|
-
const rows: UltragoalChangeSetPath[] = [];
|
|
4523
|
-
for (const line of output.split("\n")) {
|
|
4524
|
-
const trimmed = line.trim();
|
|
4525
|
-
if (!trimmed) continue;
|
|
4526
|
-
const parts = trimmed.split(/\s+/);
|
|
4527
|
-
const statusCode = parts[0] ?? "";
|
|
4528
|
-
let status: UltragoalChangeStatus = "unknown";
|
|
4529
|
-
if (statusCode.startsWith("A")) status = "added";
|
|
4530
|
-
else if (statusCode.startsWith("M")) status = "modified";
|
|
4531
|
-
else if (statusCode.startsWith("D")) status = "deleted";
|
|
4532
|
-
else if (statusCode.startsWith("R")) status = "renamed";
|
|
4533
|
-
else if (statusCode.startsWith("C")) status = "copied";
|
|
4534
|
-
const pathValue = status === "renamed" || status === "copied" ? parts[2] : parts[1];
|
|
4535
|
-
if (!pathValue) continue;
|
|
4536
|
-
const oldPath = status === "renamed" || status === "copied" ? parts[1] : undefined;
|
|
4537
|
-
rows.push({
|
|
4538
|
-
path: normalizeRepoPath(pathValue),
|
|
4539
|
-
oldPath: oldPath ? normalizeRepoPath(oldPath) : undefined,
|
|
4540
|
-
status,
|
|
4541
|
-
category: categorizeComputerChangePath(pathValue),
|
|
4542
|
-
});
|
|
4543
|
-
}
|
|
4544
|
-
return rows;
|
|
4545
|
-
}
|
|
4546
|
-
|
|
4547
|
-
function categorizeCiChangedPath(value: string): UltragoalChangeCategory {
|
|
4548
|
-
// CI_DEV_CHANGED_PATHS intentionally carries path names only. Mixed registries
|
|
4549
|
-
// such as settings-schema.ts require diff-level narrowing; without the diff,
|
|
4550
|
-
// treating the whole registry as computer-control source forces the mandatory
|
|
4551
|
-
// computer red-team suite on unrelated settings changes.
|
|
4552
|
-
if (isSettingsSchemaPath(value)) return "other";
|
|
4553
|
-
return categorizeComputerChangePath(value);
|
|
4554
|
-
}
|
|
4555
|
-
|
|
4556
|
-
function ciDevChangedPathRows(): UltragoalChangeSetPath[] {
|
|
4557
|
-
const raw = process.env.CI_DEV_CHANGED_PATHS;
|
|
4558
|
-
if (!raw) return [];
|
|
4559
|
-
return raw
|
|
4560
|
-
.split(/\r?\n/)
|
|
4561
|
-
.map(row => row.trim())
|
|
4562
|
-
.filter(Boolean)
|
|
4563
|
-
.map(pathValue => ({
|
|
4564
|
-
path: normalizeRepoPath(pathValue),
|
|
4565
|
-
status: "unknown" as UltragoalChangeStatus,
|
|
4566
|
-
category: categorizeCiChangedPath(pathValue),
|
|
4567
|
-
}));
|
|
4568
|
-
}
|
|
4569
|
-
|
|
4570
|
-
function mergeChangeSetPaths(groups: UltragoalChangeSetPath[][]): UltragoalChangeSetPath[] {
|
|
4571
|
-
const byKey = new Map<string, UltragoalChangeSetPath>();
|
|
4572
|
-
for (const row of groups.flat()) byKey.set(`${row.oldPath ?? ""}\u0000${row.path}`, row);
|
|
4573
|
-
return [...byKey.values()];
|
|
4574
|
-
}
|
|
4575
|
-
|
|
4576
|
-
async function computeCheckpointChangeSet(cwd: string): Promise<UltragoalChangeSet | undefined> {
|
|
4577
|
-
const ciChangedPaths = ciDevChangedPathRows();
|
|
4578
|
-
const inGit = await spawnText(["git", "rev-parse", "--is-inside-work-tree"], { cwd, timeoutMs: 3000 });
|
|
4579
|
-
if (!inGit.ok || inGit.stdout.trim() !== "true") {
|
|
4580
|
-
if (ciChangedPaths.length === 0) return undefined;
|
|
4581
|
-
return { source: "checkpoint-git", paths: ciChangedPaths, trusted: true };
|
|
4582
|
-
}
|
|
4583
|
-
const baseRef = await resolveGitBase(cwd);
|
|
4584
|
-
const base = baseRef;
|
|
4585
|
-
const mergeBase = await spawnText(["git", "merge-base", "HEAD", baseRef], { cwd, timeoutMs: 3000 });
|
|
4586
|
-
const [committed, unstaged, staged, stat, committedDiff, unstagedDiff, stagedDiff] = await Promise.all([
|
|
4587
|
-
spawnText(["git", "diff", "--name-status", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
4588
|
-
spawnText(["git", "diff", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
4589
|
-
spawnText(["git", "diff", "--cached", "--name-status"], { cwd, timeoutMs: 5000 }),
|
|
4590
|
-
spawnText(["git", "diff", "--stat", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
4591
|
-
spawnText(["git", "diff", `${base}...HEAD`], { cwd, timeoutMs: 5000 }),
|
|
4592
|
-
spawnText(["git", "diff"], { cwd, timeoutMs: 5000 }),
|
|
4593
|
-
spawnText(["git", "diff", "--cached"], { cwd, timeoutMs: 5000 }),
|
|
4594
|
-
]);
|
|
4595
|
-
if (!committed.ok && !unstaged.ok && !staged.ok && ciChangedPaths.length === 0) return undefined;
|
|
4596
|
-
const gitPaths = mergeChangeSetPaths([
|
|
4597
|
-
parseGitNameStatus(committed.stdout),
|
|
4598
|
-
parseGitNameStatus(unstaged.stdout),
|
|
4599
|
-
parseGitNameStatus(staged.stdout),
|
|
4600
|
-
]);
|
|
4601
|
-
const paths = gitPaths.length > 0 ? gitPaths : ciChangedPaths;
|
|
4602
|
-
return {
|
|
4603
|
-
source: "checkpoint-git",
|
|
4604
|
-
baseRef,
|
|
4605
|
-
mergeBase: mergeBase.ok && mergeBase.stdout.trim() ? mergeBase.stdout.trim() : undefined,
|
|
4606
|
-
headRef: "HEAD",
|
|
4607
|
-
paths,
|
|
4608
|
-
rawDiffStat: stat.stdout,
|
|
4609
|
-
rawDiff: [committedDiff.stdout, unstagedDiff.stdout, stagedDiff.stdout].filter(Boolean).join("\n"),
|
|
4610
|
-
trusted: true,
|
|
4611
|
-
};
|
|
4612
|
-
}
|
|
4613
|
-
|
|
4614
|
-
function parseUnifiedDiffPaths(diff: string): UltragoalChangeSetPath[] {
|
|
4615
|
-
const paths: UltragoalChangeSetPath[] = [];
|
|
4616
|
-
for (const line of diff.split("\n")) {
|
|
4617
|
-
if (!line.startsWith("diff --git ")) continue;
|
|
4618
|
-
const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
|
|
4619
|
-
if (!match) continue;
|
|
4620
|
-
const oldPath = normalizeRepoPath(match[1]!);
|
|
4621
|
-
const newPath = normalizeRepoPath(match[2]!);
|
|
4622
|
-
paths.push({
|
|
4623
|
-
path: newPath,
|
|
4624
|
-
oldPath: oldPath === newPath ? undefined : oldPath,
|
|
4625
|
-
status: oldPath === newPath ? "modified" : "renamed",
|
|
4626
|
-
category: categorizeComputerChangePath(newPath),
|
|
4627
|
-
});
|
|
4628
|
-
}
|
|
4629
|
-
return paths;
|
|
4630
|
-
}
|
|
3625
|
+
import {
|
|
3626
|
+
computeCheckpointChangeSet,
|
|
3627
|
+
parseGitNameStatus,
|
|
3628
|
+
parseUnifiedDiffPaths,
|
|
3629
|
+
resolveGitBase,
|
|
3630
|
+
spawnText,
|
|
3631
|
+
} from "./ultragoal-change-set";
|
|
3632
|
+
|
|
3633
|
+
export { computeCheckpointChangeSet, parseGitNameStatus, parseUnifiedDiffPaths, resolveGitBase, spawnText };
|
|
4631
3634
|
|
|
4632
3635
|
function changeSetFromReviewSource(source: JsonObject): UltragoalChangeSet | undefined {
|
|
4633
3636
|
const kind = nonEmptyString(source.kind);
|