@gajae-code/coding-agent 0.11.0 → 0.11.2
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 +63 -1
- package/dist/types/async/job-manager.d.ts +46 -7
- package/dist/types/cli/config-cli.d.ts +11 -1
- package/dist/types/cli/notify-cli.d.ts +1 -0
- package/dist/types/cli/session-picker.d.ts +2 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/mcp-serve.d.ts +33 -0
- package/dist/types/commands/mcp.d.ts +1 -1
- package/dist/types/commands/notify.d.ts +0 -2
- package/dist/types/commands/sdk.d.ts +1 -1
- package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
- package/dist/types/config/file-lock.d.ts +14 -4
- package/dist/types/config/keybindings.d.ts +42 -1
- package/dist/types/config/model-auth.d.ts +3 -0
- package/dist/types/config/model-bindings-applier.d.ts +13 -0
- package/dist/types/config/model-discovery-manager.d.ts +49 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-registry.d.ts +16 -15
- package/dist/types/config/model-resolver.d.ts +28 -18
- package/dist/types/config/models-config-schema.d.ts +11 -0
- package/dist/types/config/settings-schema.d.ts +56 -1
- package/dist/types/config/settings.d.ts +14 -11
- package/dist/types/coordinator-mcp/policy.d.ts +5 -0
- package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +0 -6
- package/dist/types/edit/index.d.ts +3 -0
- package/dist/types/eval/py/env.d.ts +9 -0
- package/dist/types/extensibility/extensions/loader.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -3
- package/dist/types/extensibility/hooks/runner.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
- package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
- package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
- package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
- package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
- package/dist/types/gjc-runtime/team-store.d.ts +160 -0
- package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
- package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
- package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
- package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/goals/state.d.ts +8 -0
- package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
- package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
- package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/state.d.ts +11 -4
- package/dist/types/lsp/lspmux.d.ts +5 -3
- package/dist/types/lsp/path-trust.d.ts +1 -0
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memories/storage.d.ts +1 -0
- package/dist/types/modes/action-registry.d.ts +29 -0
- package/dist/types/modes/components/chrome.d.ts +14 -0
- package/dist/types/modes/components/command-palette.d.ts +27 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +2 -1
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
- package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
- package/dist/types/modes/components/queue-pane.d.ts +12 -0
- package/dist/types/modes/components/read-tool-group.d.ts +6 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
- package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
- package/dist/types/modes/components/settings-selector.d.ts +1 -1
- package/dist/types/modes/components/show-images-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +1 -94
- package/dist/types/modes/components/tasks-pane.d.ts +15 -0
- package/dist/types/modes/components/theme-selector.d.ts +1 -1
- package/dist/types/modes/components/tool-execution.d.ts +15 -0
- package/dist/types/modes/components/tool-status-header.d.ts +111 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
- package/dist/types/modes/controllers/input-controller.d.ts +8 -1
- package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
- package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
- package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
- package/dist/types/modes/interactive-mode.d.ts +21 -19
- package/dist/types/modes/jobs-observer.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
- package/dist/types/modes/tasks-aggregator.d.ts +39 -0
- package/dist/types/modes/transcript-item-registry.d.ts +60 -0
- package/dist/types/modes/types.d.ts +22 -15
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +78 -12
- package/dist/types/sdk/broker/ensure.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
- package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
- package/dist/types/sdk/broker/session-index.d.ts +0 -1
- package/dist/types/sdk/broker/state-version.d.ts +2 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
- package/dist/types/sdk/bus/config-commands.d.ts +1 -1
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
- package/dist/types/sdk/bus/html-format.d.ts +3 -2
- package/dist/types/sdk/bus/index.d.ts +27 -0
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/notification-service.d.ts +20 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
- package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
- package/dist/types/sdk/bus/rich-document.d.ts +7 -0
- package/dist/types/sdk/bus/rich-render.d.ts +18 -41
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
- package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/session-directory.d.ts +56 -0
- package/dist/types/session/agent-session.d.ts +82 -47
- package/dist/types/session/agent-storage.d.ts +7 -0
- package/dist/types/session/fallback-chain-controller.d.ts +10 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
- package/dist/types/session/messages.d.ts +16 -0
- package/dist/types/session/session-manager-internal.d.ts +14 -0
- package/dist/types/session/session-manager.d.ts +82 -12
- package/dist/types/session/session-storage.d.ts +43 -3
- package/dist/types/session/volatile-context-pruning.d.ts +15 -0
- package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
- package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
- package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
- package/dist/types/system-prompt.d.ts +2 -11
- package/dist/types/task/executor.d.ts +3 -1
- package/dist/types/thinking.d.ts +7 -0
- package/dist/types/tool-discovery/tool-index.d.ts +1 -50
- package/dist/types/tools/ask.d.ts +64 -2
- package/dist/types/tools/bash.d.ts +3 -0
- package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
- package/dist/types/tools/cron.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +1 -0
- package/dist/types/tools/index.d.ts +30 -15
- package/dist/types/tools/output-meta.d.ts +1 -0
- package/dist/types/tools/read.d.ts +3 -0
- package/dist/types/tools/search-tool-bm25.d.ts +3 -4
- package/dist/types/tools/subagent-render.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +6 -1
- package/dist/types/utils/event-bus.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +3 -1
- package/package.json +21 -9
- package/scripts/build-sdk-package-smoke.ts +39 -1
- package/scripts/generate-hotkeys-docs.ts +74 -0
- package/scripts/generate-sdk-operation-inventory.ts +64 -20
- package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
- package/src/async/job-manager.ts +455 -69
- package/src/cli/config-cli.ts +75 -2
- package/src/cli/notify-cli.ts +92 -26
- package/src/cli/session-picker.ts +20 -3
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/harness.ts +34 -106
- package/src/commands/launch.ts +6 -4
- package/src/commands/mcp-serve.ts +85 -8
- package/src/commands/mcp.ts +2 -2
- package/src/commands/notify.ts +8 -4
- package/src/commands/sdk.ts +61 -40
- package/src/commands/ultragoal.ts +4 -1
- package/src/config/atomic-yaml-patch.ts +80 -13
- 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-registry.ts +169 -302
- package/src/config/model-resolver.ts +361 -369
- package/src/config/models-config-schema.ts +5 -4
- package/src/config/settings-schema.ts +159 -2
- package/src/config/settings.ts +458 -482
- 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 +1702 -238
- package/src/debug/index.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
- package/src/defaults/gjc/skills/team/SKILL.md +16 -0
- package/src/discovery/helpers.ts +1 -50
- package/src/edit/index.ts +64 -0
- package/src/eval/py/env.ts +26 -0
- package/src/eval/py/kernel.ts +5 -3
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +12 -3
- package/src/extensibility/extensions/types.ts +39 -1
- package/src/extensibility/gjc-plugins/installer.ts +16 -16
- package/src/extensibility/hooks/runner.ts +2 -2
- package/src/extensibility/runtime-skill-discovery.ts +33 -13
- package/src/extensibility/shared-events.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
- package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
- package/src/gjc-runtime/deep-interview-state.ts +317 -5
- package/src/gjc-runtime/goal-mode-request.ts +55 -8
- package/src/gjc-runtime/launch-tmux.ts +2 -5
- package/src/gjc-runtime/launch-worktree.ts +5 -5
- package/src/gjc-runtime/linux-proc.ts +71 -0
- package/src/gjc-runtime/ralplan-runtime.ts +74 -90
- package/src/gjc-runtime/session-resolution.ts +18 -3
- package/src/gjc-runtime/session-state-sidecar.ts +6 -107
- package/src/gjc-runtime/state-migrations.ts +1 -1
- package/src/gjc-runtime/state-runtime.ts +320 -249
- package/src/gjc-runtime/state-writer.ts +36 -40
- package/src/gjc-runtime/team-launch.ts +319 -0
- package/src/gjc-runtime/team-notify.ts +326 -0
- package/src/gjc-runtime/team-runtime.ts +1099 -1482
- package/src/gjc-runtime/team-store.ts +828 -0
- package/src/gjc-runtime/team-workers.ts +962 -0
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
- package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
- package/src/gjc-runtime/tmux-sessions.ts +4 -21
- package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
- package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
- package/src/gjc-runtime/ultragoal-guard.ts +83 -12
- package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
- package/src/gjc-runtime/workflow-cli-common.ts +30 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
- package/src/gjc-runtime/workflow-manifest.ts +18 -0
- 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/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/session-lease.ts +12 -130
- package/src/hindsight/backend.ts +26 -27
- package/src/hindsight/state.ts +101 -32
- package/src/hooks/skill-state.ts +17 -11
- package/src/internal-urls/docs-index.generated.ts +21 -18
- package/src/lsp/client.ts +1 -1
- package/src/lsp/config.ts +145 -26
- package/src/lsp/index.ts +1 -1
- package/src/lsp/lspmux.ts +39 -10
- package/src/lsp/path-trust.ts +81 -0
- package/src/main.ts +28 -8
- package/src/memories/index.ts +1 -0
- package/src/memories/storage.ts +6 -2
- package/src/modes/action-registry.ts +165 -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 +1 -1
- package/src/modes/components/custom-provider-wizard.ts +51 -14
- package/src/modes/components/index.ts +2 -1
- package/src/modes/components/pet-selector.ts +11 -17
- 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/session-observer-overlay.ts +207 -772
- package/src/modes/components/sessions-dashboard.ts +181 -0
- package/src/modes/components/settings-selector.ts +1 -1
- 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/transcript-viewer-overlay.ts +384 -0
- package/src/modes/controllers/btw-controller.ts +1 -0
- package/src/modes/controllers/command-controller.ts +9 -15
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +72 -25
- package/src/modes/controllers/goal-mode-controller.ts +474 -0
- package/src/modes/controllers/input-controller.ts +764 -229
- package/src/modes/controllers/mode-gate.ts +24 -0
- package/src/modes/controllers/plan-mode-controller.ts +517 -0
- package/src/modes/controllers/selector-controller.ts +225 -10
- package/src/modes/controllers/stt-controller.ts +125 -0
- package/src/modes/interactive-mode.ts +302 -1151
- package/src/modes/jobs-observer.ts +3 -0
- package/src/modes/runtime-init.ts +8 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
- package/src/modes/tasks-aggregator.ts +199 -0
- package/src/modes/transcript-item-registry.ts +229 -0
- package/src/modes/types.ts +47 -20
- package/src/modes/utils/ui-helpers.ts +17 -9
- 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 +2 -8
- package/src/prompts/agents/critic.md +2 -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 +1 -23
- package/src/prompts/system/plan-mode-approved.md +8 -0
- package/src/prompts/system/system-prompt.md +17 -96
- 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/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/manager.ts +1 -1
- package/src/sdk/broker/broker.ts +206 -78
- package/src/sdk/broker/discovery.ts +21 -2
- package/src/sdk/broker/ensure.ts +85 -25
- package/src/sdk/broker/lifecycle-codec.ts +11 -0
- package/src/sdk/broker/lifecycle-ledger.ts +371 -74
- package/src/sdk/broker/lifecycle.ts +1399 -142
- package/src/sdk/broker/process-incarnation.ts +5 -13
- package/src/sdk/broker/session-index.ts +145 -56
- package/src/sdk/broker/state-version.ts +21 -0
- package/src/sdk/broker/transport.ts +1 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -28
- package/src/sdk/bus/chat-effect-journal.ts +14 -4
- package/src/sdk/bus/config-commands.ts +5 -4
- package/src/sdk/bus/config.ts +98 -0
- package/src/sdk/bus/conversation-store.ts +5 -29
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +25 -8
- package/src/sdk/bus/html-format.ts +20 -3
- package/src/sdk/bus/index.ts +834 -113
- package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
- package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
- package/src/sdk/bus/natives-augment.d.ts +26 -1
- package/src/sdk/bus/notification-service.ts +168 -20
- package/src/sdk/bus/rate-limit-pool.ts +58 -14
- package/src/sdk/bus/recent-activity.ts +188 -74
- package/src/sdk/bus/rich-document.ts +258 -0
- package/src/sdk/bus/rich-render.ts +62 -70
- package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
- package/src/sdk/bus/telegram-daemon.ts +1491 -247
- package/src/sdk/bus/threaded-inbound.ts +8 -3
- package/src/sdk/bus/threaded-render.ts +57 -1
- package/src/sdk/bus/topic-registry.ts +120 -13
- package/src/sdk/host/control/dispatch.ts +14 -2
- package/src/sdk/host/control/operations.ts +6 -1
- package/src/sdk/host/host.ts +31 -1
- package/src/sdk/host/query/handlers.ts +20 -22
- package/src/sdk/host/query/revision-store.ts +80 -57
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
- package/src/sdk/session-directory.ts +141 -0
- package/src/sdk/session.ts +458 -479
- package/src/session/agent-session.ts +1901 -637
- package/src/session/agent-storage.ts +18 -0
- package/src/session/cache-economics.ts +25 -8
- package/src/session/fallback-chain-controller.ts +53 -3
- package/src/session/internal/managed-session-scope.ts +2226 -0
- package/src/session/internal/managed-session-storage.ts +453 -0
- package/src/session/messages.ts +91 -2
- package/src/session/session-manager-internal.ts +33 -0
- package/src/session/session-manager.ts +1112 -333
- package/src/session/session-storage.ts +444 -39
- package/src/session/volatile-context-pruning.ts +78 -0
- package/src/setup/credential-auto-import.ts +2 -1
- package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
- package/src/setup/provider-onboarding.ts +36 -61
- package/src/setup/provider-presets.json +5 -0
- package/src/skill-state/active-state.ts +3 -13
- package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
- package/src/skill-state/workflow-state-contract.ts +6 -41
- package/src/slash-commands/builtin-registry.ts +46 -11
- package/src/system-prompt.ts +37 -44
- package/src/task/agents.ts +19 -1
- package/src/task/executor.ts +90 -6
- package/src/task/index.ts +62 -37
- package/src/thinking.ts +21 -6
- package/src/tool-discovery/tool-index.ts +2 -134
- package/src/tools/ask.ts +157 -21
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/bash.ts +38 -0
- package/src/tools/browser/dead-tab-recovery.ts +78 -0
- package/src/tools/browser/tab-supervisor.ts +282 -36
- package/src/tools/computer.ts +10 -3
- package/src/tools/cron.ts +30 -16
- package/src/tools/eval.ts +1 -2
- package/src/tools/fetch.ts +11 -4
- package/src/tools/index.ts +76 -24
- package/src/tools/output-meta.ts +13 -5
- package/src/tools/read.ts +37 -1
- package/src/tools/resource-gc.ts +2 -2
- package/src/tools/search-tool-bm25.ts +11 -74
- package/src/tools/subagent-render.ts +19 -8
- package/src/tools/subagent.ts +84 -32
- package/src/utils/event-bus.ts +9 -4
- package/src/web/search/index.ts +3 -3
- package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
- package/src/discovery/plugin-dir-roots.ts +0 -28
- package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import { DEFAULT_ULTRAGOAL_OBJECTIVE } from "./goal-mode-request";
|
|
3
3
|
import { resolveGjcSessionForRead, SessionResolutionError } from "./session-resolution";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
validateDeferredMemberReceiptFresh,
|
|
6
|
+
validateReceiptFreshBase,
|
|
7
|
+
validateSupersededFinalAggregateReceipt,
|
|
8
|
+
} from "./ultragoal-receipt-freshness";
|
|
5
9
|
import {
|
|
6
10
|
getUltragoalPaths,
|
|
7
11
|
getUltragoalRunCompletionState,
|
|
@@ -49,10 +53,18 @@ export interface UltragoalAskBlockDiagnostic {
|
|
|
49
53
|
export interface CurrentGoalLike {
|
|
50
54
|
objective: string;
|
|
51
55
|
status?: string;
|
|
56
|
+
provenance?: { source: "ultragoal"; runId: string; goalId: string } | { source: "user" };
|
|
52
57
|
}
|
|
53
58
|
|
|
54
|
-
function objectiveMatches(
|
|
55
|
-
const
|
|
59
|
+
function objectiveMatches(currentGoal: CurrentGoalLike, plan: UltragoalPlan, sessionId?: string | null): boolean {
|
|
60
|
+
const provenance = currentGoal.provenance;
|
|
61
|
+
if (provenance?.source === "ultragoal") {
|
|
62
|
+
return (
|
|
63
|
+
provenance.runId === sessionId &&
|
|
64
|
+
(provenance.goalId === "aggregate" || plan.goals.some(goal => goal.id === provenance.goalId))
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const normalized = currentGoal.objective.trim();
|
|
56
68
|
if (!normalized) return false;
|
|
57
69
|
if (normalized === plan.gjcObjective || normalized === DEFAULT_ULTRAGOAL_OBJECTIVE) return true;
|
|
58
70
|
if (plan.gjcObjectiveAliases?.some(alias => alias === normalized)) return true;
|
|
@@ -156,8 +168,33 @@ function requiredGoals(plan: UltragoalPlan): UltragoalGoal[] {
|
|
|
156
168
|
return plan.goals.filter(goal => goal.status !== "superseded");
|
|
157
169
|
}
|
|
158
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Select the goal whose final-aggregate receipt should represent the run.
|
|
173
|
+
* Prefer a receipt that still validates fresh; several goals can hold
|
|
174
|
+
* final-aggregate receipts once plan growth (e.g. `steer add_subgoal`) stales
|
|
175
|
+
* an earlier one and a later checkpoint re-mints. Fall back to the newest
|
|
176
|
+
* holder (array-last) purely for diagnostics when none validates.
|
|
177
|
+
*/
|
|
178
|
+
function findFinalAggregateReceiptGoal(
|
|
179
|
+
plan: UltragoalPlan,
|
|
180
|
+
ledger: readonly UltragoalLedgerEvent[],
|
|
181
|
+
): UltragoalGoal | null {
|
|
182
|
+
const candidates = [...requiredGoals(plan)]
|
|
183
|
+
.reverse()
|
|
184
|
+
.filter(goal => goal.completionVerification?.receiptKind === "final-aggregate");
|
|
185
|
+
if (candidates.length === 0) return null;
|
|
186
|
+
return (
|
|
187
|
+
candidates.find(
|
|
188
|
+
goal =>
|
|
189
|
+
validateCompletionReceipt({ plan, ledger, goal, receiptKind: "final-aggregate" }).state ===
|
|
190
|
+
"active_verified_complete",
|
|
191
|
+
) ?? candidates[0]!
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
159
195
|
function findReceiptGoal(
|
|
160
196
|
plan: UltragoalPlan,
|
|
197
|
+
ledger: readonly UltragoalLedgerEvent[],
|
|
161
198
|
currentObjective: string,
|
|
162
199
|
): { goal: UltragoalGoal; receiptKind: UltragoalReceiptKind } | null {
|
|
163
200
|
if (
|
|
@@ -165,9 +202,7 @@ function findReceiptGoal(
|
|
|
165
202
|
currentObjective === DEFAULT_ULTRAGOAL_OBJECTIVE ||
|
|
166
203
|
plan.gjcObjectiveAliases?.some(alias => alias === currentObjective)
|
|
167
204
|
) {
|
|
168
|
-
const finalGoal =
|
|
169
|
-
.reverse()
|
|
170
|
-
.find(goal => goal.completionVerification?.receiptKind === "final-aggregate");
|
|
205
|
+
const finalGoal = findFinalAggregateReceiptGoal(plan, ledger);
|
|
171
206
|
return finalGoal ? { goal: finalGoal, receiptKind: "final-aggregate" } : null;
|
|
172
207
|
}
|
|
173
208
|
const storyGoal = plan.goals.find(goal => goal.objective === currentObjective);
|
|
@@ -300,6 +335,28 @@ export function validateCompletionReceipt(input: {
|
|
|
300
335
|
goalId: input.goal.id,
|
|
301
336
|
};
|
|
302
337
|
}
|
|
338
|
+
if (
|
|
339
|
+
priorGoal.completionVerification.validationBatch?.role !== "deferred-member" &&
|
|
340
|
+
priorGoal.completionVerification.receiptKind === "final-aggregate"
|
|
341
|
+
) {
|
|
342
|
+
// A prior goal may hold the run's previous final-aggregate receipt
|
|
343
|
+
// when plan growth staled it and a later checkpoint re-minted the
|
|
344
|
+
// aggregate receipt. Accept it as historical evidence for its own
|
|
345
|
+
// goal instead of demanding an impossible per-goal receipt.
|
|
346
|
+
const supersededDiagnostic = validateSupersededFinalAggregateReceipt({
|
|
347
|
+
ledger: input.ledger,
|
|
348
|
+
goal: priorGoal,
|
|
349
|
+
receipt: priorGoal.completionVerification,
|
|
350
|
+
});
|
|
351
|
+
if (supersededDiagnostic) {
|
|
352
|
+
return {
|
|
353
|
+
state: supersededDiagnostic.state,
|
|
354
|
+
message: `Ultragoal final receipt requires valid historical evidence for ${priorGoal.id}: ${supersededDiagnostic.message}`,
|
|
355
|
+
goalId: input.goal.id,
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
303
360
|
const priorDiagnostic =
|
|
304
361
|
priorGoal.completionVerification.validationBatch?.role === "deferred-member"
|
|
305
362
|
? validateDeferredMemberReceiptFresh({
|
|
@@ -341,6 +398,8 @@ export async function readUltragoalVerificationState(input: {
|
|
|
341
398
|
currentGoal?: CurrentGoalLike | null;
|
|
342
399
|
sessionId?: string | null;
|
|
343
400
|
}): Promise<UltragoalGuardDiagnostic> {
|
|
401
|
+
const currentGoal = input.currentGoal;
|
|
402
|
+
|
|
344
403
|
const currentObjective = input.currentGoal?.objective?.trim() ?? "";
|
|
345
404
|
if (!currentObjective) return { state: "inactive", message: "No current goal objective is active." };
|
|
346
405
|
let plan: UltragoalPlan | null;
|
|
@@ -366,7 +425,7 @@ export async function readUltragoalVerificationState(input: {
|
|
|
366
425
|
}
|
|
367
426
|
return { state: "inactive", message: "No Ultragoal plan exists." };
|
|
368
427
|
}
|
|
369
|
-
if (!objectiveMatches(
|
|
428
|
+
if (!currentGoal || !objectiveMatches(currentGoal, plan, input.sessionId))
|
|
370
429
|
return { state: "unrelated_goal", message: "Current goal is not an active Ultragoal objective." };
|
|
371
430
|
if (plan.goals.some(goal => goal.status === "review_blocked")) {
|
|
372
431
|
return {
|
|
@@ -381,7 +440,21 @@ export async function readUltragoalVerificationState(input: {
|
|
|
381
440
|
message: "Ultragoal has blocked or failed goals; record blockers or rerun verification.",
|
|
382
441
|
};
|
|
383
442
|
}
|
|
384
|
-
const
|
|
443
|
+
const provenance = currentGoal.provenance;
|
|
444
|
+
|
|
445
|
+
const receiptTarget =
|
|
446
|
+
provenance?.source === "ultragoal"
|
|
447
|
+
? provenance.goalId === "aggregate" || plan.gjcGoalMode === "aggregate"
|
|
448
|
+
? (() => {
|
|
449
|
+
const goal = findFinalAggregateReceiptGoal(plan, ledger);
|
|
450
|
+
return goal ? { goal, receiptKind: "final-aggregate" as const } : null;
|
|
451
|
+
})()
|
|
452
|
+
: (() => {
|
|
453
|
+
const goal = plan.goals.find(item => item.id === provenance.goalId);
|
|
454
|
+
return goal ? { goal, receiptKind: "per-goal" as const } : null;
|
|
455
|
+
})()
|
|
456
|
+
: findReceiptGoal(plan, ledger, currentObjective);
|
|
457
|
+
|
|
385
458
|
if (!receiptTarget) {
|
|
386
459
|
// When earlier required goals are already complete but later ones remain, name the
|
|
387
460
|
// specific blocking goals (a final-aggregate receipt cannot exist yet anyway). Only
|
|
@@ -623,9 +696,7 @@ export async function isUltragoalAskBlocked(
|
|
|
623
696
|
});
|
|
624
697
|
}
|
|
625
698
|
|
|
626
|
-
const finalReceiptGoal =
|
|
627
|
-
.reverse()
|
|
628
|
-
.find(goal => goal.completionVerification?.receiptKind === "final-aggregate");
|
|
699
|
+
const finalReceiptGoal = findFinalAggregateReceiptGoal(plan, ledger);
|
|
629
700
|
if (!finalReceiptGoal) {
|
|
630
701
|
return activeAskDiagnostic({
|
|
631
702
|
reason: "Ultragoal aggregate completion is missing a final aggregate receipt.",
|
|
@@ -886,7 +957,7 @@ export async function assertUltragoalDropAllowed(input: {
|
|
|
886
957
|
if (!input.currentGoal) return;
|
|
887
958
|
if (input.currentGoal.status !== "active") return;
|
|
888
959
|
// Unrelated active goal: not this aggregate run.
|
|
889
|
-
if (!objectiveMatches(input.currentGoal
|
|
960
|
+
if (!objectiveMatches(input.currentGoal, plan, sessionId)) return;
|
|
890
961
|
// All required stories complete: a legitimate reset, not a give-up.
|
|
891
962
|
if (getUltragoalRunCompletionState(plan).allComplete) return;
|
|
892
963
|
const nudge = await consumeUltragoalNudge({
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import * as crypto from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
appendLedger,
|
|
4
|
+
collectPipelineBlockerFootprints,
|
|
5
|
+
currentUltragoalSessionId,
|
|
6
|
+
getUltragoalPaths,
|
|
7
|
+
handleIdsFromValue,
|
|
8
|
+
hashPipelineMetadata,
|
|
9
|
+
isSubstantiveEvidence,
|
|
10
|
+
type JsonObject,
|
|
11
|
+
nonEmptyString,
|
|
12
|
+
openPipelineOverlap,
|
|
13
|
+
pipelinePeer,
|
|
14
|
+
readUltragoalLedger,
|
|
15
|
+
readUltragoalPlan,
|
|
16
|
+
requireCoveredHandles,
|
|
17
|
+
requireFreshPipelineMetadata,
|
|
18
|
+
requireJsonObjectOrArrayValue,
|
|
19
|
+
requireJsonObjectValue,
|
|
20
|
+
requireNonEmptyPipelineTargets,
|
|
21
|
+
resultHandleIds,
|
|
22
|
+
stringArray,
|
|
23
|
+
targetsAreDisjoint,
|
|
24
|
+
targetsOverlap,
|
|
25
|
+
type UltragoalGoal,
|
|
26
|
+
type UltragoalLedgerEvent,
|
|
27
|
+
type UltragoalPipelineLedgerEventName,
|
|
28
|
+
type UltragoalPipelineOverlapReceipt,
|
|
29
|
+
type UltragoalPipelineOverlapState,
|
|
30
|
+
type UltragoalPlan,
|
|
31
|
+
validateValidationBatchPipelineExclusion,
|
|
32
|
+
writePlan,
|
|
33
|
+
} from "./ultragoal-runtime";
|
|
34
|
+
|
|
35
|
+
export function requirePipelineStartable(plan: UltragoalPlan, prior: UltragoalGoal, next: UltragoalGoal): void {
|
|
36
|
+
if (plan.gjcGoalMode !== "aggregate")
|
|
37
|
+
throw new Error("pipeline overlap is supported only for aggregate ultragoal mode");
|
|
38
|
+
if (openPipelineOverlap(plan))
|
|
39
|
+
throw new Error("Cannot start pipeline overlap because another overlap is already open");
|
|
40
|
+
if (prior.status !== "active")
|
|
41
|
+
throw new Error(`Prior goal ${prior.id} must be active before pipeline overlap starts`);
|
|
42
|
+
if (next.status !== "pending" && next.status !== "failed")
|
|
43
|
+
throw new Error(`Next goal ${next.id} must be pending or retryable failed`);
|
|
44
|
+
validateValidationBatchPipelineExclusion(prior);
|
|
45
|
+
validateValidationBatchPipelineExclusion(next);
|
|
46
|
+
if (prior.validationBatch || next.validationBatch)
|
|
47
|
+
throw new Error("pipeline overlap cannot start for validation batch goals");
|
|
48
|
+
const priorMetadata = requireFreshPipelineMetadata(prior);
|
|
49
|
+
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
50
|
+
if (!priorMetadata.eligible || !nextMetadata.eligible)
|
|
51
|
+
throw new Error("pipeline overlap requires eligible original-plan metadata on both goals");
|
|
52
|
+
if (!priorMetadata.independentOf.includes(next.id) || !nextMetadata.independentOf.includes(prior.id)) {
|
|
53
|
+
throw new Error("pipeline overlap requires symmetric original independence");
|
|
54
|
+
}
|
|
55
|
+
if (!targetsAreDisjoint(priorMetadata.targets, nextMetadata.targets))
|
|
56
|
+
throw new Error("pipeline overlap requires disjoint files and surfaces");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pipelineEventRefs(prior: UltragoalGoal, next: UltragoalGoal): JsonObject {
|
|
60
|
+
return {
|
|
61
|
+
priorMetadataHash: prior.pipelineMetadata?.metadataHash,
|
|
62
|
+
nextMetadataHash: next.pipelineMetadata?.metadataHash,
|
|
63
|
+
priorTargets: prior.pipelineMetadata?.targets,
|
|
64
|
+
nextTargets: next.pipelineMetadata?.targets,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function pipelineReceipt(
|
|
69
|
+
cwd: string,
|
|
70
|
+
event: string,
|
|
71
|
+
overlapId: string,
|
|
72
|
+
prior: UltragoalGoal,
|
|
73
|
+
next: UltragoalGoal,
|
|
74
|
+
): UltragoalPipelineOverlapReceipt {
|
|
75
|
+
const paths = getUltragoalPaths(cwd, currentUltragoalSessionId(cwd));
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
event,
|
|
79
|
+
overlap_id: overlapId,
|
|
80
|
+
prior_goal_id: prior.id,
|
|
81
|
+
next_goal_id: next.id,
|
|
82
|
+
status: next.pipelineMetadata?.overlap,
|
|
83
|
+
next_goal_status: next.status,
|
|
84
|
+
goals_path: paths.goalsPath,
|
|
85
|
+
ledger_path: paths.ledgerPath,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function startUltragoalPipelineOverlap(input: {
|
|
90
|
+
cwd: string;
|
|
91
|
+
priorGoalId: string;
|
|
92
|
+
nextGoalId: string;
|
|
93
|
+
reviewHandles: JsonObject | JsonObject[];
|
|
94
|
+
qaHandles: JsonObject | JsonObject[];
|
|
95
|
+
implementationHandle: JsonObject;
|
|
96
|
+
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
97
|
+
const plan = await readUltragoalPlan(input.cwd);
|
|
98
|
+
if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
|
|
99
|
+
const prior = plan.goals.find(goal => goal.id === input.priorGoalId);
|
|
100
|
+
const next = plan.goals.find(goal => goal.id === input.nextGoalId);
|
|
101
|
+
if (!prior || !next) throw new Error("start-pipeline-overlap requires existing prior and next goal ids");
|
|
102
|
+
const reviewHandles = requireJsonObjectOrArrayValue(input.reviewHandles, "review handles");
|
|
103
|
+
const qaHandles = requireJsonObjectOrArrayValue(input.qaHandles, "QA handles");
|
|
104
|
+
requireJsonObjectValue(input.implementationHandle, "implementation handle");
|
|
105
|
+
requirePipelineStartable(plan, prior, next);
|
|
106
|
+
const now = new Date().toISOString();
|
|
107
|
+
const overlapId = `pipeline-${crypto.randomUUID()}`;
|
|
108
|
+
const priorMetadata = requireFreshPipelineMetadata(prior);
|
|
109
|
+
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
110
|
+
const priorOpenMetadata = {
|
|
111
|
+
...priorMetadata,
|
|
112
|
+
overlap: "open" as const,
|
|
113
|
+
overlapId,
|
|
114
|
+
priorGoalId: prior.id,
|
|
115
|
+
nextGoalId: next.id,
|
|
116
|
+
};
|
|
117
|
+
const nextOpenMetadata = {
|
|
118
|
+
...nextMetadata,
|
|
119
|
+
overlap: "open" as const,
|
|
120
|
+
overlapId,
|
|
121
|
+
priorGoalId: prior.id,
|
|
122
|
+
nextGoalId: next.id,
|
|
123
|
+
};
|
|
124
|
+
prior.pipelineMetadata = { ...priorOpenMetadata, metadataHash: hashPipelineMetadata(priorOpenMetadata) };
|
|
125
|
+
next.pipelineMetadata = { ...nextOpenMetadata, metadataHash: hashPipelineMetadata(nextOpenMetadata) };
|
|
126
|
+
prior.updatedAt = now;
|
|
127
|
+
next.updatedAt = now;
|
|
128
|
+
next.status = "active";
|
|
129
|
+
next.startedAt = next.startedAt ?? now;
|
|
130
|
+
plan.updatedAt = now;
|
|
131
|
+
await writePlan(input.cwd, plan);
|
|
132
|
+
const refs = pipelineEventRefs(prior, next);
|
|
133
|
+
const expectedReviewHandleIds = handleIdsFromValue(reviewHandles, "review");
|
|
134
|
+
const expectedQaHandleIds = handleIdsFromValue(qaHandles, "QA");
|
|
135
|
+
await appendLedger(input.cwd, {
|
|
136
|
+
event: "pipeline_overlap_started",
|
|
137
|
+
eventId: crypto.randomUUID(),
|
|
138
|
+
timestamp: now,
|
|
139
|
+
schemaVersion: 1,
|
|
140
|
+
overlapId,
|
|
141
|
+
priorGoalId: prior.id,
|
|
142
|
+
nextGoalId: next.id,
|
|
143
|
+
reviewHandles,
|
|
144
|
+
reviewHandleIds: expectedReviewHandleIds,
|
|
145
|
+
qaHandles,
|
|
146
|
+
qaHandleIds: expectedQaHandleIds,
|
|
147
|
+
implementationHandle: input.implementationHandle,
|
|
148
|
+
...refs,
|
|
149
|
+
});
|
|
150
|
+
await appendLedger(input.cwd, { event: "goal_started", goalId: next.id, pipelineOverlapId: overlapId });
|
|
151
|
+
return pipelineReceipt(input.cwd, "pipeline_overlap_started", overlapId, prior, next);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function resultStatus(value: JsonObject, fieldName: string): string {
|
|
155
|
+
const status = nonEmptyString(value.status) ?? nonEmptyString(value.verdict) ?? nonEmptyString(value.result);
|
|
156
|
+
if (!status) throw new Error(`${fieldName} requires status, verdict, or result`);
|
|
157
|
+
return status.toLowerCase();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function requireCleanPipelineResult(result: JsonObject, expectedHandleIds: readonly string[], fieldName: string): void {
|
|
161
|
+
const status = resultStatus(result, fieldName);
|
|
162
|
+
if (!["passed", "pass", "approved", "clear"].includes(status)) throw new Error(`${fieldName} did not pass`);
|
|
163
|
+
const evidence = nonEmptyString(result.evidence);
|
|
164
|
+
if (!evidence || !isSubstantiveEvidence(evidence)) throw new Error(`${fieldName} requires substantive evidence`);
|
|
165
|
+
requireCoveredHandles(expectedHandleIds, resultHandleIds(result, fieldName), fieldName);
|
|
166
|
+
if (collectPipelineBlockerFootprints(result, fieldName).length > 0)
|
|
167
|
+
throw new Error(`${fieldName} cannot clean-join with blockers`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function pipelineStartEventHandleIds(
|
|
171
|
+
ledger: UltragoalLedgerEvent[],
|
|
172
|
+
overlapId: string,
|
|
173
|
+
): { review: string[]; qa: string[] } {
|
|
174
|
+
const event = ledger.find(row => row.event === "pipeline_overlap_started" && row.overlapId === overlapId) as
|
|
175
|
+
| JsonObject
|
|
176
|
+
| undefined;
|
|
177
|
+
if (!event) throw new Error(`No pipeline_overlap_started event found for ${overlapId}`);
|
|
178
|
+
const review =
|
|
179
|
+
stringArray(event.reviewHandleIds) ??
|
|
180
|
+
handleIdsFromValue(requireJsonObjectOrArrayValue(event.reviewHandles, "review handles"), "review");
|
|
181
|
+
const qa =
|
|
182
|
+
stringArray(event.qaHandleIds) ??
|
|
183
|
+
handleIdsFromValue(requireJsonObjectOrArrayValue(event.qaHandles, "QA handles"), "QA");
|
|
184
|
+
return { review, qa };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function joinUltragoalPipelineOverlap(input: {
|
|
188
|
+
cwd: string;
|
|
189
|
+
overlapId: string;
|
|
190
|
+
reviewResult: JsonObject;
|
|
191
|
+
qaResult: JsonObject;
|
|
192
|
+
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
193
|
+
const plan = await readUltragoalPlan(input.cwd);
|
|
194
|
+
if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
|
|
195
|
+
const overlap = openPipelineOverlap(plan);
|
|
196
|
+
if (!overlap || overlap.overlapId !== input.overlapId)
|
|
197
|
+
throw new Error(`No open pipeline overlap found for ${input.overlapId}`);
|
|
198
|
+
const { prior, next, overlapId } = overlap;
|
|
199
|
+
const nextMetadata = requireFreshPipelineMetadata(next);
|
|
200
|
+
const ledger = await readUltragoalLedger(input.cwd);
|
|
201
|
+
const expectedHandles = pipelineStartEventHandleIds(ledger, overlapId);
|
|
202
|
+
const reviewBlockers = collectPipelineBlockerFootprints(input.reviewResult, "review result");
|
|
203
|
+
const qaBlockers = collectPipelineBlockerFootprints(input.qaResult, "QA result");
|
|
204
|
+
const blockerFootprints = [...reviewBlockers, ...qaBlockers];
|
|
205
|
+
let state: UltragoalPipelineOverlapState;
|
|
206
|
+
let event: UltragoalPipelineLedgerEventName;
|
|
207
|
+
if (blockerFootprints.length === 0) {
|
|
208
|
+
try {
|
|
209
|
+
requireCleanPipelineResult(input.reviewResult, expectedHandles.review, "review result");
|
|
210
|
+
requireCleanPipelineResult(input.qaResult, expectedHandles.qa, "QA result");
|
|
211
|
+
state = "joined_clean";
|
|
212
|
+
event = "pipeline_overlap_joined";
|
|
213
|
+
} catch {
|
|
214
|
+
state = "quarantine_required";
|
|
215
|
+
event = "pipeline_overlap_quarantined";
|
|
216
|
+
}
|
|
217
|
+
} else if (blockerFootprints.every(footprint => !targetsOverlap(footprint, nextMetadata.targets))) {
|
|
218
|
+
state = "blocked_disjoint_continue";
|
|
219
|
+
event = "pipeline_overlap_joined";
|
|
220
|
+
} else {
|
|
221
|
+
state = "quarantine_required";
|
|
222
|
+
event = "pipeline_overlap_quarantined";
|
|
223
|
+
}
|
|
224
|
+
const now = new Date().toISOString();
|
|
225
|
+
for (const goal of [prior, next]) {
|
|
226
|
+
const metadata = requireFreshPipelineMetadata(goal);
|
|
227
|
+
const joinedMetadata = { ...metadata, overlap: state, blockerFootprints };
|
|
228
|
+
goal.pipelineMetadata = { ...joinedMetadata, metadataHash: hashPipelineMetadata(joinedMetadata) };
|
|
229
|
+
goal.updatedAt = now;
|
|
230
|
+
}
|
|
231
|
+
if (state === "quarantine_required") next.status = "blocked";
|
|
232
|
+
plan.updatedAt = now;
|
|
233
|
+
await writePlan(input.cwd, plan);
|
|
234
|
+
await appendLedger(input.cwd, {
|
|
235
|
+
event,
|
|
236
|
+
eventId: crypto.randomUUID(),
|
|
237
|
+
timestamp: now,
|
|
238
|
+
schemaVersion: 1,
|
|
239
|
+
overlapId,
|
|
240
|
+
priorGoalId: prior.id,
|
|
241
|
+
nextGoalId: next.id,
|
|
242
|
+
status: state,
|
|
243
|
+
reviewResult: input.reviewResult,
|
|
244
|
+
qaResult: input.qaResult,
|
|
245
|
+
blockerFootprints,
|
|
246
|
+
...pipelineEventRefs(prior, next),
|
|
247
|
+
});
|
|
248
|
+
return pipelineReceipt(input.cwd, event, overlapId, prior, next);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function rebaselineUltragoalPipelineOverlap(input: {
|
|
252
|
+
cwd: string;
|
|
253
|
+
overlapId: string;
|
|
254
|
+
goalId: string;
|
|
255
|
+
evidence: string;
|
|
256
|
+
targetState: JsonObject;
|
|
257
|
+
}): Promise<UltragoalPipelineOverlapReceipt> {
|
|
258
|
+
const plan = await readUltragoalPlan(input.cwd);
|
|
259
|
+
if (!plan) throw new Error("No ultragoal plan found. Run `gjc ultragoal create-goals --brief ...` first.");
|
|
260
|
+
const goal = plan.goals.find(item => item.id === input.goalId);
|
|
261
|
+
if (!goal) throw new Error(`No ultragoal goal found for ${input.goalId}.`);
|
|
262
|
+
const evidence = input.evidence.trim();
|
|
263
|
+
if (!isSubstantiveEvidence(evidence)) throw new Error("rebaseline-pipeline-overlap requires substantive evidence");
|
|
264
|
+
const targetState = requireNonEmptyPipelineTargets(input.targetState, "target state");
|
|
265
|
+
const metadata = requireFreshPipelineMetadata(goal);
|
|
266
|
+
if (metadata.overlap !== "quarantine_required" || metadata.overlapId !== input.overlapId) {
|
|
267
|
+
throw new Error(`Goal ${goal.id} is not quarantined for overlap ${input.overlapId}`);
|
|
268
|
+
}
|
|
269
|
+
for (const footprint of metadata.blockerFootprints ?? []) {
|
|
270
|
+
if (targetsOverlap(footprint, targetState))
|
|
271
|
+
throw new Error("rebaseline-pipeline-overlap target state overlaps unresolved blocker footprints");
|
|
272
|
+
}
|
|
273
|
+
const now = new Date().toISOString();
|
|
274
|
+
const rebaselinedMetadata = { ...metadata, overlap: "rebaseline_complete" as const, targets: targetState };
|
|
275
|
+
goal.pipelineMetadata = { ...rebaselinedMetadata, metadataHash: hashPipelineMetadata(rebaselinedMetadata) };
|
|
276
|
+
goal.status = "active";
|
|
277
|
+
goal.evidence = evidence;
|
|
278
|
+
goal.updatedAt = now;
|
|
279
|
+
plan.updatedAt = now;
|
|
280
|
+
await writePlan(input.cwd, plan);
|
|
281
|
+
const peer = pipelinePeer(plan, metadata);
|
|
282
|
+
await appendLedger(input.cwd, {
|
|
283
|
+
event: "pipeline_overlap_rebaselined",
|
|
284
|
+
eventId: crypto.randomUUID(),
|
|
285
|
+
timestamp: now,
|
|
286
|
+
schemaVersion: 1,
|
|
287
|
+
overlapId: input.overlapId,
|
|
288
|
+
priorGoalId: metadata.priorGoalId ?? peer?.id ?? "",
|
|
289
|
+
nextGoalId: metadata.nextGoalId ?? goal.id,
|
|
290
|
+
goalId: goal.id,
|
|
291
|
+
evidence,
|
|
292
|
+
targetState,
|
|
293
|
+
metadataHash: goal.pipelineMetadata.metadataHash,
|
|
294
|
+
});
|
|
295
|
+
const paths = getUltragoalPaths(input.cwd, currentUltragoalSessionId(input.cwd));
|
|
296
|
+
return {
|
|
297
|
+
ok: true,
|
|
298
|
+
event: "pipeline_overlap_rebaselined",
|
|
299
|
+
overlap_id: input.overlapId,
|
|
300
|
+
prior_goal_id: metadata.priorGoalId ?? peer?.id ?? "",
|
|
301
|
+
goal_id: goal.id,
|
|
302
|
+
status: goal.pipelineMetadata.overlap,
|
|
303
|
+
goals_path: paths.goalsPath,
|
|
304
|
+
ledger_path: paths.ledgerPath,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
@@ -244,6 +244,93 @@ export function validateReceiptFreshBase(input: {
|
|
|
244
244
|
return null;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
/**
|
|
248
|
+
* Validate a receipt whose plan-generation freshness was legitimately staled
|
|
249
|
+
* by later plan growth as a historical attestation for its own goal:
|
|
250
|
+
* internally consistent, anchored to a ledger checkpoint event that carries a
|
|
251
|
+
* byte-identical receipt, quality gate untampered, and the goal row untouched
|
|
252
|
+
* since verification. Plan-generation freshness against the CURRENT plan is
|
|
253
|
+
* intentionally not required; that is exactly what later plan growth
|
|
254
|
+
* invalidates by design. Returns null when the receipt stands as valid
|
|
255
|
+
* historical evidence.
|
|
256
|
+
*/
|
|
257
|
+
function validateLedgerAnchoredHistoricalReceipt(input: {
|
|
258
|
+
ledger: readonly UltragoalLedgerEvent[];
|
|
259
|
+
goal: UltragoalGoal;
|
|
260
|
+
receipt: UltragoalCompletionVerification;
|
|
261
|
+
label: string;
|
|
262
|
+
}): UltragoalReceiptFreshnessDiagnostic | null {
|
|
263
|
+
if (
|
|
264
|
+
input.receipt.schemaVersion !== 1 ||
|
|
265
|
+
input.receipt.goalId !== input.goal.id ||
|
|
266
|
+
!input.receipt.planGeneration ||
|
|
267
|
+
!input.receipt.checkpointLedgerEventId ||
|
|
268
|
+
hashStructuredValue(input.receipt.basis) !== input.receipt.planGeneration
|
|
269
|
+
) {
|
|
270
|
+
return {
|
|
271
|
+
state: "active_stale_receipt",
|
|
272
|
+
message: `Ultragoal ${input.goal.id} ${input.label} is malformed.`,
|
|
273
|
+
goalId: input.goal.id,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const event = findLedgerReceiptEvent(input.ledger, input.receipt);
|
|
277
|
+
if (!event)
|
|
278
|
+
return {
|
|
279
|
+
state: "active_stale_receipt",
|
|
280
|
+
message: `Ultragoal ${input.goal.id} ${input.label} ledger event is missing.`,
|
|
281
|
+
goalId: input.goal.id,
|
|
282
|
+
};
|
|
283
|
+
// Bind the goals.json receipt to the FULL receipt recorded on the ledger
|
|
284
|
+
// event. Field-selective matching would accept a coordinated edit of the
|
|
285
|
+
// goals-row basis/generation paired with only the event generation.
|
|
286
|
+
const eventReceipt = event.completionVerification as UltragoalCompletionVerification | undefined;
|
|
287
|
+
if (!eventReceipt || hashStructuredValue(eventReceipt) !== hashStructuredValue(input.receipt))
|
|
288
|
+
return {
|
|
289
|
+
state: "active_stale_receipt",
|
|
290
|
+
message: `Ultragoal ${input.goal.id} ${input.label} does not match its ledger event receipt.`,
|
|
291
|
+
goalId: input.goal.id,
|
|
292
|
+
};
|
|
293
|
+
if (hashStructuredValue(event.qualityGateJson) !== input.receipt.qualityGateHash)
|
|
294
|
+
return {
|
|
295
|
+
state: "active_dirty_quality_gate",
|
|
296
|
+
message: `Ultragoal ${input.goal.id} ${input.label} quality-gate hash does not match ledger.`,
|
|
297
|
+
goalId: input.goal.id,
|
|
298
|
+
};
|
|
299
|
+
if (input.goal.updatedAt !== input.receipt.verifiedAt)
|
|
300
|
+
return {
|
|
301
|
+
state: "active_stale_receipt",
|
|
302
|
+
message: `Ultragoal ${input.goal.id} changed after its ${input.label} was verified.`,
|
|
303
|
+
goalId: input.goal.id,
|
|
304
|
+
};
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Validate a final-aggregate receipt that was legitimately superseded — its
|
|
310
|
+
* aggregate claim staled by later plan growth (e.g. `steer add_subgoal`
|
|
311
|
+
* appending goals after a terminal run) — as historical evidence for its own
|
|
312
|
+
* goal. Returns null when the receipt stands.
|
|
313
|
+
*/
|
|
314
|
+
export function validateSupersededFinalAggregateReceipt(input: {
|
|
315
|
+
ledger: readonly UltragoalLedgerEvent[];
|
|
316
|
+
goal: UltragoalGoal;
|
|
317
|
+
receipt: UltragoalCompletionVerification;
|
|
318
|
+
}): UltragoalReceiptFreshnessDiagnostic | null {
|
|
319
|
+
if (input.receipt.receiptKind !== "final-aggregate") {
|
|
320
|
+
return {
|
|
321
|
+
state: "active_stale_receipt",
|
|
322
|
+
message: `Ultragoal ${input.goal.id} superseded final-aggregate receipt is malformed.`,
|
|
323
|
+
goalId: input.goal.id,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return validateLedgerAnchoredHistoricalReceipt({
|
|
327
|
+
ledger: input.ledger,
|
|
328
|
+
goal: input.goal,
|
|
329
|
+
receipt: input.receipt,
|
|
330
|
+
label: "superseded final-aggregate receipt",
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
247
334
|
export function findFreshBatchCloseReceipt(input: {
|
|
248
335
|
plan: UltragoalPlan;
|
|
249
336
|
ledger: readonly UltragoalLedgerEvent[];
|
|
@@ -267,7 +354,21 @@ export function findFreshBatchCloseReceipt(input: {
|
|
|
267
354
|
receipt: finalReceipt,
|
|
268
355
|
receiptKind: finalReceipt.receiptKind,
|
|
269
356
|
});
|
|
270
|
-
|
|
357
|
+
if (!diagnostic) return finalReceipt;
|
|
358
|
+
// A batch close staled only by later plan growth (e.g. `steer add_subgoal`
|
|
359
|
+
// after the batch completed) still proves the batch was validly closed:
|
|
360
|
+
// the member receipts are freshness-checked separately, the close is
|
|
361
|
+
// anchored to its ledger event, and the final goal's row is untouched.
|
|
362
|
+
// Without this, completing a goal appended after a closed batch can never
|
|
363
|
+
// validate, because the old close can never be fresh against the grown
|
|
364
|
+
// plan and identical-evidence replays of fresh receipts are no-ops.
|
|
365
|
+
const historicalDiagnostic = validateLedgerAnchoredHistoricalReceipt({
|
|
366
|
+
ledger: input.ledger,
|
|
367
|
+
goal: finalGoal,
|
|
368
|
+
receipt: finalReceipt,
|
|
369
|
+
label: "superseded batch-close receipt",
|
|
370
|
+
});
|
|
371
|
+
return historicalDiagnostic ? null : finalReceipt;
|
|
271
372
|
}
|
|
272
373
|
|
|
273
374
|
export function validateDeferredMemberReceiptFresh(input: {
|