@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,7 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { Snowflake } from "@gajae-code/utils";
|
|
4
|
-
import { type Goal, type GoalModeState, normalizeGoal } from "../goals/state";
|
|
4
|
+
import { type Goal, type GoalModeState, type GoalProvenance, normalizeGoal } from "../goals/state";
|
|
5
|
+
|
|
5
6
|
import {
|
|
6
7
|
buildSessionContext,
|
|
7
8
|
loadEntriesFromFile,
|
|
@@ -27,6 +28,8 @@ export interface PendingGoalModeRequest {
|
|
|
27
28
|
objective: string;
|
|
28
29
|
createdAt: string;
|
|
29
30
|
goalsPath?: string;
|
|
31
|
+
provenance: Extract<GoalProvenance, { source: "ultragoal" }>;
|
|
32
|
+
|
|
30
33
|
/**
|
|
31
34
|
* Session id that produced this request (from GJC_SESSION_ID). When present,
|
|
32
35
|
* only the originating session may consume it, so concurrent sessions sharing
|
|
@@ -42,6 +45,7 @@ export type CurrentSessionGoalModeWriteResult =
|
|
|
42
45
|
|
|
43
46
|
interface UltragoalPlanShape {
|
|
44
47
|
gjcObjective?: unknown;
|
|
48
|
+
goals?: Array<{ id?: unknown }>;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
function isEnoent(error: unknown): boolean {
|
|
@@ -70,7 +74,7 @@ export function isUltragoalCreateGoalsInvocation(args: readonly string[]): boole
|
|
|
70
74
|
export async function readUltragoalGjcObjective(
|
|
71
75
|
cwd: string,
|
|
72
76
|
sessionId?: string | null,
|
|
73
|
-
): Promise<{ objective: string; goalsPath: string }> {
|
|
77
|
+
): Promise<{ objective: string; goalsPath: string; provenance: Extract<GoalProvenance, { source: "ultragoal" }> }> {
|
|
74
78
|
const session = sessionId?.trim()
|
|
75
79
|
? { gjcSessionId: sessionId.trim() }
|
|
76
80
|
: await resolveGjcSessionForRead(cwd, { envSessionId: process.env.GJC_SESSION_ID });
|
|
@@ -78,11 +82,21 @@ export async function readUltragoalGjcObjective(
|
|
|
78
82
|
try {
|
|
79
83
|
const plan = (await Bun.file(goalsPath).json()) as UltragoalPlanShape;
|
|
80
84
|
const objective = typeof plan.gjcObjective === "string" ? plan.gjcObjective.trim() : "";
|
|
81
|
-
|
|
85
|
+
const goalId = typeof plan.goals?.[0]?.id === "string" ? plan.goals[0].id : "aggregate";
|
|
86
|
+
return {
|
|
87
|
+
objective: objective || DEFAULT_ULTRAGOAL_OBJECTIVE,
|
|
88
|
+
goalsPath,
|
|
89
|
+
provenance: { source: "ultragoal", runId: session.gjcSessionId, goalId },
|
|
90
|
+
};
|
|
82
91
|
} catch (error) {
|
|
83
92
|
if (isEnoent(error)) {
|
|
84
|
-
return {
|
|
93
|
+
return {
|
|
94
|
+
objective: DEFAULT_ULTRAGOAL_OBJECTIVE,
|
|
95
|
+
goalsPath,
|
|
96
|
+
provenance: { source: "ultragoal", runId: session.gjcSessionId, goalId: "aggregate" },
|
|
97
|
+
};
|
|
85
98
|
}
|
|
99
|
+
|
|
86
100
|
throw error;
|
|
87
101
|
}
|
|
88
102
|
}
|
|
@@ -92,6 +106,7 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
92
106
|
objective: string;
|
|
93
107
|
goalsPath?: string;
|
|
94
108
|
sessionId?: string | null;
|
|
109
|
+
provenance?: Extract<GoalProvenance, { source: "ultragoal" }>;
|
|
95
110
|
}): Promise<PendingGoalModeRequest> {
|
|
96
111
|
const objective = input.objective.trim();
|
|
97
112
|
if (!objective) throw new Error("goal objective is required");
|
|
@@ -106,6 +121,8 @@ export async function writePendingGoalModeRequest(input: {
|
|
|
106
121
|
objective,
|
|
107
122
|
createdAt: new Date().toISOString(),
|
|
108
123
|
goalsPath: input.goalsPath,
|
|
124
|
+
provenance: input.provenance ?? { source: "ultragoal", runId: sessionId, goalId: "aggregate" },
|
|
125
|
+
|
|
109
126
|
...(sessionId ? { sessionId } : {}),
|
|
110
127
|
};
|
|
111
128
|
const filePath = requestPath(input.cwd, sessionId);
|
|
@@ -125,7 +142,26 @@ function isNonTerminalGoal(goal: Goal | null): goal is Goal {
|
|
|
125
142
|
return goal !== null && goal.status !== "complete" && goal.status !== "dropped";
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
function
|
|
145
|
+
function matchesGoalModeRequest(existingGoal: Goal, objective: string, provenance: Goal["provenance"]): boolean {
|
|
146
|
+
const existingProvenance = existingGoal.provenance;
|
|
147
|
+
if (existingProvenance?.source === "ultragoal" && provenance?.source === "ultragoal") {
|
|
148
|
+
return existingProvenance.runId === provenance.runId && existingProvenance.goalId === provenance.goalId;
|
|
149
|
+
}
|
|
150
|
+
// Legacy goals have no durable identity. Only an exact normalized objective can
|
|
151
|
+
// establish that they are the same goal.
|
|
152
|
+
return existingGoal.objective.trim() === objective;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function hasProvenDifferentDurablePlan(existingGoal: Goal, provenance: Goal["provenance"]): boolean {
|
|
156
|
+
const existingProvenance = existingGoal.provenance;
|
|
157
|
+
return (
|
|
158
|
+
existingProvenance?.source === "ultragoal" &&
|
|
159
|
+
provenance?.source === "ultragoal" &&
|
|
160
|
+
existingProvenance.runId !== provenance.runId
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function createGoalModeState(objective: string, provenance: Goal["provenance"]): GoalModeState {
|
|
129
165
|
const now = Date.now();
|
|
130
166
|
const goal: Goal = {
|
|
131
167
|
id: String(Snowflake.next()),
|
|
@@ -135,6 +171,7 @@ function createGoalModeState(objective: string): GoalModeState {
|
|
|
135
171
|
timeUsedSeconds: 0,
|
|
136
172
|
createdAt: now,
|
|
137
173
|
updatedAt: now,
|
|
174
|
+
...(provenance ? { provenance } : {}),
|
|
138
175
|
};
|
|
139
176
|
return { enabled: true, mode: "active", goal };
|
|
140
177
|
}
|
|
@@ -151,6 +188,7 @@ function nextSessionEntryId(entries: readonly SessionEntry[]): string {
|
|
|
151
188
|
export async function writeCurrentSessionGoalModeState(input: {
|
|
152
189
|
sessionFile?: string | null;
|
|
153
190
|
objective: string;
|
|
191
|
+
provenance?: Goal["provenance"];
|
|
154
192
|
}): Promise<CurrentSessionGoalModeWriteResult> {
|
|
155
193
|
const sessionFile = input.sessionFile?.trim();
|
|
156
194
|
if (!sessionFile) return { status: "unavailable", reason: "missing_session_file" };
|
|
@@ -162,13 +200,22 @@ export async function writeCurrentSessionGoalModeState(input: {
|
|
|
162
200
|
const entries = fileEntries.filter((entry): entry is SessionEntry => entry.type !== "session");
|
|
163
201
|
if (fileEntries.length === 0) return { status: "unavailable", reason: "empty_session_file" };
|
|
164
202
|
|
|
203
|
+
const requestedProvenance = input.provenance;
|
|
165
204
|
const context = buildSessionContext(entries);
|
|
166
205
|
const existingGoal = goalFromModeData(context.modeData);
|
|
167
206
|
if ((context.mode === "goal" || context.mode === "goal_paused") && isNonTerminalGoal(existingGoal)) {
|
|
168
|
-
|
|
207
|
+
if (matchesGoalModeRequest(existingGoal, objective, requestedProvenance)) {
|
|
208
|
+
return { status: "existing_goal", goal: existingGoal };
|
|
209
|
+
}
|
|
210
|
+
// A legacy or user goal cannot establish that the incoming ultragoal request
|
|
211
|
+
// belongs to a different durable plan. Preserve it rather than overwriting
|
|
212
|
+
// active user work; only a distinct ultragoal run ID may replace it.
|
|
213
|
+
if (!hasProvenDifferentDurablePlan(existingGoal, requestedProvenance)) {
|
|
214
|
+
return { status: "existing_goal", goal: existingGoal };
|
|
215
|
+
}
|
|
169
216
|
}
|
|
170
217
|
|
|
171
|
-
const state = createGoalModeState(objective);
|
|
218
|
+
const state = createGoalModeState(objective, requestedProvenance);
|
|
172
219
|
const entry: ModeChangeEntry = {
|
|
173
220
|
type: "mode_change",
|
|
174
221
|
id: nextSessionEntryId(entries),
|
|
@@ -213,7 +260,7 @@ export async function consumePendingGoalModeRequest(
|
|
|
213
260
|
// (do not delete it) so its rightful owner can still pick it up. Legacy/unscoped
|
|
214
261
|
// requests (no sessionId) remain consumable by any session in this cwd.
|
|
215
262
|
const ownerSessionId = typeof candidate.sessionId === "string" ? candidate.sessionId.trim() : "";
|
|
216
|
-
if (ownerSessionId && ownerSessionId !==
|
|
263
|
+
if (ownerSessionId && ownerSessionId !== session.gjcSessionId) {
|
|
217
264
|
return null;
|
|
218
265
|
}
|
|
219
266
|
await removeFileAudited(filePath, {
|
|
@@ -5,6 +5,7 @@ import * as path from "node:path";
|
|
|
5
5
|
import { VERSION } from "@gajae-code/utils/dirs";
|
|
6
6
|
import { safeStderrWrite } from "@gajae-code/utils/safe-stderr";
|
|
7
7
|
import type { Args } from "../cli/args";
|
|
8
|
+
import { readLinuxProcStartTimeSync } from "./linux-proc";
|
|
8
9
|
import { tmuxRuntimeSessionPath } from "./session-layout";
|
|
9
10
|
import {
|
|
10
11
|
GJC_COORDINATOR_SESSION_BRANCH_ENV,
|
|
@@ -1108,11 +1109,7 @@ function defaultOwnerIsolationProbe(
|
|
|
1108
1109
|
let startTime: string | undefined;
|
|
1109
1110
|
let cgroupText: string | null = null;
|
|
1110
1111
|
try {
|
|
1111
|
-
|
|
1112
|
-
startTime = stat
|
|
1113
|
-
.slice(stat.lastIndexOf(")") + 2)
|
|
1114
|
-
.trim()
|
|
1115
|
-
.split(/\s+/)[19];
|
|
1112
|
+
startTime = readLinuxProcStartTimeSync(pid) ?? undefined;
|
|
1116
1113
|
cgroupText = fs.readFileSync(`/proc/${pid}/cgroup`, "utf8");
|
|
1117
1114
|
} catch {}
|
|
1118
1115
|
if (!startTime) return { state: "unverifiable" };
|
|
@@ -299,14 +299,14 @@ export function ensureReusableNodeModules(sourceRoot: string, worktreePath: stri
|
|
|
299
299
|
return "symlink";
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
args: string[],
|
|
305
|
-
): {
|
|
302
|
+
/** Result of {@link prepareLaunchWorktree}: the effective working directory, remaining args, and resolved worktree plan. */
|
|
303
|
+
export interface PreparedLaunchWorktree {
|
|
306
304
|
cwd: string;
|
|
307
305
|
args: string[];
|
|
308
306
|
worktree: GjcLaunchWorktreeResult | { enabled: false };
|
|
309
|
-
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function prepareLaunchWorktree(cwd: string, args: string[]): PreparedLaunchWorktree {
|
|
310
310
|
const parsed = parseLaunchWorktreeMode(args);
|
|
311
311
|
const planned = planLaunchWorktree(cwd, parsed.mode);
|
|
312
312
|
const ensured = ensureLaunchWorktree(planned);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for reading Linux `/proc/<pid>/stat` process start time.
|
|
3
|
+
*
|
|
4
|
+
* The `comm` field (field 2, wrapped in parentheses) may itself contain spaces
|
|
5
|
+
* and parentheses, so the only robust anchor is the *last* `)` in the stat
|
|
6
|
+
* string. Field 22 (the process start time in clock ticks since boot) is the
|
|
7
|
+
* 20th whitespace-separated token after that closing paren (index 19).
|
|
8
|
+
*
|
|
9
|
+
* Every caller previously parsed this format independently, with subtly
|
|
10
|
+
* different failure handling. This module fails closed: any malformed input
|
|
11
|
+
* (missing `)`, non-numeric field 22, unreadable `/proc` file, non-Linux
|
|
12
|
+
* platform) yields `null` rather than an inconsistent sentinel.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as nodeFsSync from "node:fs";
|
|
16
|
+
import * as nodeFs from "node:fs/promises";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse field 22 (process start time, in clock ticks since boot) from one
|
|
20
|
+
* `/proc/<pid>/stat` record. Returns the raw numeric token, or `null` when the
|
|
21
|
+
* record shape is malformed or field 22 is absent/non-numeric.
|
|
22
|
+
*/
|
|
23
|
+
export function parseLinuxProcStartTime(stat: string | null | undefined): string | null {
|
|
24
|
+
if (!stat || stat.includes("\0") || stat.includes("\r")) return null;
|
|
25
|
+
const record = stat.endsWith("\n") ? stat.slice(0, -1) : stat;
|
|
26
|
+
if (!record || record.includes("\n")) return null;
|
|
27
|
+
|
|
28
|
+
const open = record.indexOf(" (");
|
|
29
|
+
const close = record.lastIndexOf(")");
|
|
30
|
+
if (open < 1 || close <= open + 1 || !/^[1-9]\d*$/.test(record.slice(0, open))) return null;
|
|
31
|
+
|
|
32
|
+
const suffix = record.slice(close + 1);
|
|
33
|
+
if (!/^[ \t]+/.test(suffix)) return null;
|
|
34
|
+
const fields = suffix.trim().split(/[ \t]+/);
|
|
35
|
+
if (fields.length < 20 || !/^[RSDTtXZPI]$/.test(fields[0])) return null;
|
|
36
|
+
|
|
37
|
+
const startTime = fields[19];
|
|
38
|
+
return /^\d+$/.test(startTime) ? startTime : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read `/proc/<pid>/stat` synchronously and return the parsed start time.
|
|
43
|
+
* Returns `null` on non-Linux platforms, unreadable files, or malformed input.
|
|
44
|
+
*/
|
|
45
|
+
export function readLinuxProcStartTimeSync(pid: number): string | null {
|
|
46
|
+
if (process.platform !== "linux") return null;
|
|
47
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return null;
|
|
48
|
+
let stat: string;
|
|
49
|
+
try {
|
|
50
|
+
stat = nodeFsSync.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return parseLinuxProcStartTime(stat);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Read `/proc/<pid>/stat` asynchronously and return the parsed start time.
|
|
59
|
+
* Returns `null` on non-Linux platforms, unreadable files, or malformed input.
|
|
60
|
+
*/
|
|
61
|
+
export async function readLinuxProcStartTime(pid: number): Promise<string | null> {
|
|
62
|
+
if (process.platform !== "linux") return null;
|
|
63
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return null;
|
|
64
|
+
let stat: string;
|
|
65
|
+
try {
|
|
66
|
+
stat = await nodeFs.readFile(`/proc/${pid}/stat`, "utf8");
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return parseLinuxProcStartTime(stat);
|
|
71
|
+
}
|
|
@@ -19,9 +19,12 @@ import { runNativeStateCommand } from "./state-runtime";
|
|
|
19
19
|
import {
|
|
20
20
|
appendJsonlIdempotent,
|
|
21
21
|
readExistingStateForMutation,
|
|
22
|
+
withWorkflowStateLock,
|
|
22
23
|
writeArtifact,
|
|
23
24
|
writeWorkflowEnvelopeAtomic,
|
|
24
25
|
} from "./state-writer";
|
|
26
|
+
import { assertSafePathComponent, CommandError, flagValue, hasFlag } from "./workflow-cli-common";
|
|
27
|
+
import { getSkillManifest } from "./workflow-manifest";
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* Native implementation of `gjc ralplan`.
|
|
@@ -55,8 +58,6 @@ type RalplanStage = (typeof KNOWN_STAGES)[number];
|
|
|
55
58
|
const KNOWN_ARCHITECT_KINDS = new Set(["openai-code"]);
|
|
56
59
|
const KNOWN_CRITIC_KINDS = new Set(["openai-code"]);
|
|
57
60
|
|
|
58
|
-
const PATH_COMPONENT_RE = /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,63}$/;
|
|
59
|
-
|
|
60
61
|
const SUBAGENT_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/;
|
|
61
62
|
|
|
62
63
|
const KNOWN_FALLBACK_REASONS = new Set([
|
|
@@ -68,12 +69,9 @@ const KNOWN_FALLBACK_REASONS = new Set([
|
|
|
68
69
|
"missing_record",
|
|
69
70
|
]);
|
|
70
71
|
|
|
71
|
-
class RalplanCommandError extends
|
|
72
|
-
constructor(
|
|
73
|
-
|
|
74
|
-
message: string,
|
|
75
|
-
) {
|
|
76
|
-
super(message);
|
|
72
|
+
class RalplanCommandError extends CommandError {
|
|
73
|
+
constructor(exitStatus: number, message: string) {
|
|
74
|
+
super(exitStatus, message);
|
|
77
75
|
this.name = "RalplanCommandError";
|
|
78
76
|
}
|
|
79
77
|
}
|
|
@@ -95,16 +93,6 @@ const VALUE_FLAGS = new Set([
|
|
|
95
93
|
"--fallback-receipt-path",
|
|
96
94
|
]);
|
|
97
95
|
|
|
98
|
-
function flagValue(args: readonly string[], flag: string): string | undefined {
|
|
99
|
-
const index = args.indexOf(flag);
|
|
100
|
-
if (index < 0) return undefined;
|
|
101
|
-
return args[index + 1];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function hasFlag(args: readonly string[], flag: string): boolean {
|
|
105
|
-
return args.includes(flag);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
96
|
export function isRalplanArtifactWriteInvocation(args: readonly string[]): boolean {
|
|
109
97
|
return hasFlag(args, "--write");
|
|
110
98
|
}
|
|
@@ -113,12 +101,6 @@ function isRalplanDoctorInvocation(args: readonly string[]): boolean {
|
|
|
113
101
|
return args[0] === "doctor";
|
|
114
102
|
}
|
|
115
103
|
|
|
116
|
-
function assertSafePathComponent(value: string, label: string): void {
|
|
117
|
-
if (!PATH_COMPONENT_RE.test(value) || value.includes("..")) {
|
|
118
|
-
throw new RalplanCommandError(2, `invalid path component for --${label}: ${value}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
104
|
function assertKnownStage(stage: string): asserts stage is RalplanStage {
|
|
123
105
|
if (!(KNOWN_STAGES as readonly string[]).includes(stage)) {
|
|
124
106
|
throw new RalplanCommandError(2, `unknown --stage: ${stage}. Expected one of: ${KNOWN_STAGES.join(", ")}.`);
|
|
@@ -204,61 +186,57 @@ async function readActiveRunId(cwd: string, sessionId: string): Promise<string |
|
|
|
204
186
|
* undo Stop semantics. Every other phase advances to track the stage just
|
|
205
187
|
* persisted so run-state stays coherent with the active ralplan stage.
|
|
206
188
|
*/
|
|
207
|
-
const PHASE_LOCK = new Set([
|
|
208
|
-
"final",
|
|
209
|
-
"handoff",
|
|
210
|
-
"complete",
|
|
211
|
-
"completed",
|
|
212
|
-
"failed",
|
|
213
|
-
"cancelled",
|
|
214
|
-
"canceled",
|
|
215
|
-
"inactive",
|
|
216
|
-
]);
|
|
217
189
|
|
|
218
190
|
/** Phase that keeps run-state coherent with the stage just written, preserving locked phases. */
|
|
219
191
|
function advanceCurrentPhase(existingPhase: unknown, stage: RalplanStage): string {
|
|
220
192
|
const current = typeof existingPhase === "string" ? existingPhase.trim() : "";
|
|
221
|
-
if (current &&
|
|
193
|
+
if (current && getSkillManifest("ralplan").phaseLock.includes(current)) return current;
|
|
222
194
|
return stage;
|
|
223
195
|
}
|
|
224
196
|
|
|
225
197
|
async function persistActiveRunId(cwd: string, sessionId: string, runId: string, stage: RalplanStage): Promise<void> {
|
|
226
198
|
const statePath = ralplanStatePath(cwd, sessionId);
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
199
|
+
return await withWorkflowStateLock(
|
|
200
|
+
statePath,
|
|
201
|
+
async () => {
|
|
202
|
+
const existingRead = await readExistingStateForMutation(statePath);
|
|
203
|
+
if (existingRead.kind === "corrupt") {
|
|
204
|
+
throw new RalplanCommandError(
|
|
205
|
+
2,
|
|
206
|
+
`existing ralplan state is corrupt or tampered (${existingRead.error}); refusing to overwrite ${statePath}`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
let existing: Record<string, unknown> = existingRead.kind === "valid" ? existingRead.value : {};
|
|
210
|
+
|
|
211
|
+
// A new run_id is a fresh run, not a stray write on the prior run: never inherit a
|
|
212
|
+
// previous run's terminal/locked phase (which would start the new run already
|
|
213
|
+
// "complete"/"handoff" and disarm the Stop hook). PHASE_LOCK only guards same-run writes.
|
|
214
|
+
const isNewRun = existing.run_id !== runId;
|
|
215
|
+
const nextPhase = isNewRun ? stage : advanceCurrentPhase(existing.current_phase, stage);
|
|
216
|
+
if (
|
|
217
|
+
existing.run_id === runId &&
|
|
218
|
+
existing.version === WORKFLOW_STATE_VERSION &&
|
|
219
|
+
existing.current_phase === nextPhase &&
|
|
220
|
+
(existing.active === true || getSkillManifest("ralplan").phaseLock.includes(nextPhase))
|
|
221
|
+
) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
existing.run_id = runId;
|
|
225
|
+
if (typeof existing.skill !== "string") existing.skill = "ralplan";
|
|
226
|
+
// A successful persist means ralplan is actively writing this run's artifacts, so always
|
|
227
|
+
// re-assert active. Fallback-only init left active:false after a clear (#644, sibling of #638).
|
|
228
|
+
existing.active = true;
|
|
229
|
+
existing.current_phase = nextPhase;
|
|
230
|
+
existing = migrateWorkflowState(existing, "ralplan").state;
|
|
231
|
+
existing.updated_at = new Date().toISOString();
|
|
232
|
+
await writeWorkflowEnvelopeAtomic(statePath, existing, {
|
|
233
|
+
cwd,
|
|
234
|
+
receipt: { cwd, skill: "ralplan", owner: "gjc-runtime", command: "gjc ralplan persist-run-id", sessionId },
|
|
235
|
+
audit: { category: "state", verb: "write", owner: "gjc-runtime", skill: "ralplan", sessionId },
|
|
236
|
+
});
|
|
237
|
+
},
|
|
238
|
+
{ cwd },
|
|
239
|
+
);
|
|
262
240
|
}
|
|
263
241
|
|
|
264
242
|
/* --------------------------- planner run-state --------------------------- */
|
|
@@ -382,25 +360,31 @@ function plannerStatePayload(update: PlannerStateUpdate): Record<string, unknown
|
|
|
382
360
|
*/
|
|
383
361
|
async function applyPlannerStateUpdate(cwd: string, sessionId: string, update: PlannerStateUpdate): Promise<void> {
|
|
384
362
|
const statePath = ralplanStatePath(cwd, sessionId);
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
363
|
+
return await withWorkflowStateLock(
|
|
364
|
+
statePath,
|
|
365
|
+
async () => {
|
|
366
|
+
const existingRead = await readExistingStateForMutation(statePath);
|
|
367
|
+
if (existingRead.kind === "corrupt") {
|
|
368
|
+
throw new RalplanCommandError(
|
|
369
|
+
2,
|
|
370
|
+
`existing ralplan state is corrupt or tampered (${existingRead.error}); refusing to overwrite ${statePath}`,
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
let existing: Record<string, unknown> = existingRead.kind === "valid" ? existingRead.value : {};
|
|
374
|
+
Object.assign(existing, plannerStatePayload(update));
|
|
375
|
+
if (typeof existing.skill !== "string") existing.skill = "ralplan";
|
|
376
|
+
if (typeof existing.active !== "boolean") existing.active = true;
|
|
377
|
+
if (typeof existing.current_phase !== "string") existing.current_phase = "planner";
|
|
378
|
+
existing = migrateWorkflowState(existing, "ralplan").state;
|
|
379
|
+
existing.updated_at = new Date().toISOString();
|
|
380
|
+
await writeWorkflowEnvelopeAtomic(statePath, existing, {
|
|
381
|
+
cwd,
|
|
382
|
+
receipt: { cwd, skill: "ralplan", owner: "gjc-runtime", command: "gjc ralplan planner-state", sessionId },
|
|
383
|
+
audit: { category: "state", verb: "write", owner: "gjc-runtime", skill: "ralplan", sessionId },
|
|
384
|
+
});
|
|
385
|
+
},
|
|
386
|
+
{ cwd },
|
|
387
|
+
);
|
|
404
388
|
}
|
|
405
389
|
|
|
406
390
|
async function resolveArtifactArgs(args: readonly string[], cwd: string): Promise<ResolvedArtifactArgs> {
|
|
@@ -916,7 +900,7 @@ export async function runNativeRalplanCommand(args: string[], cwd = process.cwd(
|
|
|
916
900
|
if (isRalplanArtifactWriteInvocation(args)) return await handleArtifactWrite(args, cwd);
|
|
917
901
|
return await handleConsensusHandoff(args, cwd);
|
|
918
902
|
} catch (error) {
|
|
919
|
-
if (error instanceof
|
|
903
|
+
if (error instanceof CommandError) return { status: error.exitStatus, stderr: `${error.message}\n` };
|
|
920
904
|
return { status: 1, stderr: `${error instanceof Error ? error.message : String(error)}\n` };
|
|
921
905
|
}
|
|
922
906
|
}
|
|
@@ -39,13 +39,22 @@ export interface SessionIdSources {
|
|
|
39
39
|
export class SessionResolutionError extends Error {
|
|
40
40
|
constructor(
|
|
41
41
|
message: string,
|
|
42
|
-
readonly code: "blank_flag" | "no_session" | "ambiguous" | "missing_for_write",
|
|
42
|
+
readonly code: "blank_flag" | "unsafe_session" | "no_session" | "ambiguous" | "missing_for_write",
|
|
43
43
|
) {
|
|
44
44
|
super(message);
|
|
45
45
|
this.name = "SessionResolutionError";
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function assertSafeResolvedSessionId(sessionId: string): void {
|
|
50
|
+
if (sessionId === "." || sessionId === ".." || /[/\\]/.test(sessionId)) {
|
|
51
|
+
throw new SessionResolutionError(
|
|
52
|
+
"session id must be a single path component (no separators or traversal)",
|
|
53
|
+
"unsafe_session",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
interface ResolvedFromSources {
|
|
50
59
|
gjcSessionId: string;
|
|
51
60
|
source: GjcSessionSource;
|
|
@@ -65,13 +74,18 @@ export function resolveSessionIdFromSources(sources: SessionIdSources): Resolved
|
|
|
65
74
|
"blank_flag",
|
|
66
75
|
);
|
|
67
76
|
}
|
|
77
|
+
assertSafeResolvedSessionId(trimmed);
|
|
68
78
|
return { gjcSessionId: trimmed, source: "flag" };
|
|
69
79
|
}
|
|
70
80
|
if (typeof payloadSessionId === "string" && payloadSessionId.trim() !== "") {
|
|
71
|
-
|
|
81
|
+
const trimmed = payloadSessionId.trim();
|
|
82
|
+
assertSafeResolvedSessionId(trimmed);
|
|
83
|
+
return { gjcSessionId: trimmed, source: "payload" };
|
|
72
84
|
}
|
|
73
85
|
if (typeof envSessionId === "string" && envSessionId.trim() !== "") {
|
|
74
|
-
|
|
86
|
+
const trimmed = envSessionId.trim();
|
|
87
|
+
assertSafeResolvedSessionId(trimmed);
|
|
88
|
+
return { gjcSessionId: trimmed, source: "env" };
|
|
75
89
|
}
|
|
76
90
|
return undefined;
|
|
77
91
|
}
|
|
@@ -155,6 +169,7 @@ async function collectActiveSessionCandidates(cwd: string): Promise<SessionCandi
|
|
|
155
169
|
if (!entry.isDirectory()) continue;
|
|
156
170
|
const gjcSessionId = sessionIdFromDirName(entry.name);
|
|
157
171
|
if (!gjcSessionId) continue;
|
|
172
|
+
assertSafeResolvedSessionId(gjcSessionId);
|
|
158
173
|
const dir = path.join(root, entry.name);
|
|
159
174
|
const activityMs = await readActivityMs(path.join(dir, GJC_SESSION_ACTIVITY_FILE));
|
|
160
175
|
// Sessions with no readable activity marker are considered inactive and
|