@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,6 +1,6 @@
|
|
|
1
1
|
import { dlopen, ptr } from "bun:ffi";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
2
|
import { Process } from "@gajae-code/natives";
|
|
3
|
+
import { readLinuxProcStartTimeSync } from "../../gjc-runtime/linux-proc";
|
|
4
4
|
|
|
5
5
|
const DARWIN_PROC_PIDTBSDINFO = 3;
|
|
6
6
|
const DARWIN_PROC_BSDINFO_SIZE = 136;
|
|
@@ -95,7 +95,8 @@ export function parseDarwinProcessIncarnation(info: Uint8Array): string | undefi
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
/** Whether `value` is a canonical process-incarnation string (`linux:`/`darwin:`/`windows:`). */
|
|
99
|
+
export function isProcessIncarnation(value: unknown): value is string {
|
|
99
100
|
return (
|
|
100
101
|
typeof value === "string" &&
|
|
101
102
|
(/^(?:linux:\d+|darwin:[1-9]\d*:\d+)$/.test(value) ||
|
|
@@ -116,17 +117,8 @@ export function processIncarnation(pid: number, options: ProcessIncarnationOptio
|
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
if (platform === "linux") {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const close = stat.lastIndexOf(")");
|
|
122
|
-
const startTicks = stat
|
|
123
|
-
.slice(close + 2)
|
|
124
|
-
.trim()
|
|
125
|
-
.split(/\s+/)[19];
|
|
126
|
-
return startTicks ? `linux:${startTicks}` : undefined;
|
|
127
|
-
} catch {
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
120
|
+
const startTicks = readLinuxProcStartTimeSync(pid);
|
|
121
|
+
return startTicks ? `linux:${startTicks}` : undefined;
|
|
130
122
|
}
|
|
131
123
|
if (platform === "darwin") {
|
|
132
124
|
const info = new Uint8Array(DARWIN_PROC_BSDINFO_SIZE);
|
|
@@ -2,7 +2,13 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { withFileLock } from "../../config/file-lock";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
assertSupportedSnapshotVersion,
|
|
7
|
+
assertSupportedStateVersion,
|
|
8
|
+
SDK_STATE_VERSION,
|
|
9
|
+
SESSION_INDEX_SNAPSHOT_VERSION,
|
|
10
|
+
UnsupportedStateVersionError,
|
|
11
|
+
} from "./state-version";
|
|
6
12
|
|
|
7
13
|
export type SessionIndexEventType =
|
|
8
14
|
| "host_registered"
|
|
@@ -48,15 +54,94 @@ export const sessionIndexChecksum = (event: Omit<SessionIndexEvent, "checksum">)
|
|
|
48
54
|
const dirFor = (agentDir: string) => path.join(agentDir, "sdk", "sessions");
|
|
49
55
|
const logFor = (agentDir: string) => path.join(dirFor(agentDir), "index.jsonl");
|
|
50
56
|
const snapshotFor = (agentDir: string) => path.join(dirFor(agentDir), "index.snapshot.json");
|
|
57
|
+
const ROTATE_BYTES = 4 * 1024 * 1024;
|
|
58
|
+
function isValidSnapshot(snapshot: unknown): snapshot is { indexSeq: number; events: SessionIndexEvent[] } {
|
|
59
|
+
if (!snapshot || typeof snapshot !== "object") return false;
|
|
60
|
+
const { indexSeq, events } = snapshot as { indexSeq?: unknown; events?: unknown };
|
|
61
|
+
if (typeof indexSeq !== "number" || !Number.isSafeInteger(indexSeq) || indexSeq < 0) return false;
|
|
62
|
+
if (!Array.isArray(events)) return false;
|
|
63
|
+
if (events.length === 0) return indexSeq === 0;
|
|
64
|
+
// Accept strictly-increasing indexSeq (gaps allowed after compaction), preserving
|
|
65
|
+
// each event's original checksum. The old contiguous 1..N format is a special case.
|
|
66
|
+
let previous = 0;
|
|
67
|
+
for (const event of events) {
|
|
68
|
+
if (!event || typeof event !== "object") return false;
|
|
69
|
+
const { checksum, ...unsigned } = event as SessionIndexEvent;
|
|
70
|
+
if (typeof event.indexSeq !== "number" || !Number.isSafeInteger(event.indexSeq)) return false;
|
|
71
|
+
if (event.indexSeq <= previous) return false;
|
|
72
|
+
if (checksum !== sessionIndexChecksum(unsigned)) return false;
|
|
73
|
+
previous = event.indexSeq;
|
|
74
|
+
}
|
|
75
|
+
return previous === indexSeq;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Compact the event history for a snapshot without renumbering: clients hold indexSeq
|
|
79
|
+
// across calls, so retained events keep their original indexSeq and checksum. Drops
|
|
80
|
+
// terminal+dead sessions entirely, collapses superseded heartbeats to the latest per
|
|
81
|
+
// surviving session, and always retains the global-max indexSeq as the chain anchor.
|
|
82
|
+
function compactEvents(events: SessionIndexEvent[]): SessionIndexEvent[] {
|
|
83
|
+
if (events.length === 0) return events;
|
|
84
|
+
const maxIndexSeq = events[events.length - 1]!.indexSeq;
|
|
85
|
+
const latestBySession = new Map<string, SessionIndexEvent>();
|
|
86
|
+
for (const event of events) latestBySession.set(event.sessionId, event);
|
|
87
|
+
const deadTerminal = new Set<string>();
|
|
88
|
+
for (const [sessionId, latest] of latestBySession) {
|
|
89
|
+
const terminal = latest.type === "host_unregistered" || latest.type === "session_closed";
|
|
90
|
+
if (terminal && !alive(latest.pid)) deadTerminal.add(sessionId);
|
|
91
|
+
}
|
|
92
|
+
const latestHeartbeatSeq = new Map<string, number>();
|
|
93
|
+
for (const event of events) {
|
|
94
|
+
if (event.type === "host_heartbeat" && !deadTerminal.has(event.sessionId)) {
|
|
95
|
+
latestHeartbeatSeq.set(event.sessionId, event.indexSeq);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const kept: SessionIndexEvent[] = [];
|
|
99
|
+
for (const event of events) {
|
|
100
|
+
if (event.indexSeq === maxIndexSeq) {
|
|
101
|
+
kept.push(event);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (deadTerminal.has(event.sessionId)) continue;
|
|
105
|
+
if (event.type === "host_heartbeat" && latestHeartbeatSeq.get(event.sessionId) !== event.indexSeq) continue;
|
|
106
|
+
kept.push(event);
|
|
107
|
+
}
|
|
108
|
+
return kept;
|
|
109
|
+
}
|
|
110
|
+
|
|
51
111
|
async function appendSync(file: string, value: string): Promise<void> {
|
|
52
112
|
const h = await fs.open(file, "a", 0o600);
|
|
53
113
|
try {
|
|
54
|
-
|
|
114
|
+
const data = Buffer.from(`${value}\n`);
|
|
115
|
+
for (let offset = 0; offset < data.length; ) {
|
|
116
|
+
const { bytesWritten } = await h.write(data, offset, data.length - offset);
|
|
117
|
+
if (bytesWritten <= 0) throw new Error("Unable to append session index entry");
|
|
118
|
+
offset += bytesWritten;
|
|
119
|
+
}
|
|
55
120
|
await h.sync();
|
|
56
121
|
} finally {
|
|
57
122
|
await h.close();
|
|
58
123
|
}
|
|
59
124
|
}
|
|
125
|
+
|
|
126
|
+
async function syncDirectory(file: string): Promise<void> {
|
|
127
|
+
let handle: fs.FileHandle;
|
|
128
|
+
try {
|
|
129
|
+
handle = await fs.open(path.dirname(file), "r");
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
132
|
+
if (process.platform === "win32" && (code === "EPERM" || code === "EACCES")) return;
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await handle.sync();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
139
|
+
if (process.platform !== "win32" || (code !== "EPERM" && code !== "EACCES")) throw error;
|
|
140
|
+
} finally {
|
|
141
|
+
await handle.close();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
60
145
|
function alive(pid: number): boolean {
|
|
61
146
|
try {
|
|
62
147
|
process.kill(pid, 0);
|
|
@@ -70,20 +155,21 @@ export class SessionIndex {
|
|
|
70
155
|
#events: SessionIndexEvent[] = [];
|
|
71
156
|
#warnings: string[] = [];
|
|
72
157
|
#logOffset = 0;
|
|
158
|
+
#corruptSuffix = false;
|
|
73
159
|
constructor(agentDir: string) {
|
|
74
160
|
this.#agentDir = agentDir;
|
|
75
161
|
}
|
|
76
162
|
async open(): Promise<this> {
|
|
77
|
-
await this.assertSupportedStateVersions();
|
|
78
163
|
await fs.mkdir(dirFor(this.#agentDir), { recursive: true, mode: 0o700 });
|
|
79
164
|
await fs.chmod(dirFor(this.#agentDir), 0o700);
|
|
80
|
-
await this.replay();
|
|
165
|
+
await withFileLock(logFor(this.#agentDir), () => this.replay());
|
|
81
166
|
return this;
|
|
82
167
|
}
|
|
83
168
|
async replay(): Promise<void> {
|
|
84
169
|
this.#events = [];
|
|
85
170
|
this.#warnings = [];
|
|
86
171
|
this.#logOffset = 0;
|
|
172
|
+
this.#corruptSuffix = false;
|
|
87
173
|
let snapshotSeq = 0;
|
|
88
174
|
try {
|
|
89
175
|
const snapshot = JSON.parse(await fs.readFile(snapshotFor(this.#agentDir), "utf8")) as {
|
|
@@ -91,35 +177,25 @@ export class SessionIndex {
|
|
|
91
177
|
events?: SessionIndexEvent[];
|
|
92
178
|
indexSeq?: number;
|
|
93
179
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
typeof snapshotIndexSeq !== "number" ||
|
|
98
|
-
!Number.isSafeInteger(snapshotIndexSeq) ||
|
|
99
|
-
snapshotIndexSeq < 0 ||
|
|
100
|
-
!Array.isArray(snapshot.events) ||
|
|
101
|
-
snapshot.events.some(event => {
|
|
102
|
-
const { checksum, ...unsigned } = event;
|
|
103
|
-
return event.indexSeq > snapshotIndexSeq || checksum !== sessionIndexChecksum(unsigned);
|
|
104
|
-
})
|
|
105
|
-
)
|
|
106
|
-
throw new Error("invalid snapshot");
|
|
180
|
+
assertSupportedSnapshotVersion(snapshotFor(this.#agentDir), snapshot);
|
|
181
|
+
if (!isValidSnapshot(snapshot)) throw new Error("invalid snapshot");
|
|
107
182
|
this.#events = snapshot.events;
|
|
108
|
-
snapshotSeq =
|
|
183
|
+
snapshotSeq = snapshot.indexSeq;
|
|
109
184
|
} catch (e) {
|
|
110
185
|
if (e instanceof UnsupportedStateVersionError) throw e;
|
|
111
186
|
if ((e as NodeJS.ErrnoException).code !== "ENOENT") this.#warnings.push("Invalid session index snapshot");
|
|
112
187
|
}
|
|
113
|
-
await this.#tail(snapshotSeq);
|
|
188
|
+
await this.#tail(snapshotSeq, false);
|
|
114
189
|
}
|
|
115
|
-
async #tail(snapshotSeq = this.indexSeq): Promise<void> {
|
|
190
|
+
async #tail(snapshotSeq = this.indexSeq, allowResync = true): Promise<void> {
|
|
116
191
|
let data: Buffer;
|
|
117
192
|
try {
|
|
118
193
|
const handle = await fs.open(logFor(this.#agentDir), "r");
|
|
119
194
|
try {
|
|
120
195
|
const stat = await handle.stat();
|
|
121
196
|
if (stat.size < this.#logOffset) {
|
|
122
|
-
this
|
|
197
|
+
if (allowResync) await this.replay();
|
|
198
|
+
else this.#warn("Session index log was truncated");
|
|
123
199
|
return;
|
|
124
200
|
}
|
|
125
201
|
data = Buffer.alloc(stat.size - this.#logOffset);
|
|
@@ -132,9 +208,10 @@ export class SessionIndex {
|
|
|
132
208
|
throw e;
|
|
133
209
|
}
|
|
134
210
|
const lastNewline = data.lastIndexOf(0x0a);
|
|
135
|
-
if (lastNewline < 0) return;
|
|
136
211
|
const consumed = data.subarray(0, lastNewline + 1);
|
|
137
212
|
this.#logOffset += consumed.length;
|
|
213
|
+
const hasUnterminatedSuffix = data.length > consumed.length;
|
|
214
|
+
let corrupt = false;
|
|
138
215
|
for (const line of consumed.toString("utf8").split("\n")) {
|
|
139
216
|
if (!line) continue;
|
|
140
217
|
let event: SessionIndexEvent;
|
|
@@ -143,18 +220,25 @@ export class SessionIndex {
|
|
|
143
220
|
assertSupportedStateVersion(logFor(this.#agentDir), event);
|
|
144
221
|
} catch (error) {
|
|
145
222
|
if (error instanceof UnsupportedStateVersionError) throw error;
|
|
146
|
-
|
|
147
|
-
|
|
223
|
+
corrupt = true;
|
|
224
|
+
continue;
|
|
148
225
|
}
|
|
149
|
-
if (event.indexSeq <= snapshotSeq) continue;
|
|
226
|
+
if (corrupt || event.indexSeq <= snapshotSeq) continue;
|
|
150
227
|
const { checksum, ...unsigned } = event;
|
|
151
|
-
if (checksum !== sessionIndexChecksum(unsigned) || event.indexSeq !== this.indexSeq + 1)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
228
|
+
if (checksum !== sessionIndexChecksum(unsigned) || event.indexSeq !== this.indexSeq + 1) corrupt = true;
|
|
229
|
+
else this.#events.push(event);
|
|
230
|
+
}
|
|
231
|
+
if (hasUnterminatedSuffix) corrupt = true;
|
|
232
|
+
if (corrupt) {
|
|
233
|
+
this.#corruptSuffix = true;
|
|
234
|
+
this.#warn("Corrupt session index entry; replay truncated");
|
|
235
|
+
if (allowResync) await this.replay();
|
|
156
236
|
}
|
|
157
237
|
}
|
|
238
|
+
#warn(message: string): void {
|
|
239
|
+
if (!this.#warnings.includes(message)) this.#warnings.push(message);
|
|
240
|
+
}
|
|
241
|
+
|
|
158
242
|
async refresh(): Promise<void> {
|
|
159
243
|
await this.#tail();
|
|
160
244
|
}
|
|
@@ -167,7 +251,8 @@ export class SessionIndex {
|
|
|
167
251
|
): Promise<SessionIndexEvent> {
|
|
168
252
|
await fs.mkdir(dirFor(this.#agentDir), { recursive: true, mode: 0o700 });
|
|
169
253
|
return withFileLock(logFor(this.#agentDir), async () => {
|
|
170
|
-
await this.
|
|
254
|
+
await this.replay();
|
|
255
|
+
if (this.#corruptSuffix) throw new Error("Cannot append to corrupt session index log");
|
|
171
256
|
const unsigned: Omit<SessionIndexEvent, "checksum"> = {
|
|
172
257
|
...input,
|
|
173
258
|
version: SDK_STATE_VERSION,
|
|
@@ -177,16 +262,34 @@ export class SessionIndex {
|
|
|
177
262
|
const event: SessionIndexEvent = { ...unsigned, checksum: sessionIndexChecksum(unsigned) };
|
|
178
263
|
await appendSync(logFor(this.#agentDir), JSON.stringify(event));
|
|
179
264
|
await this.refresh();
|
|
265
|
+
if ((await fs.stat(logFor(this.#agentDir))).size >= ROTATE_BYTES) await this.#rotate();
|
|
180
266
|
return event;
|
|
181
267
|
});
|
|
182
268
|
}
|
|
183
269
|
async snapshot(): Promise<void> {
|
|
270
|
+
await withFileLock(logFor(this.#agentDir), () => this.#snapshotUnderLock());
|
|
271
|
+
}
|
|
272
|
+
async #snapshotUnderLock(): Promise<void> {
|
|
273
|
+
await this.replay();
|
|
184
274
|
const file = snapshotFor(this.#agentDir);
|
|
275
|
+
let current: unknown;
|
|
276
|
+
try {
|
|
277
|
+
current = JSON.parse(await fs.readFile(file, "utf8"));
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT" && !(error instanceof SyntaxError)) throw error;
|
|
280
|
+
}
|
|
281
|
+
if (isValidSnapshot(current) && current.indexSeq > this.indexSeq) return;
|
|
185
282
|
const tmp = `${file}.${process.pid}.tmp`;
|
|
186
283
|
await fs.writeFile(
|
|
187
284
|
tmp,
|
|
188
|
-
JSON.stringify({
|
|
189
|
-
|
|
285
|
+
JSON.stringify({
|
|
286
|
+
version: SESSION_INDEX_SNAPSHOT_VERSION,
|
|
287
|
+
indexSeq: this.indexSeq,
|
|
288
|
+
events: compactEvents(this.#events),
|
|
289
|
+
}),
|
|
290
|
+
{
|
|
291
|
+
mode: 0o600,
|
|
292
|
+
},
|
|
190
293
|
);
|
|
191
294
|
const h = await fs.open(tmp, "r");
|
|
192
295
|
try {
|
|
@@ -195,30 +298,16 @@ export class SessionIndex {
|
|
|
195
298
|
await h.close();
|
|
196
299
|
}
|
|
197
300
|
await fs.rename(tmp, file);
|
|
301
|
+
await syncDirectory(file);
|
|
198
302
|
}
|
|
199
|
-
async
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
throw error;
|
|
208
|
-
}
|
|
209
|
-
if (file.endsWith(".json")) {
|
|
210
|
-
assertSupportedStateVersion(file, JSON.parse(source));
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
for (const line of source.split("\n")) {
|
|
214
|
-
if (!line) continue;
|
|
215
|
-
try {
|
|
216
|
-
assertSupportedStateVersion(file, JSON.parse(line));
|
|
217
|
-
} catch (error) {
|
|
218
|
-
if (error instanceof UnsupportedStateVersionError) throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
303
|
+
async #rotate(): Promise<void> {
|
|
304
|
+
await this.#snapshotUnderLock();
|
|
305
|
+
const file = logFor(this.#agentDir);
|
|
306
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
307
|
+
await fs.writeFile(temporary, "", { mode: 0o600 });
|
|
308
|
+
await fs.rename(temporary, file);
|
|
309
|
+
await syncDirectory(file);
|
|
310
|
+
this.#logOffset = 0;
|
|
222
311
|
}
|
|
223
312
|
|
|
224
313
|
listSessions(): SessionList {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export const SDK_STATE_VERSION = 1;
|
|
2
2
|
|
|
3
|
+
// The session-index snapshot carries its own format version, independent of the shared
|
|
4
|
+
// SDK_STATE_VERSION used by discovery, the lifecycle ledger, and per-event records. Version 2
|
|
5
|
+
// snapshots relax the on-disk schema to allow gaps in indexSeq (compaction drops terminal+dead
|
|
6
|
+
// sessions and superseded heartbeats), so an older broker must hard-fail rather than silently
|
|
7
|
+
// fall back to a truncated log. Bumping only this constant keeps the broker protocol untouched.
|
|
8
|
+
export const SESSION_INDEX_SNAPSHOT_VERSION = 2;
|
|
9
|
+
|
|
3
10
|
export class UnsupportedStateVersionError extends Error {
|
|
4
11
|
readonly code = "unsupported_state_version";
|
|
5
12
|
|
|
@@ -21,3 +28,17 @@ export function assertSupportedStateVersion(file: string, value: unknown): void
|
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
}
|
|
31
|
+
|
|
32
|
+
// Fences the session-index snapshot format independently of SDK_STATE_VERSION. An older broker
|
|
33
|
+
// (whose SESSION_INDEX_SNAPSHOT_VERSION is 1) trips assertSupportedStateVersion on a version-2
|
|
34
|
+
// snapshot and refuses to start; a current broker accepts both the legacy contiguous format
|
|
35
|
+
// (version 1 or absent) and the gapped version-2 format.
|
|
36
|
+
export function assertSupportedSnapshotVersion(file: string, value: unknown): void {
|
|
37
|
+
if (!value || typeof value !== "object") return;
|
|
38
|
+
const record = value as { version?: unknown; stateVersion?: unknown };
|
|
39
|
+
for (const version of [record.version, record.stateVersion]) {
|
|
40
|
+
if (typeof version === "number" && Number.isFinite(version) && version > SESSION_INDEX_SNAPSHOT_VERSION) {
|
|
41
|
+
throw new UnsupportedStateVersionError(file, version);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -43,7 +43,7 @@ export class BrokerTransport {
|
|
|
43
43
|
readonly #broker: Broker;
|
|
44
44
|
readonly #token: string;
|
|
45
45
|
readonly #requestedPort: number;
|
|
46
|
-
#server:
|
|
46
|
+
#server: Bun.Server<undefined> | null = null;
|
|
47
47
|
#port = 0;
|
|
48
48
|
constructor(broker: Broker, token: string, port = 0) {
|
|
49
49
|
this.#broker = broker;
|
|
@@ -66,6 +66,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
|
|
|
66
66
|
rich: { enabled: true },
|
|
67
67
|
richDraft: { enabled: false },
|
|
68
68
|
topics: { nameTemplate: undefined },
|
|
69
|
+
btw: { enabled: true },
|
|
69
70
|
})
|
|
70
71
|
) {
|
|
71
72
|
throw new Error("Discord notifications are enabled but configuration is incomplete");
|
|
@@ -98,6 +99,7 @@ async function loadConfig(agentDir: string, kind: ChatDaemonKind): Promise<ChatD
|
|
|
98
99
|
rich: { enabled: true },
|
|
99
100
|
richDraft: { enabled: false },
|
|
100
101
|
topics: { nameTemplate: undefined },
|
|
102
|
+
btw: { enabled: true },
|
|
101
103
|
})
|
|
102
104
|
) {
|
|
103
105
|
throw new Error("Slack notifications are enabled but configuration is incomplete");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn as childProcessSpawn
|
|
1
|
+
import { spawn as childProcessSpawn } from "node:child_process";
|
|
2
2
|
import * as crypto from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path from "node:path";
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
DaemonStatus,
|
|
13
13
|
} from "../../daemon/control-types";
|
|
14
14
|
import { resolveGjcRuntimeSpawnInfo } from "../../daemon/runtime";
|
|
15
|
+
import { isProcessIncarnation, processIncarnation } from "../broker/process-incarnation";
|
|
15
16
|
import { getNotificationConfig, isDiscordConfigured, isSlackConfigured } from "./config";
|
|
16
17
|
|
|
17
18
|
export type ChatDaemonKind = "discord" | "slack";
|
|
@@ -121,28 +122,7 @@ function defaultSignal(pid: number, signal: NodeJS.Signals): void {
|
|
|
121
122
|
process.kill(pid, signal);
|
|
122
123
|
} catch {}
|
|
123
124
|
}
|
|
124
|
-
|
|
125
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return undefined;
|
|
126
|
-
if (process.platform === "linux") {
|
|
127
|
-
try {
|
|
128
|
-
const stat = fs.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
129
|
-
return `linux:${
|
|
130
|
-
stat
|
|
131
|
-
.slice(stat.lastIndexOf(")") + 2)
|
|
132
|
-
.trim()
|
|
133
|
-
.split(/\s+/)[19]
|
|
134
|
-
}`;
|
|
135
|
-
} catch {
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (process.platform === "darwin") {
|
|
140
|
-
const result = spawnSync("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8" });
|
|
141
|
-
const startedAt = result.status === 0 ? result.stdout.trim() : "";
|
|
142
|
-
return startedAt ? `darwin:${startedAt}` : undefined;
|
|
143
|
-
}
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
125
|
+
|
|
146
126
|
function runtimeInfo(execPath?: string): DaemonRuntimeInfo {
|
|
147
127
|
const rt = resolveGjcRuntimeSpawnInfo(execPath ?? process.execPath);
|
|
148
128
|
return {
|
|
@@ -377,7 +357,7 @@ export class ChatDaemonController implements BuiltInDaemonController {
|
|
|
377
357
|
);
|
|
378
358
|
}
|
|
379
359
|
private incarnation(pid: number): string | undefined {
|
|
380
|
-
return (this.deps.pidIncarnation ??
|
|
360
|
+
return (this.deps.pidIncarnation ?? processIncarnation)(pid);
|
|
381
361
|
}
|
|
382
362
|
private isLiveState(state: ChatDaemonState): boolean {
|
|
383
363
|
return (
|
|
@@ -541,7 +521,7 @@ export async function acquireChatDaemonOwnership(input: {
|
|
|
541
521
|
const pid = input.pid ?? process.pid;
|
|
542
522
|
const probe: ChatDaemonOwnershipProbe = {
|
|
543
523
|
pidAlive: input.pidAlive ?? defaultPidAlive,
|
|
544
|
-
pidIncarnation: input.pidIncarnation ??
|
|
524
|
+
pidIncarnation: input.pidIncarnation ?? processIncarnation,
|
|
545
525
|
};
|
|
546
526
|
const incarnation = input.incarnation ?? probe.pidIncarnation(pid) ?? "unavailable";
|
|
547
527
|
await fs.promises.mkdir(paths.dir, { recursive: true, mode: 0o700 });
|
|
@@ -631,9 +611,9 @@ async function isStaleChatDaemonLock(lock: string, probe: ChatDaemonOwnershipPro
|
|
|
631
611
|
const currentIncarnation = probe.pidIncarnation(owner.pid);
|
|
632
612
|
return (
|
|
633
613
|
!probe.pidAlive(owner.pid) ||
|
|
634
|
-
(
|
|
635
|
-
owner.incarnation
|
|
636
|
-
|
|
614
|
+
(owner.incarnation !== "unavailable" &&
|
|
615
|
+
(!isProcessIncarnation(owner.incarnation) ||
|
|
616
|
+
(currentIncarnation !== undefined && currentIncarnation !== owner.incarnation)))
|
|
637
617
|
);
|
|
638
618
|
}
|
|
639
619
|
const stat = await fs.promises.stat(lock).catch(() => undefined);
|
|
@@ -118,6 +118,7 @@ function canClaim(effect: ChatEffect, now: number): boolean {
|
|
|
118
118
|
export class ChatEffectJournal {
|
|
119
119
|
readonly #store: ConversationStore<ChatEffect>;
|
|
120
120
|
readonly #mappings: ConversationStore<EffectReferenceMapping>;
|
|
121
|
+
readonly #maxTerminalEffects: number;
|
|
121
122
|
|
|
122
123
|
readonly #now: () => number;
|
|
123
124
|
|
|
@@ -131,10 +132,17 @@ export class ChatEffectJournal {
|
|
|
131
132
|
pidIncarnation?: (pid: number) => string | undefined;
|
|
132
133
|
sleep?: (ms: number) => Promise<void>;
|
|
133
134
|
lockTimeoutMs?: number;
|
|
135
|
+
maxTerminalEffects?: number;
|
|
134
136
|
}) {
|
|
135
137
|
this.#store = new ConversationStore<ChatEffect>({ ...input, kind: input.transport, fileName: "effects.json" });
|
|
136
138
|
this.#mappings = new ConversationStore<EffectReferenceMapping>({ ...input, kind: input.transport });
|
|
137
139
|
|
|
140
|
+
if (
|
|
141
|
+
input.maxTerminalEffects !== undefined &&
|
|
142
|
+
(!Number.isSafeInteger(input.maxTerminalEffects) || input.maxTerminalEffects < 1)
|
|
143
|
+
)
|
|
144
|
+
throw new Error("Chat effect terminal retention must be a positive safe integer");
|
|
145
|
+
this.#maxTerminalEffects = input.maxTerminalEffects ?? MAX_TERMINAL_CHAT_EFFECTS;
|
|
138
146
|
this.#now = input.now ?? Date.now;
|
|
139
147
|
}
|
|
140
148
|
|
|
@@ -343,12 +351,12 @@ export class ChatEffectJournal {
|
|
|
343
351
|
const terminal = (await this.list())
|
|
344
352
|
.filter(effect => effect.state === "terminal")
|
|
345
353
|
.sort((left, right) => left.updatedAt - right.updatedAt);
|
|
346
|
-
if (terminal.length <=
|
|
354
|
+
if (terminal.length <= this.#maxTerminalEffects) return;
|
|
347
355
|
|
|
348
356
|
const referenced = new Set<string>();
|
|
349
357
|
for (const mapping of Object.values((await this.#mappings.load()).conversations))
|
|
350
358
|
collectEffectReferences(mapping, referenced);
|
|
351
|
-
for (const effect of terminal.slice(0, terminal.length -
|
|
359
|
+
for (const effect of terminal.slice(0, terminal.length - this.#maxTerminalEffects)) {
|
|
352
360
|
if (!referenced.has(effect.id)) await this.#store.delete(effect.id, effect.generation);
|
|
353
361
|
}
|
|
354
362
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* removed), but the user can still adjust per-surface behaviour from inside a
|
|
6
6
|
* session thread with small slash commands:
|
|
7
7
|
*
|
|
8
|
-
* - `/verbose` switch the mirror to
|
|
8
|
+
* - `/verbose` switch the mirror to bounded tool-owned summaries + provider-displayable reasoning summaries
|
|
9
9
|
* - `/lean` switch back to lean (assistant text + tool names)
|
|
10
10
|
* - `/verbosity lean|verbose`
|
|
11
11
|
* - `/redact on|off` toggle redaction of streamed content
|
|
@@ -133,13 +133,14 @@ export function parseInThreadConfigCommand(text: string): ConfigCommandChange |
|
|
|
133
133
|
|
|
134
134
|
switch (command) {
|
|
135
135
|
case "verbose":
|
|
136
|
-
return { verbosity: "verbose" };
|
|
136
|
+
return rest.length === 0 ? { verbosity: "verbose" } : undefined;
|
|
137
137
|
case "lean":
|
|
138
|
-
return { verbosity: "lean" };
|
|
138
|
+
return rest.length === 0 ? { verbosity: "lean" } : undefined;
|
|
139
139
|
case "verbosity":
|
|
140
|
-
if (arg === "lean" || arg === "verbose") return { verbosity: arg };
|
|
140
|
+
if (rest.length === 1 && (arg === "lean" || arg === "verbose")) return { verbosity: arg };
|
|
141
141
|
return undefined;
|
|
142
142
|
case "redact":
|
|
143
|
+
if (rest.length !== 1) return undefined;
|
|
143
144
|
if (arg === "on" || arg === "true" || arg === "1") return { redact: true };
|
|
144
145
|
if (arg === "off" || arg === "false" || arg === "0") return { redact: false };
|
|
145
146
|
return undefined;
|
package/src/sdk/bus/config.ts
CHANGED
|
@@ -55,6 +55,9 @@ export interface NotificationSettingsSnapshot {
|
|
|
55
55
|
botToken?: string;
|
|
56
56
|
chatId?: string;
|
|
57
57
|
activation?: Record<string, unknown>;
|
|
58
|
+
btw: {
|
|
59
|
+
enabled: boolean;
|
|
60
|
+
};
|
|
58
61
|
rich: {
|
|
59
62
|
enabled: boolean;
|
|
60
63
|
};
|
|
@@ -93,6 +96,97 @@ export interface NotificationSettingsReader {
|
|
|
93
96
|
getNotificationSettingsSnapshot(): NotificationSettingsSnapshot;
|
|
94
97
|
getAgentDir(): string;
|
|
95
98
|
}
|
|
99
|
+
function notificationConfigurationError(): Error {
|
|
100
|
+
return new Error("gjc_notify_daemon_invalid_configuration");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function notificationSettingsObject(value: unknown): Record<string, unknown> {
|
|
104
|
+
if (value === undefined) return {};
|
|
105
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw notificationConfigurationError();
|
|
106
|
+
return value as Record<string, unknown>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function notificationSettingsString(value: unknown): string | undefined {
|
|
110
|
+
if (value === undefined) return undefined;
|
|
111
|
+
if (typeof value === "string") return value;
|
|
112
|
+
throw notificationConfigurationError();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function notificationSettingsBoolean(value: unknown, fallback: boolean): boolean {
|
|
116
|
+
if (value === undefined) return fallback;
|
|
117
|
+
if (typeof value === "boolean") return value;
|
|
118
|
+
throw notificationConfigurationError();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function notificationSettingsChoice<T extends string>(value: unknown, fallback: T, choices: readonly T[]): T {
|
|
122
|
+
if (value === undefined) return fallback;
|
|
123
|
+
if (typeof value === "string" && choices.includes(value as T)) return value as T;
|
|
124
|
+
throw notificationConfigurationError();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function notificationIdleTimeoutMs(value: unknown): number {
|
|
128
|
+
if (value === undefined) return 60_000;
|
|
129
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) return value;
|
|
130
|
+
throw notificationConfigurationError();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Validate and snapshot the raw global notification configuration used by both
|
|
135
|
+
* the interactive host and the lightweight daemon process.
|
|
136
|
+
*/
|
|
137
|
+
export function parseNotificationSettingsSnapshot(rawConfig?: unknown): NotificationSettingsSnapshot {
|
|
138
|
+
const root = notificationSettingsObject(rawConfig);
|
|
139
|
+
const notifications = notificationSettingsObject(root.notifications);
|
|
140
|
+
const telegram = notificationSettingsObject(notifications.telegram);
|
|
141
|
+
const btw = notificationSettingsObject(telegram.btw);
|
|
142
|
+
const rich = notificationSettingsObject(telegram.rich);
|
|
143
|
+
const richDraft = notificationSettingsObject(telegram.richDraft);
|
|
144
|
+
const topics = notificationSettingsObject(telegram.topics);
|
|
145
|
+
const activation = readTelegramActivationMarkers(notificationSettingsObject(telegram.activation));
|
|
146
|
+
const discord = notificationSettingsObject(notifications.discord);
|
|
147
|
+
const slack = notificationSettingsObject(notifications.slack);
|
|
148
|
+
const daemon = notificationSettingsObject(notifications.daemon);
|
|
149
|
+
return {
|
|
150
|
+
enabled: notificationSettingsBoolean(notifications.enabled, false),
|
|
151
|
+
telegram: {
|
|
152
|
+
botToken: notificationSettingsString(telegram.botToken),
|
|
153
|
+
chatId: notificationSettingsString(telegram.chatId),
|
|
154
|
+
...(Object.keys(activation).length === 0 ? {} : { activation }),
|
|
155
|
+
btw: {
|
|
156
|
+
enabled: notificationSettingsBoolean(btw.enabled, true),
|
|
157
|
+
},
|
|
158
|
+
rich: {
|
|
159
|
+
enabled: notificationSettingsBoolean(rich.enabled, true),
|
|
160
|
+
},
|
|
161
|
+
richDraft: {
|
|
162
|
+
enabled: notificationSettingsBoolean(richDraft.enabled, false),
|
|
163
|
+
},
|
|
164
|
+
topics: {
|
|
165
|
+
nameTemplate: notificationSettingsString(topics.nameTemplate),
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
discord: {
|
|
169
|
+
botToken: notificationSettingsString(discord.botToken),
|
|
170
|
+
applicationId: notificationSettingsString(discord.applicationId),
|
|
171
|
+
guildId: notificationSettingsString(discord.guildId),
|
|
172
|
+
parentChannelId: notificationSettingsString(discord.parentChannelId),
|
|
173
|
+
},
|
|
174
|
+
slack: {
|
|
175
|
+
botToken: notificationSettingsString(slack.botToken),
|
|
176
|
+
appToken: notificationSettingsString(slack.appToken),
|
|
177
|
+
workspaceId: notificationSettingsString(slack.workspaceId),
|
|
178
|
+
channelId: notificationSettingsString(slack.channelId),
|
|
179
|
+
authorizedUserId: notificationSettingsString(slack.authorizedUserId),
|
|
180
|
+
},
|
|
181
|
+
redact: notificationSettingsBoolean(notifications.redact, false),
|
|
182
|
+
verbosity: notificationSettingsChoice<"lean" | "verbose">(notifications.verbosity, "lean", ["lean", "verbose"]),
|
|
183
|
+
sessionScope: notificationSettingsChoice<"all" | "primary">(notifications.sessionScope, "all", [
|
|
184
|
+
"all",
|
|
185
|
+
"primary",
|
|
186
|
+
]),
|
|
187
|
+
idleTimeoutMs: notificationIdleTimeoutMs(daemon.idleTimeoutMs),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
96
190
|
|
|
97
191
|
export interface NotificationConfig {
|
|
98
192
|
enabled: boolean;
|
|
@@ -121,6 +215,9 @@ export interface NotificationConfig {
|
|
|
121
215
|
*/
|
|
122
216
|
sessionScope: "all" | "primary";
|
|
123
217
|
idleTimeoutMs: number;
|
|
218
|
+
btw: {
|
|
219
|
+
enabled: boolean;
|
|
220
|
+
};
|
|
124
221
|
rich: {
|
|
125
222
|
enabled: boolean;
|
|
126
223
|
};
|
|
@@ -154,6 +251,7 @@ export function getNotificationConfig(settings: NotificationSettingsReader): Not
|
|
|
154
251
|
sessionScope: snapshot.sessionScope,
|
|
155
252
|
idleTimeoutMs: snapshot.idleTimeoutMs,
|
|
156
253
|
rich: snapshot.telegram.rich,
|
|
254
|
+
btw: snapshot.telegram.btw,
|
|
157
255
|
richDraft: snapshot.telegram.richDraft,
|
|
158
256
|
topics: snapshot.telegram.topics,
|
|
159
257
|
};
|