@gajae-code/coding-agent 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +63 -1
- package/dist/types/async/job-manager.d.ts +46 -7
- package/dist/types/cli/config-cli.d.ts +11 -1
- package/dist/types/cli/notify-cli.d.ts +1 -0
- package/dist/types/cli/session-picker.d.ts +2 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/mcp-serve.d.ts +33 -0
- package/dist/types/commands/mcp.d.ts +1 -1
- package/dist/types/commands/notify.d.ts +0 -2
- package/dist/types/commands/sdk.d.ts +1 -1
- package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
- package/dist/types/config/file-lock.d.ts +14 -4
- package/dist/types/config/keybindings.d.ts +42 -1
- package/dist/types/config/model-auth.d.ts +3 -0
- package/dist/types/config/model-bindings-applier.d.ts +13 -0
- package/dist/types/config/model-discovery-manager.d.ts +49 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-registry.d.ts +16 -15
- package/dist/types/config/model-resolver.d.ts +28 -18
- package/dist/types/config/models-config-schema.d.ts +11 -0
- package/dist/types/config/settings-schema.d.ts +56 -1
- package/dist/types/config/settings.d.ts +14 -11
- package/dist/types/coordinator-mcp/policy.d.ts +5 -0
- package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
- package/dist/types/coordinator-mcp/server.d.ts +4 -0
- package/dist/types/discovery/helpers.d.ts +0 -6
- package/dist/types/edit/index.d.ts +3 -0
- package/dist/types/eval/py/env.d.ts +9 -0
- package/dist/types/extensibility/extensions/loader.d.ts +2 -1
- package/dist/types/extensibility/extensions/runner.d.ts +2 -2
- package/dist/types/extensibility/extensions/types.d.ts +34 -3
- package/dist/types/extensibility/hooks/runner.d.ts +1 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
- package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
- package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
- package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
- package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
- package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
- package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
- package/dist/types/gjc-runtime/team-store.d.ts +160 -0
- package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
- package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
- package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
- package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/goals/state.d.ts +8 -0
- package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
- package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
- package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
- package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
- package/dist/types/hindsight/state.d.ts +11 -4
- package/dist/types/lsp/lspmux.d.ts +5 -3
- package/dist/types/lsp/path-trust.d.ts +1 -0
- package/dist/types/main.d.ts +3 -2
- package/dist/types/memories/storage.d.ts +1 -0
- package/dist/types/modes/action-registry.d.ts +29 -0
- package/dist/types/modes/components/chrome.d.ts +14 -0
- package/dist/types/modes/components/command-palette.d.ts +27 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +2 -1
- package/dist/types/modes/components/pet-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
- package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
- package/dist/types/modes/components/queue-pane.d.ts +12 -0
- package/dist/types/modes/components/read-tool-group.d.ts +6 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
- package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
- package/dist/types/modes/components/settings-selector.d.ts +1 -1
- package/dist/types/modes/components/show-images-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/types.d.ts +1 -1
- package/dist/types/modes/components/status-line.d.ts +1 -94
- package/dist/types/modes/components/tasks-pane.d.ts +15 -0
- package/dist/types/modes/components/theme-selector.d.ts +1 -1
- package/dist/types/modes/components/tool-execution.d.ts +15 -0
- package/dist/types/modes/components/tool-status-header.d.ts +111 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
- package/dist/types/modes/controllers/input-controller.d.ts +8 -1
- package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
- package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
- package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
- package/dist/types/modes/interactive-mode.d.ts +21 -19
- package/dist/types/modes/jobs-observer.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
- package/dist/types/modes/tasks-aggregator.d.ts +39 -0
- package/dist/types/modes/transcript-item-registry.d.ts +60 -0
- package/dist/types/modes/types.d.ts +22 -15
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/sdk/broker/broker.d.ts +78 -12
- package/dist/types/sdk/broker/ensure.d.ts +9 -0
- package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
- package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
- package/dist/types/sdk/broker/session-index.d.ts +0 -1
- package/dist/types/sdk/broker/state-version.d.ts +2 -0
- package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
- package/dist/types/sdk/bus/config-commands.d.ts +1 -1
- package/dist/types/sdk/bus/config.d.ts +11 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
- package/dist/types/sdk/bus/html-format.d.ts +3 -2
- package/dist/types/sdk/bus/index.d.ts +27 -0
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/notification-service.d.ts +20 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
- package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
- package/dist/types/sdk/bus/rich-document.d.ts +7 -0
- package/dist/types/sdk/bus/rich-render.d.ts +18 -41
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
- package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/session-directory.d.ts +56 -0
- package/dist/types/session/agent-session.d.ts +82 -47
- package/dist/types/session/agent-storage.d.ts +7 -0
- package/dist/types/session/fallback-chain-controller.d.ts +10 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
- package/dist/types/session/messages.d.ts +16 -0
- package/dist/types/session/session-manager-internal.d.ts +14 -0
- package/dist/types/session/session-manager.d.ts +82 -12
- package/dist/types/session/session-storage.d.ts +43 -3
- package/dist/types/session/volatile-context-pruning.d.ts +15 -0
- package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
- package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
- package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
- package/dist/types/system-prompt.d.ts +2 -11
- package/dist/types/task/executor.d.ts +3 -1
- package/dist/types/thinking.d.ts +7 -0
- package/dist/types/tool-discovery/tool-index.d.ts +1 -50
- package/dist/types/tools/ask.d.ts +64 -2
- package/dist/types/tools/bash.d.ts +3 -0
- package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
- package/dist/types/tools/cron.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +1 -0
- package/dist/types/tools/index.d.ts +30 -15
- package/dist/types/tools/output-meta.d.ts +1 -0
- package/dist/types/tools/read.d.ts +3 -0
- package/dist/types/tools/search-tool-bm25.d.ts +3 -4
- package/dist/types/tools/subagent-render.d.ts +1 -0
- package/dist/types/tools/subagent.d.ts +6 -1
- package/dist/types/utils/event-bus.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +3 -1
- package/package.json +21 -9
- package/scripts/build-sdk-package-smoke.ts +39 -1
- package/scripts/generate-hotkeys-docs.ts +74 -0
- package/scripts/generate-sdk-operation-inventory.ts +64 -20
- package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
- package/src/async/job-manager.ts +455 -69
- package/src/cli/config-cli.ts +75 -2
- package/src/cli/notify-cli.ts +92 -26
- package/src/cli/session-picker.ts +20 -3
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli.ts +1 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/harness.ts +34 -106
- package/src/commands/launch.ts +6 -4
- package/src/commands/mcp-serve.ts +85 -8
- package/src/commands/mcp.ts +2 -2
- package/src/commands/notify.ts +8 -4
- package/src/commands/sdk.ts +61 -40
- package/src/commands/ultragoal.ts +4 -1
- package/src/config/atomic-yaml-patch.ts +80 -13
- package/src/config/file-lock.ts +105 -43
- package/src/config/keybindings.ts +64 -20
- package/src/config/model-auth.ts +9 -0
- package/src/config/model-bindings-applier.ts +139 -0
- package/src/config/model-discovery-manager.ts +224 -0
- package/src/config/model-equivalence.ts +66 -0
- package/src/config/model-registry.ts +169 -302
- package/src/config/model-resolver.ts +361 -369
- package/src/config/models-config-schema.ts +5 -4
- package/src/config/settings-schema.ts +159 -2
- package/src/config/settings.ts +458 -482
- package/src/coordinator-mcp/policy.ts +76 -11
- package/src/coordinator-mcp/question-gate-codec.ts +274 -0
- package/src/coordinator-mcp/question-state.ts +778 -0
- package/src/coordinator-mcp/server.ts +1702 -238
- package/src/debug/index.ts +1 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
- package/src/defaults/gjc/skills/team/SKILL.md +16 -0
- package/src/discovery/helpers.ts +1 -50
- package/src/edit/index.ts +64 -0
- package/src/eval/py/env.ts +26 -0
- package/src/eval/py/kernel.ts +5 -3
- package/src/extensibility/extensions/loader.ts +9 -1
- package/src/extensibility/extensions/runner.ts +12 -3
- package/src/extensibility/extensions/types.ts +39 -1
- package/src/extensibility/gjc-plugins/installer.ts +16 -16
- package/src/extensibility/hooks/runner.ts +2 -2
- package/src/extensibility/runtime-skill-discovery.ts +33 -13
- package/src/extensibility/shared-events.ts +2 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
- package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
- package/src/gjc-runtime/deep-interview-state.ts +317 -5
- package/src/gjc-runtime/goal-mode-request.ts +55 -8
- package/src/gjc-runtime/launch-tmux.ts +2 -5
- package/src/gjc-runtime/launch-worktree.ts +5 -5
- package/src/gjc-runtime/linux-proc.ts +71 -0
- package/src/gjc-runtime/ralplan-runtime.ts +74 -90
- package/src/gjc-runtime/session-resolution.ts +18 -3
- package/src/gjc-runtime/session-state-sidecar.ts +6 -107
- package/src/gjc-runtime/state-migrations.ts +1 -1
- package/src/gjc-runtime/state-runtime.ts +320 -249
- package/src/gjc-runtime/state-writer.ts +36 -40
- package/src/gjc-runtime/team-launch.ts +319 -0
- package/src/gjc-runtime/team-notify.ts +326 -0
- package/src/gjc-runtime/team-runtime.ts +1099 -1482
- package/src/gjc-runtime/team-store.ts +828 -0
- package/src/gjc-runtime/team-workers.ts +962 -0
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
- package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
- package/src/gjc-runtime/tmux-sessions.ts +4 -21
- package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
- package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
- package/src/gjc-runtime/ultragoal-guard.ts +83 -12
- package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
- package/src/gjc-runtime/workflow-cli-common.ts +30 -0
- package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
- package/src/gjc-runtime/workflow-manifest.ts +18 -0
- package/src/goals/continuation-timeout-guard.ts +79 -0
- package/src/goals/runtime.ts +7 -5
- package/src/goals/state.ts +18 -0
- package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
- package/src/harness-control-plane/phase-rollup.ts +4 -7
- package/src/harness-control-plane/receipt-ingest.ts +5 -0
- package/src/harness-control-plane/receipt-spool.ts +51 -7
- package/src/harness-control-plane/session-lease.ts +12 -130
- package/src/hindsight/backend.ts +26 -27
- package/src/hindsight/state.ts +101 -32
- package/src/hooks/skill-state.ts +17 -11
- package/src/internal-urls/docs-index.generated.ts +21 -18
- package/src/lsp/client.ts +1 -1
- package/src/lsp/config.ts +145 -26
- package/src/lsp/index.ts +1 -1
- package/src/lsp/lspmux.ts +39 -10
- package/src/lsp/path-trust.ts +81 -0
- package/src/main.ts +28 -8
- package/src/memories/index.ts +1 -0
- package/src/memories/storage.ts +6 -2
- package/src/modes/action-registry.ts +165 -0
- package/src/modes/components/chrome.ts +40 -0
- package/src/modes/components/command-palette.ts +168 -0
- package/src/modes/components/custom-editor.ts +1 -1
- package/src/modes/components/custom-provider-wizard.ts +51 -14
- package/src/modes/components/index.ts +2 -1
- package/src/modes/components/pet-selector.ts +11 -17
- package/src/modes/components/plan-preview-overlay.ts +295 -0
- package/src/modes/components/queue-mode-selector.ts +10 -27
- package/src/modes/components/queue-pane.ts +79 -0
- package/src/modes/components/read-tool-group.ts +13 -0
- package/src/modes/components/session-observer-overlay.ts +207 -772
- package/src/modes/components/sessions-dashboard.ts +181 -0
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +10 -24
- package/src/modes/components/status-line/types.ts +1 -1
- package/src/modes/components/status-line.ts +1 -969
- package/src/modes/components/tasks-pane.ts +73 -0
- package/src/modes/components/theme-selector.ts +11 -31
- package/src/modes/components/tool-execution.ts +22 -0
- package/src/modes/components/tool-status-header.ts +1056 -0
- package/src/modes/components/transcript-viewer-overlay.ts +384 -0
- package/src/modes/controllers/btw-controller.ts +1 -0
- package/src/modes/controllers/command-controller.ts +9 -15
- package/src/modes/controllers/event-controller.ts +3 -2
- package/src/modes/controllers/extension-ui-controller.ts +72 -25
- package/src/modes/controllers/goal-mode-controller.ts +474 -0
- package/src/modes/controllers/input-controller.ts +764 -229
- package/src/modes/controllers/mode-gate.ts +24 -0
- package/src/modes/controllers/plan-mode-controller.ts +517 -0
- package/src/modes/controllers/selector-controller.ts +225 -10
- package/src/modes/controllers/stt-controller.ts +125 -0
- package/src/modes/interactive-mode.ts +302 -1151
- package/src/modes/jobs-observer.ts +3 -0
- package/src/modes/runtime-init.ts +8 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
- package/src/modes/tasks-aggregator.ts +199 -0
- package/src/modes/transcript-item-registry.ts +229 -0
- package/src/modes/types.ts +47 -20
- package/src/modes/utils/ui-helpers.ts +17 -9
- package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
- package/src/prompts/agent-fragments/restricted-bash.md +1 -0
- package/src/prompts/agents/architect.md +2 -8
- package/src/prompts/agents/critic.md +2 -8
- package/src/prompts/agents/executor.md +3 -0
- package/src/prompts/agents/planner.md +4 -12
- package/src/prompts/system/custom-system-prompt.md +1 -23
- package/src/prompts/system/plan-mode-approved.md +8 -0
- package/src/prompts/system/system-prompt.md +17 -96
- package/src/prompts/tools/bash.md +4 -4
- package/src/prompts/tools/browser.md +15 -52
- package/src/prompts/tools/find.md +2 -2
- package/src/prompts/tools/hashline.md +13 -55
- package/src/prompts/tools/read.md +3 -6
- package/src/prompts/tools/search-tool-bm25.md +0 -2
- package/src/prompts/tools/search.md +2 -4
- package/src/prompts/tools/skill-discovery.md +1 -2
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-mcp/manager.ts +1 -1
- package/src/sdk/broker/broker.ts +206 -78
- package/src/sdk/broker/discovery.ts +21 -2
- package/src/sdk/broker/ensure.ts +85 -25
- package/src/sdk/broker/lifecycle-codec.ts +11 -0
- package/src/sdk/broker/lifecycle-ledger.ts +371 -74
- package/src/sdk/broker/lifecycle.ts +1399 -142
- package/src/sdk/broker/process-incarnation.ts +5 -13
- package/src/sdk/broker/session-index.ts +145 -56
- package/src/sdk/broker/state-version.ts +21 -0
- package/src/sdk/broker/transport.ts +1 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -28
- package/src/sdk/bus/chat-effect-journal.ts +14 -4
- package/src/sdk/bus/config-commands.ts +5 -4
- package/src/sdk/bus/config.ts +98 -0
- package/src/sdk/bus/conversation-store.ts +5 -29
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/discord-daemon.ts +25 -8
- package/src/sdk/bus/html-format.ts +20 -3
- package/src/sdk/bus/index.ts +834 -113
- package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
- package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
- package/src/sdk/bus/natives-augment.d.ts +26 -1
- package/src/sdk/bus/notification-service.ts +168 -20
- package/src/sdk/bus/rate-limit-pool.ts +58 -14
- package/src/sdk/bus/recent-activity.ts +188 -74
- package/src/sdk/bus/rich-document.ts +258 -0
- package/src/sdk/bus/rich-render.ts +62 -70
- package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
- package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
- package/src/sdk/bus/telegram-daemon.ts +1491 -247
- package/src/sdk/bus/threaded-inbound.ts +8 -3
- package/src/sdk/bus/threaded-render.ts +57 -1
- package/src/sdk/bus/topic-registry.ts +120 -13
- package/src/sdk/host/control/dispatch.ts +14 -2
- package/src/sdk/host/control/operations.ts +6 -1
- package/src/sdk/host/host.ts +31 -1
- package/src/sdk/host/query/handlers.ts +20 -22
- package/src/sdk/host/query/revision-store.ts +80 -57
- package/src/sdk/index.ts +1 -0
- package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
- package/src/sdk/session-directory.ts +141 -0
- package/src/sdk/session.ts +458 -479
- package/src/session/agent-session.ts +1901 -637
- package/src/session/agent-storage.ts +18 -0
- package/src/session/cache-economics.ts +25 -8
- package/src/session/fallback-chain-controller.ts +53 -3
- package/src/session/internal/managed-session-scope.ts +2226 -0
- package/src/session/internal/managed-session-storage.ts +453 -0
- package/src/session/messages.ts +91 -2
- package/src/session/session-manager-internal.ts +33 -0
- package/src/session/session-manager.ts +1112 -333
- package/src/session/session-storage.ts +444 -39
- package/src/session/volatile-context-pruning.ts +78 -0
- package/src/setup/credential-auto-import.ts +2 -1
- package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
- package/src/setup/provider-onboarding.ts +36 -61
- package/src/setup/provider-presets.json +5 -0
- package/src/skill-state/active-state.ts +3 -13
- package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
- package/src/skill-state/workflow-state-contract.ts +6 -41
- package/src/slash-commands/builtin-registry.ts +46 -11
- package/src/system-prompt.ts +37 -44
- package/src/task/agents.ts +19 -1
- package/src/task/executor.ts +90 -6
- package/src/task/index.ts +62 -37
- package/src/thinking.ts +21 -6
- package/src/tool-discovery/tool-index.ts +2 -134
- package/src/tools/ask.ts +157 -21
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/bash.ts +38 -0
- package/src/tools/browser/dead-tab-recovery.ts +78 -0
- package/src/tools/browser/tab-supervisor.ts +282 -36
- package/src/tools/computer.ts +10 -3
- package/src/tools/cron.ts +30 -16
- package/src/tools/eval.ts +1 -2
- package/src/tools/fetch.ts +11 -4
- package/src/tools/index.ts +76 -24
- package/src/tools/output-meta.ts +13 -5
- package/src/tools/read.ts +37 -1
- package/src/tools/resource-gc.ts +2 -2
- package/src/tools/search-tool-bm25.ts +11 -74
- package/src/tools/subagent-render.ts +19 -8
- package/src/tools/subagent.ts +84 -32
- package/src/utils/event-bus.ts +9 -4
- package/src/web/search/index.ts +3 -3
- package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
- package/src/discovery/plugin-dir-roots.ts +0 -28
- package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
|
@@ -63,7 +63,7 @@ export type ThreadedInboundDecision =
|
|
|
63
63
|
text: string;
|
|
64
64
|
updateId: number;
|
|
65
65
|
threadId: string;
|
|
66
|
-
messageId
|
|
66
|
+
messageId: number;
|
|
67
67
|
attachment?: InboundAttachment;
|
|
68
68
|
}
|
|
69
69
|
| { kind: "duplicate"; updateId: number }
|
|
@@ -118,7 +118,9 @@ export function decideThreadedInbound(update: InboundUpdate, ctx: ThreadedInboun
|
|
|
118
118
|
const sessionId = ctx.topicToSession(threadId);
|
|
119
119
|
if (sessionId === undefined) return { kind: "ignore", reason: "unknown_topic" };
|
|
120
120
|
|
|
121
|
-
if (typeof update.update_id !== "number"
|
|
121
|
+
if (typeof update.update_id !== "number" || !Number.isSafeInteger(update.update_id) || update.update_id < 0) {
|
|
122
|
+
return { kind: "ignore", reason: "missing_update_id" };
|
|
123
|
+
}
|
|
122
124
|
const updateId = update.update_id;
|
|
123
125
|
if (ctx.isDuplicate(updateId)) return { kind: "duplicate", updateId };
|
|
124
126
|
|
|
@@ -131,6 +133,9 @@ export function decideThreadedInbound(update: InboundUpdate, ctx: ThreadedInboun
|
|
|
131
133
|
const attachment = extractAttachment(message);
|
|
132
134
|
if (!text && attachment === undefined) return { kind: "ignore", reason: "empty_text" };
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
if (typeof message.message_id !== "number" || !Number.isSafeInteger(message.message_id) || message.message_id <= 0) {
|
|
137
|
+
return { kind: "ignore", reason: "missing_message_id" };
|
|
138
|
+
}
|
|
139
|
+
const messageId = message.message_id;
|
|
135
140
|
return { kind: "inject", sessionId, text, updateId, threadId, messageId, attachment };
|
|
136
141
|
}
|
|
@@ -45,6 +45,13 @@ export interface ThreadedSend {
|
|
|
45
45
|
richMarkdown?: string;
|
|
46
46
|
/** Live-turn raw markdown for opt-in draft streaming (set ONLY on non-finalized turn frames; never triggers rich-final promotion, which requires `lane === "finalized"`). */
|
|
47
47
|
richDraftMarkdown?: string;
|
|
48
|
+
/**
|
|
49
|
+
* True for a terminal tool_activity frame (phase completed/failed/cancelled/
|
|
50
|
+
* unknown). The daemon evicts the tool's `tool:<toolCallId>` live-message entry
|
|
51
|
+
* after delivering it, so completed tools do not accumulate unbounded across a
|
|
52
|
+
* long session (concurrent in-flight `started` entries are retained).
|
|
53
|
+
*/
|
|
54
|
+
terminal?: boolean;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
57
|
interface ThreadedFrame {
|
|
@@ -63,11 +70,18 @@ interface ThreadedFrame {
|
|
|
63
70
|
model?: unknown;
|
|
64
71
|
diff?: unknown;
|
|
65
72
|
cwd?: unknown;
|
|
66
|
-
// turn_stream
|
|
73
|
+
// turn_stream / reasoning_summary
|
|
67
74
|
phase?: unknown;
|
|
68
75
|
finalAnswer?: boolean;
|
|
69
76
|
text?: unknown;
|
|
70
77
|
messageRef?: unknown;
|
|
78
|
+
turnRef?: unknown;
|
|
79
|
+
// tool_activity
|
|
80
|
+
toolCallId?: unknown;
|
|
81
|
+
toolName?: unknown;
|
|
82
|
+
argsSummary?: unknown;
|
|
83
|
+
resultSummary?: unknown;
|
|
84
|
+
isError?: unknown;
|
|
71
85
|
// image_attachment / file_attachment
|
|
72
86
|
source?: unknown;
|
|
73
87
|
data?: unknown;
|
|
@@ -184,6 +198,48 @@ export function renderThreadedFrame(frame: ThreadedFrame): ThreadedSend | undefi
|
|
|
184
198
|
richDraftMarkdown: finalized ? undefined : raw,
|
|
185
199
|
};
|
|
186
200
|
}
|
|
201
|
+
case "tool_activity": {
|
|
202
|
+
const toolCallId = str(frame.toolCallId);
|
|
203
|
+
if (!toolCallId) return undefined;
|
|
204
|
+
const phase = str(frame.phase);
|
|
205
|
+
const status =
|
|
206
|
+
phase === "started"
|
|
207
|
+
? "started"
|
|
208
|
+
: phase === "completed" && frame.isError !== true
|
|
209
|
+
? "ok"
|
|
210
|
+
: phase === "completed" || phase === "failed"
|
|
211
|
+
? "error"
|
|
212
|
+
: phase === "cancelled"
|
|
213
|
+
? "cancelled"
|
|
214
|
+
: "unknown";
|
|
215
|
+
const argsSummary = str(frame.argsSummary);
|
|
216
|
+
const resultSummary = str(frame.resultSummary);
|
|
217
|
+
const lines = [`⚙ ${escapeHtml(truncate(str(frame.toolName) ?? "tool", 300))} — ${status}`];
|
|
218
|
+
if (argsSummary) lines.push(`args:\n${pre(truncate(argsSummary, 1200))}`);
|
|
219
|
+
if (resultSummary) lines.push(`result:\n${pre(truncate(resultSummary, 1200))}`);
|
|
220
|
+
return {
|
|
221
|
+
method: "sendMessage",
|
|
222
|
+
lane: phase === "started" ? "live" : "finalized",
|
|
223
|
+
text: finalizeTelegramHtml(lines.join("\n")),
|
|
224
|
+
coalesceKey: `tool:${toolCallId}`,
|
|
225
|
+
editable: true,
|
|
226
|
+
terminal: phase !== "started",
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
case "reasoning_summary": {
|
|
230
|
+
const text = str(frame.text);
|
|
231
|
+
if (!text) return undefined;
|
|
232
|
+
const turnRef = str(frame.turnRef);
|
|
233
|
+
return {
|
|
234
|
+
method: "sendMessage",
|
|
235
|
+
lane: "finalized",
|
|
236
|
+
// markdownToTelegramHtml escapes raw markdown itself; pre-escaping here
|
|
237
|
+
// would turn entities such as < into visible &lt; text.
|
|
238
|
+
text: finalizeTelegramHtml(markdownToTelegramHtml(text)),
|
|
239
|
+
coalesceKey: turnRef ? `reason:${turnRef}` : undefined,
|
|
240
|
+
editable: turnRef !== undefined,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
187
243
|
case "image_attachment": {
|
|
188
244
|
const data = str(frame.data);
|
|
189
245
|
if (!data) return undefined;
|
|
@@ -21,6 +21,8 @@ export interface TopicRecord {
|
|
|
21
21
|
identitySent: boolean;
|
|
22
22
|
/** Creation timestamp (ms epoch). */
|
|
23
23
|
createdAt: number;
|
|
24
|
+
/** First positive observation that the owning endpoint is stale, dead, or missing. */
|
|
25
|
+
orphanedAt?: number;
|
|
24
26
|
/** Last applied or observed Telegram topic title. */
|
|
25
27
|
name?: string;
|
|
26
28
|
/** Naming authority. Missing values are legacy daemon-owned records. */
|
|
@@ -35,12 +37,24 @@ export interface TopicRecord {
|
|
|
35
37
|
replayGeneration?: number;
|
|
36
38
|
/** Last SDK event sequence durably consumed within replayGeneration. */
|
|
37
39
|
replaySeq?: number;
|
|
40
|
+
/** Serialized authority epoch; a late create may commit only in its starting epoch. */
|
|
41
|
+
authorityEpoch?: number;
|
|
42
|
+
/** An uncertain delete fences future creation and inbound routing. */
|
|
43
|
+
authorityState?: "active" | "delete_pending";
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
/** Serialisable shape persisted to disk. */
|
|
41
47
|
export interface TopicRegistryState {
|
|
42
48
|
/** sessionId -> record. */
|
|
43
49
|
topics: Record<string, TopicRecord>;
|
|
50
|
+
/** Durable deletion epochs retained after a definite delete. */
|
|
51
|
+
fences?: Record<string, number>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isValidTopicId(value: unknown): value is string {
|
|
55
|
+
return (
|
|
56
|
+
typeof value === "string" && /^[1-9]\d*$/.test(value) && Number.isSafeInteger(Number(value)) && Number(value) > 0
|
|
57
|
+
);
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
export function emptyTopicRegistryState(): TopicRegistryState {
|
|
@@ -56,8 +70,12 @@ export class TopicRegistry {
|
|
|
56
70
|
private readonly topics: Map<string, TopicRecord>;
|
|
57
71
|
/** Maps topicId -> sessionId for fast inbound routing. */
|
|
58
72
|
private readonly byTopic = new Map<string, string>();
|
|
73
|
+
/** Persisted collisions are ambiguous and must never authorize inbound routing. */
|
|
74
|
+
readonly #ambiguousTopicIds = new Set<string>();
|
|
59
75
|
/** In-flight create promises, keyed by session, to dedupe concurrent creates. */
|
|
60
76
|
private readonly inflight = new Map<string, Promise<TopicRecord>>();
|
|
77
|
+
/** Monotonic authority epochs, including deletion fences for absent records. */
|
|
78
|
+
private readonly epochs = new Map<string, number>();
|
|
61
79
|
|
|
62
80
|
constructor(state: TopicRegistryState = emptyTopicRegistryState()) {
|
|
63
81
|
this.topics = new Map();
|
|
@@ -66,8 +84,12 @@ export class TopicRegistry {
|
|
|
66
84
|
|
|
67
85
|
/** Merge serialized state and normalize authority fields from older releases. */
|
|
68
86
|
load(state: TopicRegistryState): void {
|
|
87
|
+
for (const [sessionId, epoch] of Object.entries(state.fences ?? {})) {
|
|
88
|
+
if (Number.isSafeInteger(epoch) && epoch >= 0) this.epochs.set(sessionId, epoch);
|
|
89
|
+
}
|
|
90
|
+
|
|
69
91
|
for (const [sessionId, raw] of Object.entries(state.topics ?? {})) {
|
|
70
|
-
if (!raw ||
|
|
92
|
+
if (!raw || !isValidTopicId(raw.topicId)) continue;
|
|
71
93
|
const hasValidUserAuthority =
|
|
72
94
|
raw.nameOwner === "user" &&
|
|
73
95
|
typeof raw.name === "string" &&
|
|
@@ -87,14 +109,45 @@ export class TopicRegistry {
|
|
|
87
109
|
identitySent: raw.identitySent === true,
|
|
88
110
|
createdAt: typeof raw.createdAt === "number" ? raw.createdAt : 0,
|
|
89
111
|
...(typeof raw.name === "string" ? { name: raw.name } : {}),
|
|
112
|
+
...(typeof raw.orphanedAt === "number" && Number.isFinite(raw.orphanedAt) && raw.orphanedAt >= 0
|
|
113
|
+
? { orphanedAt: raw.orphanedAt }
|
|
114
|
+
: {}),
|
|
90
115
|
...(hasValidUserAuthority ? { nameOwner: "user" as const } : {}),
|
|
91
116
|
...(hasValidUserAuthority && raw.nameReconcilePending === true ? { nameReconcilePending: true } : {}),
|
|
92
117
|
...(hasValidUserAuthority ? { userNameUpdateId: raw.userNameUpdateId } : {}),
|
|
93
118
|
...(typeof raw.identityKey === "string" ? { identityKey: raw.identityKey } : {}),
|
|
94
119
|
...(hasValidReplayCursor ? { replayGeneration: raw.replayGeneration, replaySeq: raw.replaySeq } : {}),
|
|
120
|
+
...(Number.isSafeInteger(raw.authorityEpoch) && raw.authorityEpoch! >= 0
|
|
121
|
+
? { authorityEpoch: raw.authorityEpoch }
|
|
122
|
+
: {}),
|
|
123
|
+
...(raw.authorityState === "delete_pending" ? { authorityState: "delete_pending" as const } : {}),
|
|
95
124
|
};
|
|
125
|
+
this.epochs.set(sessionId, Math.max(this.epochs.get(sessionId) ?? 0, record.authorityEpoch ?? 0));
|
|
126
|
+
|
|
96
127
|
this.topics.set(sessionId, record);
|
|
97
|
-
|
|
128
|
+
}
|
|
129
|
+
this.rebuildInboundRoutes();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private rebuildInboundRoutes(): void {
|
|
133
|
+
this.byTopic.clear();
|
|
134
|
+
this.#ambiguousTopicIds.clear();
|
|
135
|
+
const activeByTopic = new Map<string, string>();
|
|
136
|
+
|
|
137
|
+
for (const [sessionId, record] of this.topics) {
|
|
138
|
+
if (record.authorityState === "delete_pending") {
|
|
139
|
+
this.#ambiguousTopicIds.add(record.topicId);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (activeByTopic.has(record.topicId)) {
|
|
143
|
+
this.#ambiguousTopicIds.add(record.topicId);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
activeByTopic.set(record.topicId, sessionId);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (const [topicId, sessionId] of activeByTopic) {
|
|
150
|
+
if (!this.#ambiguousTopicIds.has(topicId)) this.byTopic.set(topicId, sessionId);
|
|
98
151
|
}
|
|
99
152
|
}
|
|
100
153
|
|
|
@@ -119,22 +172,36 @@ export class TopicRegistry {
|
|
|
119
172
|
*/
|
|
120
173
|
async getOrCreateTopic(
|
|
121
174
|
sessionId: string,
|
|
122
|
-
create: () => Promise<
|
|
175
|
+
create: () => Promise<unknown>,
|
|
123
176
|
now: () => number = Date.now,
|
|
124
177
|
name?: string,
|
|
125
178
|
): Promise<TopicRecord> {
|
|
126
179
|
const existing = this.topics.get(sessionId);
|
|
180
|
+
if (existing?.authorityState === "delete_pending") throw new Error("topic authority is deletion-fenced");
|
|
127
181
|
if (existing) return existing;
|
|
128
|
-
// Concurrency guard: many session frames (identity/idle/turn/ask) can race
|
|
129
|
-
// to first-use the same session. Without this, each call passes the
|
|
130
|
-
// `existing` check before `create()` resolves and creates a DUPLICATE
|
|
131
|
-
// forum topic. Share a single in-flight create per session id.
|
|
132
182
|
const pending = this.inflight.get(sessionId);
|
|
133
183
|
if (pending) return pending;
|
|
184
|
+
const epoch = this.epochs.get(sessionId) ?? 0;
|
|
134
185
|
const promise = (async () => {
|
|
135
186
|
const topicId = await create();
|
|
136
|
-
|
|
187
|
+
if (!isValidTopicId(topicId)) throw new Error("createForumTopic: invalid message_thread_id");
|
|
188
|
+
const revoked = (this.epochs.get(sessionId) ?? 0) !== epoch;
|
|
189
|
+
const record: TopicRecord = {
|
|
190
|
+
topicId,
|
|
191
|
+
name,
|
|
192
|
+
identitySent: false,
|
|
193
|
+
createdAt: now(),
|
|
194
|
+
authorityEpoch: revoked ? (this.epochs.get(sessionId) ?? 0) : epoch,
|
|
195
|
+
...(revoked ? { authorityState: "delete_pending" as const } : {}),
|
|
196
|
+
};
|
|
137
197
|
this.topics.set(sessionId, record);
|
|
198
|
+
if (revoked) throw new Error("topic authority was revoked during creation");
|
|
199
|
+
if (this.#ambiguousTopicIds.has(topicId)) return record;
|
|
200
|
+
if (this.byTopic.has(topicId)) {
|
|
201
|
+
this.byTopic.delete(topicId);
|
|
202
|
+
this.#ambiguousTopicIds.add(topicId);
|
|
203
|
+
return record;
|
|
204
|
+
}
|
|
138
205
|
this.byTopic.set(topicId, sessionId);
|
|
139
206
|
return record;
|
|
140
207
|
})();
|
|
@@ -165,6 +232,21 @@ export class TopicRegistry {
|
|
|
165
232
|
record.identityKey = identityKey;
|
|
166
233
|
return true;
|
|
167
234
|
}
|
|
235
|
+
/** Start the orphan grace clock on the first positive liveness-loss observation. */
|
|
236
|
+
markOrphaned(sessionId: string, now: number): boolean {
|
|
237
|
+
const record = this.topics.get(sessionId);
|
|
238
|
+
if (!record || record.orphanedAt !== undefined) return false;
|
|
239
|
+
record.orphanedAt = now;
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Clear a prior orphan observation after the endpoint is positively live again. */
|
|
244
|
+
clearOrphaned(sessionId: string): boolean {
|
|
245
|
+
const record = this.topics.get(sessionId);
|
|
246
|
+
if (!record || record.orphanedAt === undefined) return false;
|
|
247
|
+
delete record.orphanedAt;
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
168
250
|
|
|
169
251
|
/** Last durably consumed SDK event cursor for reconnect replay. */
|
|
170
252
|
replayCursor(sessionId: string): { generation: number; seq: number } | undefined {
|
|
@@ -241,17 +323,42 @@ export class TopicRegistry {
|
|
|
241
323
|
record.nameReconcilePending = false;
|
|
242
324
|
}
|
|
243
325
|
|
|
244
|
-
/**
|
|
245
|
-
|
|
326
|
+
/** Fence new work before the remote delete starts, including an absent in-flight create. */
|
|
327
|
+
beginDelete(sessionId: string): TopicRecord | undefined {
|
|
246
328
|
const record = this.topics.get(sessionId);
|
|
247
|
-
|
|
329
|
+
const epoch = Math.max(this.epochs.get(sessionId) ?? 0, record?.authorityEpoch ?? 0) + 1;
|
|
330
|
+
this.epochs.set(sessionId, epoch);
|
|
331
|
+
if (!record) return undefined;
|
|
332
|
+
record.authorityEpoch = epoch;
|
|
333
|
+
record.authorityState = "delete_pending";
|
|
334
|
+
if (this.byTopic.get(record.topicId) === sessionId) this.byTopic.delete(record.topicId);
|
|
335
|
+
return record;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Wait for a revoked create to settle before admitting a later lifecycle epoch. */
|
|
339
|
+
async awaitInflight(sessionId: string): Promise<void> {
|
|
340
|
+
await this.inflight.get(sessionId)?.catch(() => undefined);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Remove only after a definite remote deletion; ambiguity deliberately retains its fence. */
|
|
344
|
+
settleDelete(sessionId: string, topicId: string): boolean {
|
|
345
|
+
const record = this.topics.get(sessionId);
|
|
346
|
+
if (!record || record.topicId !== topicId || record.authorityState !== "delete_pending") return false;
|
|
248
347
|
this.topics.delete(sessionId);
|
|
249
|
-
this.byTopic.delete(record.topicId);
|
|
250
348
|
return true;
|
|
251
349
|
}
|
|
252
350
|
|
|
351
|
+
/** Remove a topic record immediately for local/test cleanup compatibility. */
|
|
352
|
+
delete(sessionId: string): boolean {
|
|
353
|
+
const record = this.topics.get(sessionId);
|
|
354
|
+
if (!record) return false;
|
|
355
|
+
this.epochs.set(sessionId, Math.max(this.epochs.get(sessionId) ?? 0, record.authorityEpoch ?? 0) + 1);
|
|
356
|
+
if (this.byTopic.get(record.topicId) === sessionId) this.byTopic.delete(record.topicId);
|
|
357
|
+
return this.topics.delete(sessionId);
|
|
358
|
+
}
|
|
359
|
+
|
|
253
360
|
/** Serialise for atomic persistence beside the daemon state. */
|
|
254
361
|
serialize(): TopicRegistryState {
|
|
255
|
-
return { topics: Object.fromEntries(this.topics) };
|
|
362
|
+
return { topics: Object.fromEntries(this.topics), fences: Object.fromEntries(this.epochs) };
|
|
256
363
|
}
|
|
257
364
|
}
|
|
@@ -133,6 +133,7 @@ function invoke(
|
|
|
133
133
|
operation: string,
|
|
134
134
|
input: ControlInput,
|
|
135
135
|
confirm: boolean | undefined,
|
|
136
|
+
idempotencyKey: string | undefined,
|
|
136
137
|
): Promise<ControlValue> | ControlValue {
|
|
137
138
|
switch (operation) {
|
|
138
139
|
case "turn.prompt":
|
|
@@ -148,7 +149,12 @@ function invoke(
|
|
|
148
149
|
case "ask.answer":
|
|
149
150
|
return surface.answerAsk(text(input, "id"), input.answer);
|
|
150
151
|
case "workflow.gate_answer":
|
|
151
|
-
return surface.answerGate(
|
|
152
|
+
return surface.answerGate(
|
|
153
|
+
text(input, "id"),
|
|
154
|
+
input.response,
|
|
155
|
+
input.expectedSessionId as string | undefined,
|
|
156
|
+
idempotencyKey,
|
|
157
|
+
);
|
|
152
158
|
case "workflow.plan_approve":
|
|
153
159
|
return surface.approvePlan(text(input, "id"), input.choice, input.expectedSessionId as string | undefined);
|
|
154
160
|
case "skill.invoke":
|
|
@@ -276,7 +282,13 @@ async function execute(surface: ControlSurface, row: Operation, request: Control
|
|
|
276
282
|
return {
|
|
277
283
|
id: request.id,
|
|
278
284
|
ok: true,
|
|
279
|
-
result: await invoke(
|
|
285
|
+
result: await invoke(
|
|
286
|
+
surface,
|
|
287
|
+
row.sdkId,
|
|
288
|
+
request.input as ControlInput,
|
|
289
|
+
request.confirm,
|
|
290
|
+
request.idempotencyKey,
|
|
291
|
+
),
|
|
280
292
|
};
|
|
281
293
|
} catch (error) {
|
|
282
294
|
return errorResponse(request.id, row, error);
|
|
@@ -12,7 +12,12 @@ export interface ControlSurface {
|
|
|
12
12
|
abort(): Promise<ControlValue> | ControlValue;
|
|
13
13
|
abortAndPrompt(text: string): Promise<ControlValue> | ControlValue;
|
|
14
14
|
answerAsk(id: string, answer: ControlValue): Promise<ControlValue> | ControlValue;
|
|
15
|
-
answerGate(
|
|
15
|
+
answerGate(
|
|
16
|
+
id: string,
|
|
17
|
+
response: ControlValue,
|
|
18
|
+
expectedSessionId?: string,
|
|
19
|
+
idempotencyKey?: string,
|
|
20
|
+
): Promise<ControlValue> | ControlValue;
|
|
16
21
|
approvePlan(id: string, choice: ControlValue, expectedSessionId?: string): Promise<ControlValue> | ControlValue;
|
|
17
22
|
invokeSkill(name: string, args: ControlValue): Promise<ControlValue> | ControlValue;
|
|
18
23
|
setPlanMode(on: boolean): Promise<ControlValue> | ControlValue;
|
package/src/sdk/host/host.ts
CHANGED
|
@@ -14,8 +14,14 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
|
|
|
14
14
|
installProviderDefinitions?: (capability: string, definitions: unknown) => void;
|
|
15
15
|
onProviderDefinitionsRemoved?: (capability: string) => void;
|
|
16
16
|
onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
|
|
17
|
+
/** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */
|
|
18
|
+
connectionCapabilities?: (connectionId: string) => ReadonlySet<string>;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
const TOOL_ACTIVITY_V1 = "tool_activity_v1";
|
|
22
|
+
const CAP_GATED_FRAME_KINDS = new Set(["tool_activity", "reasoning_summary"]);
|
|
23
|
+
const EMPTY_CAPABILITIES: ReadonlySet<string> = new Set();
|
|
24
|
+
|
|
19
25
|
/** SDK hosting is independent of notification configuration. Only root sessions host an endpoint. */
|
|
20
26
|
export function shouldHostSdk(_settings: unknown, isTopLevel: boolean, env: NodeJS.ProcessEnv = process.env): boolean {
|
|
21
27
|
return isTopLevel && env.GJC_SDK_DISABLE !== "1";
|
|
@@ -175,6 +181,19 @@ export class SessionSdkHost {
|
|
|
175
181
|
await this.#options.sendFrame(connectionId, frame);
|
|
176
182
|
}
|
|
177
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Best-effort delivery for structured error frames. When the original failure
|
|
186
|
+
* was already a disconnected/dead connection, a second send must not escape
|
|
187
|
+
* the fire-and-forget `#onFrame` callback as an unhandled rejection.
|
|
188
|
+
*/
|
|
189
|
+
async #sendBestEffort(connectionId: string, frame: SdkFrame): Promise<void> {
|
|
190
|
+
try {
|
|
191
|
+
await this.#send(connectionId, frame);
|
|
192
|
+
} catch {
|
|
193
|
+
// Per-connection delivery only; never rethrow into fire-and-forget handlers.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
178
197
|
async #onFrame(connectionId: string, frame: SdkFrame): Promise<void> {
|
|
179
198
|
try {
|
|
180
199
|
switch (frame.type) {
|
|
@@ -200,11 +219,20 @@ export class SessionSdkHost {
|
|
|
200
219
|
const sinceGeneration = rawGeneration;
|
|
201
220
|
const sinceSeq = rawSeq;
|
|
202
221
|
const replay = this.events.replay(sinceSeq, sinceGeneration);
|
|
222
|
+
const capabilities = Array.isArray(frame.capabilities)
|
|
223
|
+
? new Set(
|
|
224
|
+
frame.capabilities.filter((capability): capability is string => typeof capability === "string"),
|
|
225
|
+
)
|
|
226
|
+
: (this.#options.connectionCapabilities?.(connectionId) ?? EMPTY_CAPABILITIES);
|
|
227
|
+
const events = replay.events.filter(
|
|
228
|
+
event => !CAP_GATED_FRAME_KINDS.has(String(event.kind)) || capabilities.has(TOOL_ACTIVITY_V1),
|
|
229
|
+
);
|
|
203
230
|
await this.#send(connectionId, {
|
|
204
231
|
type: "event_replay_result",
|
|
205
232
|
id,
|
|
206
233
|
ok: true,
|
|
207
234
|
...replay,
|
|
235
|
+
events,
|
|
208
236
|
generation: this.events.generation,
|
|
209
237
|
lastSeq: this.events.sequence,
|
|
210
238
|
});
|
|
@@ -283,7 +311,9 @@ export class SessionSdkHost {
|
|
|
283
311
|
return;
|
|
284
312
|
}
|
|
285
313
|
} catch (error) {
|
|
286
|
-
|
|
314
|
+
// Structured error delivery is best-effort: if the client already
|
|
315
|
+
// disconnected, do not escalate a second send failure process-wide.
|
|
316
|
+
await this.#sendBestEffort(connectionId, errorFrame(connectionId, frame, error));
|
|
287
317
|
}
|
|
288
318
|
}
|
|
289
319
|
#observeRequest(kind: "control" | "query", connectionId: string, frame: SdkFrame): void {
|
|
@@ -378,31 +378,24 @@ export class QueryHandlers {
|
|
|
378
378
|
const input = request.input ?? {};
|
|
379
379
|
const artifactId = String(input.artifactId ?? "");
|
|
380
380
|
const start = Math.max(0, Number(input.offset ?? 0));
|
|
381
|
-
const
|
|
381
|
+
const emptyResult = { artifactId, offset: start, bytes: "", complete: false };
|
|
382
|
+
const baseBytes = Buffer.byteLength(JSON.stringify({ id: request.id, ok: true, result: emptyResult }));
|
|
383
|
+
const maxRawBytes = Math.floor((RESPONSE_CEILING_BYTES - baseBytes) / 4) * 3;
|
|
382
384
|
const requested = Math.max(0, Math.min(Number(input.length ?? TARGET_PAGE_BYTES), maxRawBytes));
|
|
383
385
|
const artifact = await this.surface.getArtifactRange?.(artifactId, start, requested);
|
|
384
386
|
if (!artifact) return this.#error(request, "resource_gone");
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
};
|
|
392
|
-
while (
|
|
393
|
-
Buffer.byteLength(JSON.stringify({ id: request.id, ok: true, result })) >= RESPONSE_CEILING_BYTES &&
|
|
394
|
-
bytes.length > 0
|
|
395
|
-
) {
|
|
396
|
-
bytes = bytes.subarray(0, bytes.length - Math.max(1, Math.ceil(bytes.length / 128)));
|
|
397
|
-
result = {
|
|
387
|
+
const bytes = Buffer.from(artifact.bytes);
|
|
388
|
+
if (bytes.length === 0 && start < artifact.totalBytes) return this.#error(request, "item_too_large");
|
|
389
|
+
return {
|
|
390
|
+
id: request.id,
|
|
391
|
+
ok: true,
|
|
392
|
+
result: {
|
|
398
393
|
artifactId,
|
|
399
394
|
offset: start,
|
|
400
395
|
bytes: bytes.toString("base64"),
|
|
401
396
|
complete: start + bytes.length >= artifact.totalBytes,
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
if (bytes.length === 0 && start < artifact.totalBytes) return this.#error(request, "item_too_large");
|
|
405
|
-
return { id: request.id, ok: true, result };
|
|
397
|
+
},
|
|
398
|
+
};
|
|
406
399
|
}
|
|
407
400
|
|
|
408
401
|
async #paginate(
|
|
@@ -417,12 +410,17 @@ export class QueryHandlers {
|
|
|
417
410
|
): Promise<QueryResponse> {
|
|
418
411
|
const values = Array.isArray(snapshot) ? snapshot : [snapshot];
|
|
419
412
|
const items: unknown[] = [];
|
|
413
|
+
let itemsBytes = 2; // []
|
|
420
414
|
let index = offset;
|
|
421
415
|
while (index < values.length) {
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
items.
|
|
416
|
+
const item = values[index]!;
|
|
417
|
+
const itemBytes = Buffer.byteLength(JSON.stringify(item) ?? "null");
|
|
418
|
+
const candidateBytes = itemsBytes + itemBytes + (items.length ? 1 : 0);
|
|
419
|
+
if (candidateBytes > TARGET_PAGE_BYTES && items.length) break;
|
|
420
|
+
if (candidateBytes > RESPONSE_CEILING_BYTES) break;
|
|
421
|
+
items.push(item);
|
|
422
|
+
itemsBytes = candidateBytes;
|
|
423
|
+
index++;
|
|
426
424
|
}
|
|
427
425
|
const complete = index >= values.length;
|
|
428
426
|
const page: QueryPage = { items, complete, revision };
|