@gajae-code/coding-agent 0.11.1 → 0.11.3
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 +64 -0
- package/dist/types/cli/config-cli.d.ts +11 -1
- package/dist/types/cli/launch-disposition.d.ts +23 -0
- 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 +141 -11
- 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-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 +4 -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/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 +19 -17
- package/dist/types/modes/jobs-observer.d.ts +2 -0
- package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -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 +20 -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 +20 -13
- 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/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/html-format.d.ts +3 -2
- package/dist/types/sdk/bus/index.d.ts +29 -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 +46 -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/sdk/session.d.ts +2 -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 +5 -0
- package/dist/types/tools/search-tool-bm25.d.ts +3 -4
- package/dist/types/tools/subagent.d.ts +1 -0
- package/dist/types/utils/event-bus.d.ts +1 -1
- package/dist/types/utils/file-mentions.d.ts +3 -3
- package/dist/types/web/search/index.d.ts +3 -1
- package/package.json +21 -9
- package/scripts/build-sdk-package-smoke.ts +34 -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 +49 -6
- package/src/cli/config-cli.ts +75 -2
- package/src/cli/launch-disposition.ts +42 -0
- 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 +214 -12
- 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 +327 -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 +29 -5
- package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +84 -1311
- 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 +127 -30
- 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/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +24 -1
- 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 +289 -1150
- package/src/modes/jobs-observer.ts +3 -0
- package/src/modes/prompt-action-autocomplete.ts +7 -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 +71 -16
- 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 +45 -18
- 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 +1 -1
- package/src/sdk/broker/ensure.ts +3 -1
- 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 +10 -2
- 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/html-format.ts +20 -3
- package/src/sdk/bus/index.ts +782 -94
- package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
- 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 +1389 -237
- 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 +44 -2
- 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 +462 -480
- package/src/session/agent-session.ts +1700 -583
- 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 +36 -21
- 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 +19 -7
- package/src/tools/read.ts +219 -64
- package/src/tools/resource-gc.ts +2 -2
- package/src/tools/search-tool-bm25.ts +11 -74
- package/src/tools/subagent.ts +43 -2
- package/src/utils/event-bus.ts +9 -4
- package/src/utils/file-mentions.ts +4 -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,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import * as fs from "node:fs/promises";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import type { WorkflowHudSummary } from "../skill-state/active-state";
|
|
@@ -27,7 +28,13 @@ import {
|
|
|
27
28
|
} from "../skill-state/workflow-state-contract";
|
|
28
29
|
import { renderCliWriteReceipt } from "./cli-write-receipt";
|
|
29
30
|
import { applyAmbiguityFloorToEnvelope } from "./deep-interview-ambiguity";
|
|
30
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
assertDeepInterviewIntentManifest,
|
|
33
|
+
assertDeepInterviewIntentReview,
|
|
34
|
+
type DeepInterviewIntentManifest,
|
|
35
|
+
mergeDeepInterviewEnvelope,
|
|
36
|
+
normalizeDeepInterviewEnvelope,
|
|
37
|
+
} from "./deep-interview-state";
|
|
31
38
|
import { activeSnapshotPath, auditPath, modeStatePath, sessionStateDir } from "./session-layout";
|
|
32
39
|
import {
|
|
33
40
|
resolveGjcSessionForRead,
|
|
@@ -61,8 +68,10 @@ import {
|
|
|
61
68
|
softDelete,
|
|
62
69
|
updateWorkflowTransactionJournal,
|
|
63
70
|
type WorkflowEnvelopeIntegrityMismatch,
|
|
71
|
+
withWorkflowStateLock,
|
|
64
72
|
writeGuardedWorkflowEnvelopeAtomic,
|
|
65
73
|
} from "./state-writer";
|
|
74
|
+
import { assertSafePathComponent, CommandError, flagValue, hasFlag, isPlainObject } from "./workflow-cli-common";
|
|
66
75
|
import { getSkillManifest, isKnownWorkflowState, isValidTransition, typedArgsFor } from "./workflow-manifest";
|
|
67
76
|
|
|
68
77
|
/**
|
|
@@ -80,30 +89,15 @@ export interface StateCommandResult {
|
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
const SKILL_ACTIVE_STATE_FILE = "skill-active-state.json";
|
|
83
|
-
const TERMINAL_CLEAR_PHASES = new Set(["complete", "completed", "cancelled", "canceled", "failed"]);
|
|
84
|
-
const PATH_COMPONENT_RE = /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,63}$/;
|
|
85
92
|
const KNOWN_MODES: readonly string[] = CANONICAL_GJC_WORKFLOW_SKILLS;
|
|
86
93
|
|
|
87
|
-
class StateCommandError extends
|
|
88
|
-
constructor(
|
|
89
|
-
|
|
90
|
-
message: string,
|
|
91
|
-
) {
|
|
92
|
-
super(message);
|
|
94
|
+
class StateCommandError extends CommandError {
|
|
95
|
+
constructor(exitStatus: number, message: string) {
|
|
96
|
+
super(exitStatus, message);
|
|
93
97
|
this.name = "StateCommandError";
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
97
|
-
function flagValue(args: readonly string[], flag: string): string | undefined {
|
|
98
|
-
const index = args.indexOf(flag);
|
|
99
|
-
if (index < 0) return undefined;
|
|
100
|
-
return args[index + 1];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function hasFlag(args: readonly string[], flag: string): boolean {
|
|
104
|
-
return args.includes(flag);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
101
|
const GRAPH_FORMATS = new Set(["ascii", "mermaid", "dot"]);
|
|
108
102
|
const FLAGS_WITH_VALUES = new Set([
|
|
109
103
|
"--input",
|
|
@@ -237,11 +231,6 @@ function parsePositionalArgs(args: readonly string[]): ParsedInvocation {
|
|
|
237
231
|
return { action: "read" };
|
|
238
232
|
}
|
|
239
233
|
|
|
240
|
-
function assertSafePathComponent(value: string, label: string): void {
|
|
241
|
-
if (!PATH_COMPONENT_RE.test(value) || value.includes("..")) {
|
|
242
|
-
throw new StateCommandError(2, `invalid path component for --${label}: ${value}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
234
|
function isKnownMode(mode: string): mode is CanonicalGjcWorkflowSkill {
|
|
246
235
|
return KNOWN_MODES.includes(mode);
|
|
247
236
|
}
|
|
@@ -391,7 +380,9 @@ async function describeStaleClearState(
|
|
|
391
380
|
existing: Record<string, unknown>,
|
|
392
381
|
): Promise<string | undefined> {
|
|
393
382
|
const phase = typeof existing.current_phase === "string" ? existing.current_phase.trim() : undefined;
|
|
394
|
-
if (phase &&
|
|
383
|
+
if (phase && getSkillManifest(mode).stopReleasingPhases.includes(phase) && phase !== "inactive") {
|
|
384
|
+
return `mode-state is already terminal (${phase})`;
|
|
385
|
+
}
|
|
395
386
|
const activePhase = await readActivePhaseForSkill(cwd, sessionId, mode);
|
|
396
387
|
if (activePhase && phase && activePhase !== phase) {
|
|
397
388
|
return `active-state phase ${activePhase} differs from mode-state phase ${phase}`;
|
|
@@ -504,22 +495,11 @@ function phaseFromActiveValue(value: unknown): string | undefined {
|
|
|
504
495
|
return phase || undefined;
|
|
505
496
|
}
|
|
506
497
|
|
|
507
|
-
const RALPLAN_CANONICAL_PHASE_OVERRIDES = new Set([
|
|
508
|
-
"final",
|
|
509
|
-
"handoff",
|
|
510
|
-
"complete",
|
|
511
|
-
"completed",
|
|
512
|
-
"failed",
|
|
513
|
-
"cancelled",
|
|
514
|
-
"canceled",
|
|
515
|
-
"inactive",
|
|
516
|
-
]);
|
|
517
|
-
|
|
518
498
|
function modeStatePhase(value: unknown): string | undefined {
|
|
519
499
|
if (!isPlainObject(value) || typeof value.current_phase !== "string") return undefined;
|
|
520
500
|
const phase = value.current_phase.trim();
|
|
521
501
|
if (!phase) return undefined;
|
|
522
|
-
if (value.active === false && !
|
|
502
|
+
if (value.active === false && !getSkillManifest("ralplan").canonicalOverrides.includes(phase)) return undefined;
|
|
523
503
|
return phase;
|
|
524
504
|
}
|
|
525
505
|
|
|
@@ -821,6 +801,7 @@ async function writeJsonAtomic(
|
|
|
821
801
|
fromPhase?: string;
|
|
822
802
|
toPhase?: string;
|
|
823
803
|
owner?: WorkflowStateMutationOwner;
|
|
804
|
+
lockHeld?: boolean;
|
|
824
805
|
},
|
|
825
806
|
): Promise<{ warning?: string; stamped: Record<string, unknown>; revision: number }> {
|
|
826
807
|
const warning = options?.skill
|
|
@@ -850,6 +831,7 @@ async function writeJsonAtomic(
|
|
|
850
831
|
toPhase: options?.toPhase,
|
|
851
832
|
forced: options?.force ?? false,
|
|
852
833
|
},
|
|
834
|
+
lockHeld: options?.lockHeld ?? false,
|
|
853
835
|
});
|
|
854
836
|
// `writeResult.stamped` and `.revision` are computed inside the writer lock, so they are
|
|
855
837
|
// the envelope/revision this write actually owns. Never post-lock re-read here: a concurrent
|
|
@@ -938,10 +920,6 @@ async function readAuditWindow(
|
|
|
938
920
|
return { entries: selected.reverse(), limit, ...(since ? { since } : {}), truncated: matched > limit };
|
|
939
921
|
}
|
|
940
922
|
|
|
941
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
942
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
923
|
/**
|
|
946
924
|
* Shallow-merge `source` into `target`, with the convention that a `source` key whose value is
|
|
947
925
|
* `null` deletes that key from `target`. Nested objects are replaced wholesale (not deep-merged)
|
|
@@ -1127,11 +1105,21 @@ export async function reconcileWorkflowSkillState(options: {
|
|
|
1127
1105
|
payload: Record<string, unknown>;
|
|
1128
1106
|
sourceRevision?: number;
|
|
1129
1107
|
}): Promise<{ stateFile: string }> {
|
|
1130
|
-
const {
|
|
1131
|
-
const { gjcSessionId: sessionId } = resolveGjcSessionForWrite(cwd, {
|
|
1108
|
+
const { gjcSessionId: sessionId } = resolveGjcSessionForWrite(options.cwd, {
|
|
1132
1109
|
payloadSessionId: options.sessionId,
|
|
1133
1110
|
envSessionId: process.env.GJC_SESSION_ID,
|
|
1134
1111
|
});
|
|
1112
|
+
return withWorkflowStateLock(
|
|
1113
|
+
path.relative(options.cwd, modeStateFile(options.cwd, options.mode, sessionId)),
|
|
1114
|
+
async () => reconcileWorkflowSkillStateUnlocked(options, sessionId),
|
|
1115
|
+
);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
async function reconcileWorkflowSkillStateUnlocked(
|
|
1119
|
+
options: Parameters<typeof reconcileWorkflowSkillState>[0],
|
|
1120
|
+
sessionId: string,
|
|
1121
|
+
): Promise<{ stateFile: string }> {
|
|
1122
|
+
const { cwd, mode, threadId, turnId, active, payload } = options;
|
|
1135
1123
|
const filePath = modeStateFile(cwd, mode, sessionId);
|
|
1136
1124
|
const existingRead = await readExistingStateForMutation(filePath);
|
|
1137
1125
|
const existingPayload = existingRead.kind === "valid" ? existingRead.value : {};
|
|
@@ -1181,9 +1169,10 @@ export async function reconcileWorkflowSkillState(options: {
|
|
|
1181
1169
|
if (!validation.valid) throw new StateCommandError(2, validation.error ?? `invalid ${mode} state envelope`);
|
|
1182
1170
|
|
|
1183
1171
|
if (existingRead.kind === "corrupt") await fs.rm(filePath, { force: true });
|
|
1184
|
-
await writeGuardedWorkflowEnvelopeAtomic(filePath, merged, {
|
|
1172
|
+
const writeResult = await writeGuardedWorkflowEnvelopeAtomic(filePath, merged, {
|
|
1185
1173
|
cwd,
|
|
1186
1174
|
policy: "source",
|
|
1175
|
+
lockHeld: true,
|
|
1187
1176
|
receipt: {
|
|
1188
1177
|
cwd,
|
|
1189
1178
|
skill: mode,
|
|
@@ -1209,8 +1198,7 @@ export async function reconcileWorkflowSkillState(options: {
|
|
|
1209
1198
|
toPhase: trimmedPhase,
|
|
1210
1199
|
},
|
|
1211
1200
|
});
|
|
1212
|
-
const
|
|
1213
|
-
const sourceRevision = options.sourceRevision ?? existingStateRevision(persisted);
|
|
1201
|
+
const sourceRevision = options.sourceRevision ?? writeResult.revision;
|
|
1214
1202
|
|
|
1215
1203
|
// Reconciliation drives the active-state/HUD update directly (not via the
|
|
1216
1204
|
// best-effort syncWorkflowSkillState wrapper) so a failed HUD/active-state write
|
|
@@ -1332,132 +1320,149 @@ async function handleWrite(
|
|
|
1332
1320
|
|
|
1333
1321
|
const filePath = modeStateFile(cwd, mode, sessionId);
|
|
1334
1322
|
const forced = hasFlag(args, "--force");
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1323
|
+
return await withWorkflowStateLock(
|
|
1324
|
+
filePath,
|
|
1325
|
+
async () => {
|
|
1326
|
+
const existingRead = await readExistingStateForMutation(filePath);
|
|
1327
|
+
if (existingRead.kind === "corrupt" && !forced) {
|
|
1328
|
+
throw new StateCommandError(
|
|
1329
|
+
2,
|
|
1330
|
+
`existing state for ${mode} is corrupt or tampered (${existingRead.error}); use --force to overwrite`,
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
const existingPayload = existingRead.kind === "valid" ? existingRead.value : {};
|
|
1334
|
+
const nowIsoStr = nowIso();
|
|
1335
|
+
const mutationId = `${mode}:${nowIsoStr}`;
|
|
1336
|
+
const receipt = buildWorkflowStateReceipt({
|
|
1337
|
+
cwd,
|
|
1338
|
+
skill: mode,
|
|
1339
|
+
owner: "gjc-state-cli",
|
|
1340
|
+
command: `gjc state ${mode} write`,
|
|
1341
|
+
sessionId,
|
|
1342
|
+
nowIso: nowIsoStr,
|
|
1343
|
+
mutationId,
|
|
1344
|
+
});
|
|
1345
|
+
const innerState = (payload.state as Record<string, unknown> | undefined) ?? {};
|
|
1346
|
+
const incomingPhase =
|
|
1347
|
+
typeof payload.current_phase === "string" && payload.current_phase.trim()
|
|
1348
|
+
? payload.current_phase.trim()
|
|
1349
|
+
: typeof payload.phase === "string" && payload.phase.trim()
|
|
1350
|
+
? payload.phase.trim()
|
|
1351
|
+
: typeof innerState.current_phase === "string" && (innerState.current_phase as string).trim()
|
|
1352
|
+
? (innerState.current_phase as string).trim()
|
|
1353
|
+
: undefined;
|
|
1354
|
+
let merged: Record<string, unknown>;
|
|
1355
|
+
if (mode === "deep-interview") {
|
|
1356
|
+
// Deep-interview keeps interview data nested under `state` and merges rounds
|
|
1357
|
+
// losslessly by durable key; never flatten or delete `state` (that drops recorder history).
|
|
1358
|
+
// The deterministic ambiguity floor is applied after the merge so a reported
|
|
1359
|
+
// score written through the CLI can never undercut persisted contradiction evidence.
|
|
1360
|
+
merged = applyAmbiguityFloorToEnvelope(
|
|
1361
|
+
mergeDeepInterviewEnvelope(existingPayload, payload, { replace: hasFlag(args, "--replace") }),
|
|
1362
|
+
).envelope;
|
|
1363
|
+
} else if (hasFlag(args, "--replace")) {
|
|
1364
|
+
merged = { ...payload };
|
|
1365
|
+
} else {
|
|
1366
|
+
merged = mergeWithNullDelete(existingPayload, payload);
|
|
1367
|
+
// Flatten payload.state.* into the top-level envelope so downstream consumers
|
|
1368
|
+
// see a single canonical structure with the receipt at top level.
|
|
1369
|
+
if (payload.state && typeof payload.state === "object" && !Array.isArray(payload.state)) {
|
|
1370
|
+
merged = mergeWithNullDelete(merged, payload.state as Record<string, unknown>);
|
|
1371
|
+
delete merged.state;
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
const preDefaultValidation = validateWorkflowStateEnvelope(mode, merged);
|
|
1375
|
+
if (!preDefaultValidation.valid) {
|
|
1376
|
+
throw new StateCommandError(2, preDefaultValidation.error ?? `invalid ${mode} state envelope`);
|
|
1377
|
+
}
|
|
1378
|
+
merged.skill = mode;
|
|
1379
|
+
if (incomingPhase) {
|
|
1380
|
+
merged.current_phase = incomingPhase;
|
|
1381
|
+
} else if (typeof merged.current_phase !== "string" || !merged.current_phase.trim()) {
|
|
1382
|
+
const retainedPhase =
|
|
1383
|
+
typeof existingPayload.current_phase === "string" ? existingPayload.current_phase.trim() : "";
|
|
1384
|
+
merged.current_phase = retainedPhase || initialPhaseForSkill(mode);
|
|
1385
|
+
} else {
|
|
1386
|
+
merged.current_phase = merged.current_phase.trim();
|
|
1387
|
+
}
|
|
1388
|
+
merged.version = WORKFLOW_STATE_VERSION;
|
|
1389
|
+
if (typeof merged.active !== "boolean") merged.active = true;
|
|
1390
|
+
merged.updated_at = nowIsoStr;
|
|
1391
|
+
merged.receipt = receipt;
|
|
1392
|
+
if (sessionId && typeof merged.session_id !== "string") merged.session_id = sessionId;
|
|
1393
|
+
|
|
1394
|
+
const fromPhase =
|
|
1395
|
+
typeof existingPayload.current_phase === "string" ? existingPayload.current_phase.trim() : undefined;
|
|
1396
|
+
const toPhase = merged.current_phase as string;
|
|
1397
|
+
const manifestStates = new Set(getSkillManifest(mode).states.map(state => state.id));
|
|
1398
|
+
if (!manifestStates.has(toPhase) && !forced) {
|
|
1399
|
+
throw new StateCommandError(2, `unknown ${mode} phase "${toPhase}"; use --force to bypass`);
|
|
1400
|
+
}
|
|
1401
|
+
if (fromPhase && toPhase && isKnownWorkflowState(mode, fromPhase) && isKnownWorkflowState(mode, toPhase)) {
|
|
1402
|
+
if (!isValidTransition(mode, fromPhase, toPhase) && !forced) {
|
|
1403
|
+
throw new StateCommandError(
|
|
1404
|
+
2,
|
|
1405
|
+
`invalid ${mode} phase transition from ${fromPhase} to ${toPhase}; use --force to bypass`,
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1421
1409
|
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1410
|
+
const validation = validateWorkflowStateEnvelope(mode, merged);
|
|
1411
|
+
if (!validation.valid) throw new StateCommandError(2, validation.error ?? `invalid ${mode} state envelope`);
|
|
1412
|
+
|
|
1413
|
+
const {
|
|
1414
|
+
warning: outOfBandWarning,
|
|
1415
|
+
stamped,
|
|
1416
|
+
revision: stampedRevision,
|
|
1417
|
+
} = await writeJsonAtomic(cwd, filePath, merged, "write", {
|
|
1418
|
+
sessionId,
|
|
1419
|
+
skill: mode,
|
|
1420
|
+
mutationId,
|
|
1421
|
+
force: forced,
|
|
1422
|
+
fromPhase,
|
|
1423
|
+
toPhase,
|
|
1424
|
+
lockHeld: true,
|
|
1425
|
+
});
|
|
1426
|
+
const stampedReceipt = isPlainObject(stamped.receipt) ? stamped.receipt : {};
|
|
1427
|
+
|
|
1428
|
+
const phase = typeof merged.current_phase === "string" ? merged.current_phase : undefined;
|
|
1429
|
+
const active = merged.active !== false;
|
|
1430
|
+
// Reflect the lock-owned mode-state revision onto the in-memory payload so the active-state/HUD
|
|
1431
|
+
// sync derives a `sourceRevision` from the revision this write actually owns (computed inside the
|
|
1432
|
+
// writer lock), not the stale pre-write value or a post-lock re-read a concurrent writer could
|
|
1433
|
+
// have advanced; otherwise the active-state writer stale-skips the update and the mirror keeps the
|
|
1434
|
+
// prior phase (e.g. staying "interviewing" after a "handoff" write).
|
|
1435
|
+
merged.state_revision = stampedRevision;
|
|
1436
|
+
await syncWorkflowSkillState({
|
|
1437
|
+
cwd,
|
|
1438
|
+
mode,
|
|
1439
|
+
sessionId,
|
|
1440
|
+
threadId,
|
|
1441
|
+
turnId,
|
|
1442
|
+
active,
|
|
1443
|
+
phase,
|
|
1444
|
+
payload: merged,
|
|
1445
|
+
receipt,
|
|
1446
|
+
});
|
|
1447
|
+
await touchStateActivityMarker(cwd, sessionId, filePath);
|
|
1446
1448
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1449
|
+
return {
|
|
1450
|
+
status: 0,
|
|
1451
|
+
stdout: renderCliWriteReceipt({
|
|
1452
|
+
ok: true,
|
|
1453
|
+
skill: mode,
|
|
1454
|
+
state_path: receipt.state_path,
|
|
1455
|
+
current_phase: phase,
|
|
1456
|
+
active,
|
|
1457
|
+
mutation_id: typeof stampedReceipt.mutation_id === "string" ? stampedReceipt.mutation_id : mutationId,
|
|
1458
|
+
status: typeof stampedReceipt.status === "string" ? stampedReceipt.status : undefined,
|
|
1459
|
+
content_sha256: stampedReceipt.content_sha256,
|
|
1460
|
+
}),
|
|
1461
|
+
...(outOfBandWarning ? { stderr: `${outOfBandWarning}\n` } : {}),
|
|
1462
|
+
};
|
|
1463
|
+
},
|
|
1464
|
+
{ cwd },
|
|
1465
|
+
);
|
|
1461
1466
|
}
|
|
1462
1467
|
|
|
1463
1468
|
async function handleClear(
|
|
@@ -1476,74 +1481,135 @@ async function handleClear(
|
|
|
1476
1481
|
|
|
1477
1482
|
const filePath = modeStateFile(cwd, mode, sessionId);
|
|
1478
1483
|
const forced = hasFlag(args, "--force");
|
|
1479
|
-
|
|
1480
|
-
|
|
1484
|
+
return await withWorkflowStateLock(
|
|
1485
|
+
filePath,
|
|
1486
|
+
async () => {
|
|
1487
|
+
const existingRead = await readExistingStateForMutation(filePath);
|
|
1488
|
+
if (existingRead.kind === "corrupt" && !forced) {
|
|
1489
|
+
throw new StateCommandError(
|
|
1490
|
+
2,
|
|
1491
|
+
`existing state for ${mode} is corrupt or tampered (${existingRead.error}); use --force to overwrite`,
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
const existing = existingRead.kind === "valid" ? existingRead.value : {};
|
|
1495
|
+
const staleReason = await describeStaleClearState(cwd, sessionId, mode, existing);
|
|
1496
|
+
if (staleReason && !forced) {
|
|
1497
|
+
throw new StateCommandError(
|
|
1498
|
+
2,
|
|
1499
|
+
`existing state for ${mode} is stale (${staleReason}); use --force to clear`,
|
|
1500
|
+
);
|
|
1501
|
+
}
|
|
1502
|
+
const clearedAt = nowIso();
|
|
1503
|
+
const cleared: Record<string, unknown> = {
|
|
1504
|
+
skill: mode,
|
|
1505
|
+
...existing,
|
|
1506
|
+
active: false,
|
|
1507
|
+
current_phase: "complete",
|
|
1508
|
+
updated_at: clearedAt,
|
|
1509
|
+
version: WORKFLOW_STATE_VERSION,
|
|
1510
|
+
};
|
|
1511
|
+
cleared.skill = mode;
|
|
1512
|
+
const mutationId = `${mode}:clear:${clearedAt}`;
|
|
1513
|
+
const receipt = buildWorkflowStateReceipt({
|
|
1514
|
+
cwd,
|
|
1515
|
+
skill: mode,
|
|
1516
|
+
owner: "gjc-state-cli",
|
|
1517
|
+
command: `gjc state ${mode} clear`,
|
|
1518
|
+
sessionId,
|
|
1519
|
+
nowIso: clearedAt,
|
|
1520
|
+
mutationId,
|
|
1521
|
+
});
|
|
1522
|
+
cleared.receipt = receipt;
|
|
1523
|
+
const { warning: outOfBandWarning, stamped } = await writeJsonAtomic(cwd, filePath, cleared, "clear", {
|
|
1524
|
+
sessionId,
|
|
1525
|
+
skill: mode,
|
|
1526
|
+
mutationId,
|
|
1527
|
+
force: forced,
|
|
1528
|
+
fromPhase: typeof existing.current_phase === "string" ? existing.current_phase : undefined,
|
|
1529
|
+
toPhase: "complete",
|
|
1530
|
+
lockHeld: true,
|
|
1531
|
+
});
|
|
1532
|
+
const stampedReceipt = isPlainObject(stamped.receipt) ? stamped.receipt : {};
|
|
1533
|
+
|
|
1534
|
+
await syncWorkflowSkillState({
|
|
1535
|
+
cwd,
|
|
1536
|
+
mode,
|
|
1537
|
+
sessionId,
|
|
1538
|
+
threadId,
|
|
1539
|
+
turnId,
|
|
1540
|
+
active: false,
|
|
1541
|
+
phase: "complete",
|
|
1542
|
+
payload: cleared,
|
|
1543
|
+
});
|
|
1544
|
+
await touchStateActivityMarker(cwd, sessionId, filePath);
|
|
1545
|
+
return {
|
|
1546
|
+
status: 0,
|
|
1547
|
+
stdout: renderCliWriteReceipt({
|
|
1548
|
+
ok: true,
|
|
1549
|
+
skill: mode,
|
|
1550
|
+
state_path: receipt.state_path,
|
|
1551
|
+
active: false,
|
|
1552
|
+
current_phase: typeof cleared.current_phase === "string" ? cleared.current_phase : undefined,
|
|
1553
|
+
mutation_id: typeof stampedReceipt.mutation_id === "string" ? stampedReceipt.mutation_id : mutationId,
|
|
1554
|
+
status: typeof stampedReceipt.status === "string" ? stampedReceipt.status : undefined,
|
|
1555
|
+
content_sha256: stampedReceipt.content_sha256,
|
|
1556
|
+
}),
|
|
1557
|
+
...(outOfBandWarning ? { stderr: `${outOfBandWarning}\n` } : {}),
|
|
1558
|
+
};
|
|
1559
|
+
},
|
|
1560
|
+
{ cwd },
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
const DEEP_INTERVIEW_INTENT_ID_RE = /(?:artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}/g;
|
|
1565
|
+
|
|
1566
|
+
async function assertDeepInterviewHandoffReady(state: Record<string, unknown>): Promise<void> {
|
|
1567
|
+
const envelope = normalizeDeepInterviewEnvelope(state);
|
|
1568
|
+
const inner = envelope.state;
|
|
1569
|
+
if (!inner) return;
|
|
1570
|
+
if (inner.intent_contract === undefined) {
|
|
1571
|
+
if (inner.intent_contract_required === true)
|
|
1572
|
+
throw new StateCommandError(2, "deep-interview handoff requires a locked Round 0 intent contract");
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
assertDeepInterviewIntentManifest(inner.intent_contract);
|
|
1576
|
+
const specPath = typeof state.spec_path === "string" ? state.spec_path : undefined;
|
|
1577
|
+
const expectedSha = typeof state.spec_sha256 === "string" ? state.spec_sha256 : undefined;
|
|
1578
|
+
if (!specPath || !expectedSha)
|
|
1579
|
+
throw new StateCommandError(2, "deep-interview handoff requires a persisted intent-validated spec");
|
|
1580
|
+
let content: string;
|
|
1581
|
+
try {
|
|
1582
|
+
content = await fs.readFile(specPath, "utf-8");
|
|
1583
|
+
} catch (error) {
|
|
1481
1584
|
throw new StateCommandError(
|
|
1482
1585
|
2,
|
|
1483
|
-
`
|
|
1586
|
+
`deep-interview handoff cannot read persisted spec: ${error instanceof Error ? error.message : String(error)}`,
|
|
1484
1587
|
);
|
|
1485
1588
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1589
|
+
if (createHash("sha256").update(content).digest("hex") !== expectedSha)
|
|
1590
|
+
throw new StateCommandError(2, "deep-interview handoff spec hash mismatch");
|
|
1591
|
+
const observedIds = [...new Set(content.match(DEEP_INTERVIEW_INTENT_ID_RE) ?? [])].sort();
|
|
1592
|
+
const rounds = Array.isArray(inner.rounds)
|
|
1593
|
+
? inner.rounds
|
|
1594
|
+
.filter(
|
|
1595
|
+
(round): round is Record<string, unknown> =>
|
|
1596
|
+
Boolean(round) && typeof round === "object" && !Array.isArray(round),
|
|
1597
|
+
)
|
|
1598
|
+
.map(round => ({ round: round.round, answer_hash: round.answer_hash }))
|
|
1599
|
+
: [];
|
|
1600
|
+
try {
|
|
1601
|
+
assertDeepInterviewIntentReview(
|
|
1602
|
+
inner.intent_review,
|
|
1603
|
+
inner.intent_contract as DeepInterviewIntentManifest,
|
|
1604
|
+
observedIds,
|
|
1605
|
+
rounds,
|
|
1606
|
+
);
|
|
1607
|
+
} catch (error) {
|
|
1608
|
+
throw new StateCommandError(
|
|
1609
|
+
2,
|
|
1610
|
+
`deep-interview handoff intent validation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
1611
|
+
);
|
|
1490
1612
|
}
|
|
1491
|
-
const clearedAt = nowIso();
|
|
1492
|
-
const cleared: Record<string, unknown> = {
|
|
1493
|
-
skill: mode,
|
|
1494
|
-
...existing,
|
|
1495
|
-
active: false,
|
|
1496
|
-
current_phase: "complete",
|
|
1497
|
-
updated_at: clearedAt,
|
|
1498
|
-
version: WORKFLOW_STATE_VERSION,
|
|
1499
|
-
};
|
|
1500
|
-
cleared.skill = mode;
|
|
1501
|
-
const mutationId = `${mode}:clear:${clearedAt}`;
|
|
1502
|
-
const receipt = buildWorkflowStateReceipt({
|
|
1503
|
-
cwd,
|
|
1504
|
-
skill: mode,
|
|
1505
|
-
owner: "gjc-state-cli",
|
|
1506
|
-
command: `gjc state ${mode} clear`,
|
|
1507
|
-
sessionId,
|
|
1508
|
-
nowIso: clearedAt,
|
|
1509
|
-
mutationId,
|
|
1510
|
-
});
|
|
1511
|
-
cleared.receipt = receipt;
|
|
1512
|
-
const { warning: outOfBandWarning, stamped } = await writeJsonAtomic(cwd, filePath, cleared, "clear", {
|
|
1513
|
-
sessionId,
|
|
1514
|
-
skill: mode,
|
|
1515
|
-
mutationId,
|
|
1516
|
-
force: forced,
|
|
1517
|
-
fromPhase: typeof existing.current_phase === "string" ? existing.current_phase : undefined,
|
|
1518
|
-
toPhase: "complete",
|
|
1519
|
-
});
|
|
1520
|
-
const stampedReceipt = isPlainObject(stamped.receipt) ? stamped.receipt : {};
|
|
1521
|
-
|
|
1522
|
-
await syncWorkflowSkillState({
|
|
1523
|
-
cwd,
|
|
1524
|
-
mode,
|
|
1525
|
-
sessionId,
|
|
1526
|
-
threadId,
|
|
1527
|
-
turnId,
|
|
1528
|
-
active: false,
|
|
1529
|
-
phase: "complete",
|
|
1530
|
-
payload: cleared,
|
|
1531
|
-
});
|
|
1532
|
-
await touchStateActivityMarker(cwd, sessionId, filePath);
|
|
1533
|
-
return {
|
|
1534
|
-
status: 0,
|
|
1535
|
-
stdout: renderCliWriteReceipt({
|
|
1536
|
-
ok: true,
|
|
1537
|
-
skill: mode,
|
|
1538
|
-
state_path: filePath,
|
|
1539
|
-
active: false,
|
|
1540
|
-
current_phase: typeof cleared.current_phase === "string" ? cleared.current_phase : undefined,
|
|
1541
|
-
mutation_id: typeof stampedReceipt.mutation_id === "string" ? stampedReceipt.mutation_id : mutationId,
|
|
1542
|
-
status: typeof stampedReceipt.status === "string" ? stampedReceipt.status : undefined,
|
|
1543
|
-
content_sha256: stampedReceipt.content_sha256,
|
|
1544
|
-
}),
|
|
1545
|
-
...(outOfBandWarning ? { stderr: `${outOfBandWarning}\n` } : {}),
|
|
1546
|
-
};
|
|
1547
1613
|
}
|
|
1548
1614
|
|
|
1549
1615
|
/**
|
|
@@ -1565,7 +1631,7 @@ async function handleClear(
|
|
|
1565
1631
|
* the phase remains in `skill-active-state.json` until a chain call (or
|
|
1566
1632
|
* explicit `clear`) demotes it.
|
|
1567
1633
|
*/
|
|
1568
|
-
async function
|
|
1634
|
+
async function handleHandoffUnlocked(
|
|
1569
1635
|
args: readonly string[],
|
|
1570
1636
|
cwd: string,
|
|
1571
1637
|
positionalSkill: string | undefined,
|
|
@@ -1619,18 +1685,19 @@ async function handleHandoff(
|
|
|
1619
1685
|
nowIso: handoffAt,
|
|
1620
1686
|
mutationId,
|
|
1621
1687
|
});
|
|
1688
|
+
const normalizedCaller =
|
|
1689
|
+
caller === "deep-interview"
|
|
1690
|
+
? (normalizeDeepInterviewEnvelope(migrateWorkflowState(existingCaller, caller).state) as Record<
|
|
1691
|
+
string,
|
|
1692
|
+
unknown
|
|
1693
|
+
>)
|
|
1694
|
+
: migrateWorkflowState(existingCaller, caller).state;
|
|
1695
|
+
if (caller === "deep-interview") await assertDeepInterviewHandoffReady(normalizedCaller);
|
|
1622
1696
|
|
|
1623
1697
|
// Runtime callees have no native mode-state to clear later, so do not
|
|
1624
1698
|
// persist them as active-state entries; the prompt observer tracks them
|
|
1625
1699
|
// in memory the same way direct `/skill:<runtime>` invocation does.
|
|
1626
1700
|
if (!calleeIsWorkflow) {
|
|
1627
|
-
const normalizedCaller =
|
|
1628
|
-
caller === "deep-interview"
|
|
1629
|
-
? (normalizeDeepInterviewEnvelope(migrateWorkflowState(existingCaller, caller).state) as Record<
|
|
1630
|
-
string,
|
|
1631
|
-
unknown
|
|
1632
|
-
>)
|
|
1633
|
-
: migrateWorkflowState(existingCaller, caller).state;
|
|
1634
1701
|
const mergedCallerState: Record<string, unknown> = {
|
|
1635
1702
|
...normalizedCaller,
|
|
1636
1703
|
skill: caller,
|
|
@@ -1727,13 +1794,6 @@ async function handleHandoff(
|
|
|
1727
1794
|
});
|
|
1728
1795
|
|
|
1729
1796
|
const calleeInitial = initialPhaseForSkill(callee);
|
|
1730
|
-
const normalizedCaller =
|
|
1731
|
-
caller === "deep-interview"
|
|
1732
|
-
? (normalizeDeepInterviewEnvelope(migrateWorkflowState(existingCaller, caller).state) as Record<
|
|
1733
|
-
string,
|
|
1734
|
-
unknown
|
|
1735
|
-
>)
|
|
1736
|
-
: migrateWorkflowState(existingCaller, caller).state;
|
|
1737
1797
|
const normalizedCallee =
|
|
1738
1798
|
callee === "deep-interview"
|
|
1739
1799
|
? (normalizeDeepInterviewEnvelope(migrateWorkflowState(existingCallee, callee).state) as Record<
|
|
@@ -1884,6 +1944,24 @@ async function handleHandoff(
|
|
|
1884
1944
|
};
|
|
1885
1945
|
}
|
|
1886
1946
|
|
|
1947
|
+
async function handleHandoff(
|
|
1948
|
+
args: readonly string[],
|
|
1949
|
+
cwd: string,
|
|
1950
|
+
positionalSkill: string | undefined,
|
|
1951
|
+
): Promise<StateCommandResult> {
|
|
1952
|
+
const selectors = await resolveSelectors(args, cwd, positionalSkill, "handoff");
|
|
1953
|
+
// Serialize concurrent handoffs on a dedicated sentinel lock, NOT on the
|
|
1954
|
+
// derived `skill-active-state.json` cache. The inner transaction
|
|
1955
|
+
// (applyHandoffToActiveState / syncSkillActiveState -> rebuildActiveSnapshot)
|
|
1956
|
+
// re-locks that cache file, and `withFileLock` is not reentrant: holding the
|
|
1957
|
+
// active-state lock here made the inner rebuild self-contend and fail after
|
|
1958
|
+
// all retries whenever `cwd === process.cwd()` (the real CLI case). Pass
|
|
1959
|
+
// `{ cwd }` so the sentinel resolves against the handoff cwd rather than
|
|
1960
|
+
// `process.cwd()`.
|
|
1961
|
+
const handoffLock = path.join(sessionStateDir(cwd, selectors.gjcSessionId), "handoff");
|
|
1962
|
+
return withWorkflowStateLock(handoffLock, async () => handleHandoffUnlocked(args, cwd, positionalSkill), { cwd });
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1887
1965
|
async function handleContract(
|
|
1888
1966
|
args: readonly string[],
|
|
1889
1967
|
cwd: string,
|
|
@@ -2253,7 +2331,7 @@ export async function runNativeStateCommand(args: string[], cwd = process.cwd())
|
|
|
2253
2331
|
return { status: 2, stderr: `Unknown gjc state command: ${parsed.action}\n` };
|
|
2254
2332
|
}
|
|
2255
2333
|
} catch (error) {
|
|
2256
|
-
if (error instanceof
|
|
2334
|
+
if (error instanceof CommandError) return { status: error.exitStatus, stderr: `${error.message}\n` };
|
|
2257
2335
|
if (error instanceof SessionResolutionError) return { status: 2, stderr: `${error.message}\n` };
|
|
2258
2336
|
return { status: 1, stderr: `${error instanceof Error ? error.message : String(error)}\n` };
|
|
2259
2337
|
}
|