@adhdev/daemon-core 0.9.82-rc.49 → 0.9.82-rc.490
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/dist/boot/daemon-lifecycle.d.ts +9 -0
- package/dist/boot/process-hardening.d.ts +50 -0
- package/dist/build-info.d.ts +37 -0
- package/dist/chat/source-machine.d.ts +166 -0
- package/dist/chat/source-resolver.d.ts +104 -0
- package/dist/chat/subscription-updates.d.ts +1 -0
- package/dist/cli-adapter-types.d.ts +68 -2
- package/dist/cli-adapters/cli-script-runner.d.ts +117 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +220 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +137 -64
- package/dist/cli-adapters/provider-cli-parse.d.ts +5 -0
- package/dist/cli-adapters/provider-cli-runtime.d.ts +34 -0
- package/dist/cli-adapters/provider-cli-shared.d.ts +74 -0
- package/dist/cli-adapters/pty-write-chunking.d.ts +34 -0
- package/dist/cli-adapters/raw-terminal-io.d.ts +37 -0
- package/dist/cli-adapters/resolve-executable.d.ts +69 -0
- package/dist/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +6 -3
- package/dist/cli-adapters/terminal-backends/types.d.ts +10 -2
- package/dist/cli-adapters/terminal-screen.d.ts +4 -11
- package/dist/commands/chat-commands-debug-bundle.d.ts +14 -0
- package/dist/commands/chat-commands-read.d.ts +23 -0
- package/dist/commands/chat-commands-scope.d.ts +39 -0
- package/dist/commands/chat-commands-shared.d.ts +33 -0
- package/dist/commands/chat-commands-write.d.ts +14 -0
- package/dist/commands/chat-commands.d.ts +10 -23
- package/dist/commands/cli-manager.d.ts +71 -1
- package/dist/commands/handler.d.ts +125 -0
- package/dist/commands/high-family/index.d.ts +3 -0
- package/dist/commands/high-family/mesh-coordinator-launch.d.ts +2 -0
- package/dist/commands/high-family/mesh-events.d.ts +2 -0
- package/dist/commands/high-family/mesh-status.d.ts +2 -0
- package/dist/commands/high-family/types.d.ts +64 -0
- package/dist/commands/low-family/coordinator-prompt.d.ts +9 -0
- package/dist/commands/low-family/daemon-lifecycle.d.ts +2 -0
- package/dist/commands/low-family/diagnostics.d.ts +2 -0
- package/dist/commands/low-family/index.d.ts +3 -0
- package/dist/commands/low-family/mesh-ledger.d.ts +10 -0
- package/dist/commands/low-family/mesh-node-logs.d.ts +2 -0
- package/dist/commands/low-family/notification.d.ts +2 -0
- package/dist/commands/low-family/refine-config.d.ts +2 -0
- package/dist/commands/low-family/session-host.d.ts +2 -0
- package/dist/commands/low-family/spec-providerdev.d.ts +11 -0
- package/dist/commands/low-family/status-meta.d.ts +2 -0
- package/dist/commands/low-family/types.d.ts +33 -0
- package/dist/commands/med-family/cli-agent.d.ts +2 -0
- package/dist/commands/med-family/fast-forward.d.ts +2 -0
- package/dist/commands/med-family/ide.d.ts +10 -0
- package/dist/commands/med-family/index.d.ts +3 -0
- package/dist/commands/med-family/mesh-crud.d.ts +48 -0
- package/dist/commands/med-family/mesh-host-pairing.d.ts +2 -0
- package/dist/commands/med-family/mesh-queue.d.ts +2 -0
- package/dist/commands/med-family/mesh-restart.d.ts +2 -0
- package/dist/commands/med-family/types.d.ts +152 -0
- package/dist/commands/mesh-coordinator.d.ts +85 -1
- package/dist/commands/router-aggregate-status.d.ts +24 -0
- package/dist/commands/router-mesh-session-owner.d.ts +59 -0
- package/dist/commands/router-refine.d.ts +151 -0
- package/dist/commands/router-worktree-cleanup.d.ts +133 -0
- package/dist/commands/router.d.ts +203 -15
- package/dist/commands/upgrade-helper.d.ts +41 -1
- package/dist/config/chat-history.d.ts +11 -0
- package/dist/config/config.d.ts +19 -0
- package/dist/config/mesh-config.d.ts +65 -0
- package/dist/config/mesh-json-config.d.ts +199 -0
- package/dist/config/registry-resolver.d.ts +54 -0
- package/dist/config/repo-settings.d.ts +77 -0
- package/dist/config/state-store.d.ts +30 -0
- package/dist/daemon/dev-server.d.ts +0 -2
- package/dist/detection/cli-detector.d.ts +17 -0
- package/dist/detection/ide-detector.d.ts +13 -0
- package/dist/detection/win32-ide-version.d.ts +37 -0
- package/dist/git/change-impact-config.d.ts +159 -0
- package/dist/git/git-commands.d.ts +26 -2
- package/dist/git/git-diff.d.ts +6 -0
- package/dist/git/git-executor.d.ts +11 -0
- package/dist/git/git-status.d.ts +80 -0
- package/dist/git/git-types.d.ts +2 -50
- package/dist/git/git-worktree.d.ts +71 -1
- package/dist/git/index.d.ts +3 -1
- package/dist/index.d.ts +70 -17
- package/dist/index.js +53532 -19073
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +53283 -18989
- package/dist/index.mjs.map +1 -1
- package/dist/installer.d.ts +1 -4
- package/dist/ipc/local-ipc-server.d.ts +91 -0
- package/dist/launch.d.ts +1 -1
- package/dist/logging/async-batch-writer.d.ts +10 -0
- package/dist/logging/debug-config.d.ts +16 -0
- package/dist/logging/log-redactor.d.ts +24 -0
- package/dist/logging/log-tail-reader.d.ts +81 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/mesh/contracts.d.ts +256 -0
- package/dist/mesh/coordinator-prompt.d.ts +76 -0
- package/dist/mesh/coordinator-registry.d.ts +59 -0
- package/dist/mesh/mesh-active-work.d.ts +203 -0
- package/dist/mesh/mesh-clone-grace.d.ts +8 -0
- package/dist/mesh/mesh-completion-synthesis.d.ts +4 -0
- package/dist/mesh/mesh-coordinator-config.d.ts +21 -0
- package/dist/mesh/mesh-delivery-policy.d.ts +104 -0
- package/dist/mesh/mesh-event-classify.d.ts +7 -0
- package/dist/mesh/mesh-event-forwarding.d.ts +58 -0
- package/dist/mesh/mesh-event-trace.d.ts +21 -0
- package/dist/mesh/mesh-events-coordinator.d.ts +4 -0
- package/dist/mesh/mesh-events-pending.d.ts +258 -0
- package/dist/mesh/mesh-events-stale.d.ts +61 -0
- package/dist/mesh/mesh-events-utils.d.ts +75 -0
- package/dist/mesh/mesh-events.d.ts +6 -49
- package/dist/mesh/mesh-fast-forward.d.ts +81 -0
- package/dist/mesh/mesh-host-ownership.d.ts +21 -1
- package/dist/mesh/mesh-init.d.ts +128 -0
- package/dist/mesh/mesh-ledger-reconciliation.d.ts +23 -4
- package/dist/mesh/mesh-ledger.d.ts +139 -1
- package/dist/mesh/mesh-magi-status.d.ts +63 -0
- package/dist/mesh/mesh-missions.d.ts +281 -0
- package/dist/mesh/mesh-node-identity.d.ts +310 -0
- package/dist/mesh/mesh-queue-assignment.d.ts +162 -0
- package/dist/mesh/mesh-reconcile-acked-hold.d.ts +17 -0
- package/dist/mesh/mesh-reconcile-config.d.ts +6 -0
- package/dist/mesh/mesh-reconcile-identity.d.ts +6 -0
- package/dist/mesh/mesh-reconcile-loop.d.ts +84 -0
- package/dist/mesh/mesh-reconcile-v2-backstop.d.ts +16 -0
- package/dist/mesh/mesh-refine-batch.d.ts +68 -0
- package/dist/mesh/mesh-refine-gates.d.ts +457 -0
- package/dist/mesh/mesh-refine-status.d.ts +62 -0
- package/dist/mesh/mesh-remote-event-pull.d.ts +15 -0
- package/dist/mesh/mesh-review-inbox.d.ts +90 -0
- package/dist/mesh/mesh-routing.d.ts +70 -0
- package/dist/mesh/mesh-runtime-store.d.ts +567 -0
- package/dist/mesh/mesh-scheduling-runtime.d.ts +78 -0
- package/dist/mesh/mesh-task-inflight.d.ts +46 -0
- package/dist/mesh/mesh-task-stats.d.ts +49 -0
- package/dist/mesh/mesh-unresolved-forward-outbox.d.ts +34 -0
- package/dist/mesh/mesh-warmup-deadline.d.ts +68 -0
- package/dist/mesh/mesh-work-queue.d.ts +392 -1
- package/dist/mesh/preview-freshness.d.ts +28 -0
- package/dist/mesh/refine-config.d.ts +97 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +219 -0
- package/dist/providers/acp-provider-instance.d.ts +5 -0
- package/dist/providers/approval-utils.d.ts +46 -0
- package/dist/providers/chat-message-normalization.d.ts +58 -1
- package/dist/providers/cli-provider-effect-format.d.ts +30 -0
- package/dist/providers/cli-provider-history-dedup.d.ts +17 -0
- package/dist/providers/cli-provider-input-prompt.d.ts +12 -0
- package/dist/providers/cli-provider-instance-types.d.ts +45 -0
- package/dist/providers/cli-provider-instance.d.ts +426 -30
- package/dist/providers/cli-provider-status-helpers.d.ts +46 -0
- package/dist/providers/cli-provider-transcript-merge.d.ts +7 -0
- package/dist/providers/contracts.d.ts +196 -6
- package/dist/providers/external-sources.d.ts +71 -0
- package/dist/providers/manual-attendance.d.ts +79 -0
- package/dist/providers/native-history/antigravity-claim-registry.d.ts +39 -0
- package/dist/providers/native-history/antigravity-cli-transcript.d.ts +137 -0
- package/dist/providers/native-history/claude-cli-transcript.d.ts +70 -0
- package/dist/providers/native-history/codex-cli-transcript.d.ts +73 -0
- package/dist/providers/native-history/constants.d.ts +12 -0
- package/dist/providers/native-history/dispatcher.d.ts +44 -0
- package/dist/providers/native-history/hermes-cli-transcript.d.ts +30 -0
- package/dist/providers/native-history/index.d.ts +13 -0
- package/dist/providers/provider-instance-manager.d.ts +30 -0
- package/dist/providers/provider-instance.d.ts +31 -1
- package/dist/providers/provider-loader.d.ts +42 -5
- package/dist/providers/provider-trust.d.ts +31 -0
- package/dist/providers/read-chat-contract.d.ts +29 -0
- package/dist/providers/sdk/v1/builders/acp/detect-status.d.ts +68 -0
- package/dist/providers/sdk/v1/builders/cli/detect-status.d.ts +86 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval-squash.d.ts +59 -0
- package/dist/providers/sdk/v1/builders/cli/parse-approval.d.ts +65 -0
- package/dist/providers/sdk/v1/builders/cli/parse-session.d.ts +91 -0
- package/dist/providers/sdk/v1/builders/cli/visible-region.d.ts +42 -0
- package/dist/providers/sdk/v1/fixture-tooling/format.d.ts +126 -0
- package/dist/providers/sdk/v1/fixture-tooling/index.d.ts +8 -0
- package/dist/providers/sdk/v1/fixture-tooling/replay.d.ts +38 -0
- package/dist/providers/sdk/v1/index.d.ts +30 -0
- package/dist/providers/sdk/v1/sandbox/README-design.d.ts +193 -0
- package/dist/providers/sdk/v1/sandbox/require-whitelist.d.ts +74 -0
- package/dist/providers/sdk/v1/sandbox/script-runner.d.ts +98 -0
- package/dist/providers/sdk/v1/types/cli/index.d.ts +277 -0
- package/dist/providers/sdk/v1/types/common/index.d.ts +203 -0
- package/dist/providers/sdk/v1/validators/index.d.ts +5 -0
- package/dist/providers/sdk/v1/validators/manifest.d.ts +40 -0
- package/dist/providers/sdk/v1/validators/taint.d.ts +52 -0
- package/dist/providers/spec/adapter.d.ts +98 -0
- package/dist/providers/spec/cli-adapter.d.ts +223 -0
- package/dist/providers/spec/evaluator.d.ts +45 -0
- package/dist/providers/spec/fsm-driver.d.ts +449 -0
- package/dist/providers/spec/fsm-evaluator.d.ts +93 -0
- package/dist/providers/spec/fsm-loader.d.ts +14 -0
- package/dist/providers/spec/fsm-types.d.ts +202 -0
- package/dist/providers/spec/native-history-executor.d.ts +41 -0
- package/dist/providers/spec/pre-launch-trust.d.ts +16 -0
- package/dist/providers/spec/route.d.ts +4 -0
- package/dist/providers/spec/types.d.ts +244 -0
- package/dist/providers/status-monitor.d.ts +7 -7
- package/dist/providers/transcript-v2.d.ts +176 -0
- package/dist/providers/types/interactive-prompt.d.ts +84 -0
- package/dist/providers/working-dir.d.ts +17 -0
- package/dist/repo-mesh-types.d.ts +616 -12
- package/dist/runtime-defaults.d.ts +2 -0
- package/dist/session-host/managed-host.d.ts +64 -0
- package/dist/session-host/runtime-surface.d.ts +10 -16
- package/dist/sessions/registry.d.ts +26 -0
- package/dist/shared-types-extra.d.ts +2 -4
- package/dist/shared-types.d.ts +110 -55
- package/dist/status/chat-tail-hot-sessions.d.ts +40 -0
- package/dist/status/normalize.d.ts +1 -1
- package/dist/status/normalize.js +1 -0
- package/dist/status/normalize.js.map +1 -1
- package/dist/status/normalize.mjs +1 -0
- package/dist/status/normalize.mjs.map +1 -1
- package/dist/status/reporter.d.ts +2 -0
- package/dist/status/snapshot.d.ts +27 -0
- package/dist/system/hash.d.ts +8 -0
- package/dist/system/load-better-sqlite3.d.ts +21 -0
- package/dist/types.d.ts +20 -6
- package/package.json +11 -4
- package/src/agent-stream/poller.ts +2 -3
- package/src/agent-stream/provider-adapter.ts +1 -1
- package/src/boot/daemon-lifecycle.ts +80 -14
- package/src/boot/process-hardening.ts +89 -0
- package/src/build-info.ts +73 -0
- package/src/chat/source-machine.ts +534 -0
- package/src/chat/source-resolver.ts +0 -0
- package/src/chat/subscription-updates.ts +20 -1
- package/src/cli-adapter-types.d.ts +3 -1
- package/src/cli-adapter-types.ts +68 -2
- package/src/cli-adapters/cli-script-runner.ts +421 -0
- package/src/cli-adapters/cli-state-engine.ts +1282 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +1048 -1143
- package/src/cli-adapters/provider-cli-parse.d.ts +2 -0
- package/src/cli-adapters/provider-cli-parse.ts +19 -0
- package/src/cli-adapters/provider-cli-runtime.ts +56 -7
- package/src/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/src/cli-adapters/provider-cli-shared.ts +121 -11
- package/src/cli-adapters/pty-transport.ts +2 -1
- package/src/cli-adapters/pty-write-chunking.ts +106 -0
- package/src/cli-adapters/raw-terminal-io.ts +252 -0
- package/src/cli-adapters/resolve-executable.ts +204 -0
- package/src/cli-adapters/session-host-transport.ts +2 -1
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.ts +63 -29
- package/src/cli-adapters/terminal-backends/types.ts +10 -3
- package/src/cli-adapters/terminal-screen.ts +16 -81
- package/src/commands/chat-commands-debug-bundle.ts +398 -0
- package/src/commands/chat-commands-read.ts +2788 -0
- package/src/commands/chat-commands-scope.ts +54 -0
- package/src/commands/chat-commands-shared.ts +114 -0
- package/src/commands/chat-commands-write.ts +891 -0
- package/src/commands/chat-commands.ts +19 -1841
- package/src/commands/cli-manager.d.ts +2 -0
- package/src/commands/cli-manager.ts +724 -27
- package/src/commands/handler.ts +865 -2
- package/src/commands/high-family/index.ts +28 -0
- package/src/commands/high-family/mesh-coordinator-launch.ts +696 -0
- package/src/commands/high-family/mesh-events.ts +102 -0
- package/src/commands/high-family/mesh-status.ts +857 -0
- package/src/commands/high-family/types.ts +80 -0
- package/src/commands/low-family/coordinator-prompt.ts +125 -0
- package/src/commands/low-family/daemon-lifecycle.ts +120 -0
- package/src/commands/low-family/diagnostics.ts +57 -0
- package/src/commands/low-family/index.ts +37 -0
- package/src/commands/low-family/mesh-ledger.ts +62 -0
- package/src/commands/low-family/mesh-node-logs.ts +87 -0
- package/src/commands/low-family/notification.ts +116 -0
- package/src/commands/low-family/refine-config.ts +106 -0
- package/src/commands/low-family/session-host.ts +282 -0
- package/src/commands/low-family/spec-providerdev.ts +217 -0
- package/src/commands/low-family/status-meta.ts +112 -0
- package/src/commands/low-family/types.ts +39 -0
- package/src/commands/med-family/cli-agent.ts +270 -0
- package/src/commands/med-family/fast-forward.ts +230 -0
- package/src/commands/med-family/ide.ts +163 -0
- package/src/commands/med-family/index.ts +37 -0
- package/src/commands/med-family/mesh-crud.ts +1268 -0
- package/src/commands/med-family/mesh-host-pairing.ts +234 -0
- package/src/commands/med-family/mesh-queue.ts +167 -0
- package/src/commands/med-family/mesh-restart.ts +92 -0
- package/src/commands/med-family/types.ts +153 -0
- package/src/commands/mesh-coordinator.ts +334 -124
- package/src/commands/router-aggregate-status.ts +209 -0
- package/src/commands/router-mesh-session-owner.ts +114 -0
- package/src/commands/router-refine.ts +1794 -0
- package/src/commands/router-worktree-cleanup.ts +870 -0
- package/src/commands/router.ts +567 -4149
- package/src/commands/stream-commands.ts +8 -0
- package/src/commands/upgrade-helper.ts +310 -45
- package/src/config/chat-history.ts +517 -24
- package/src/config/config.ts +30 -0
- package/src/config/mesh-config.ts +300 -24
- package/src/config/mesh-json-config.ts +376 -0
- package/src/config/recent-activity.ts +8 -2
- package/src/config/registry-resolver.ts +100 -0
- package/src/config/repo-settings.ts +111 -0
- package/src/config/state-store.ts +55 -0
- package/src/daemon/dev-auto-implement.ts +3 -2
- package/src/daemon/dev-cli-debug.ts +10 -1
- package/src/daemon/dev-server.ts +0 -541
- package/src/detection/cli-detector.ts +94 -9
- package/src/detection/ide-detector.ts +55 -16
- package/src/detection/win32-ide-version.ts +106 -0
- package/src/git/change-impact-config.ts +354 -0
- package/src/git/git-commands.ts +112 -16
- package/src/git/git-diff.ts +81 -11
- package/src/git/git-executor.ts +12 -0
- package/src/git/git-status.ts +822 -48
- package/src/git/git-types.ts +14 -62
- package/src/git/git-worktree.ts +261 -4
- package/src/git/index.ts +17 -0
- package/src/index.ts +190 -13
- package/src/installer.d.ts +1 -1
- package/src/installer.ts +8 -6
- package/src/ipc/local-ipc-server.ts +278 -0
- package/src/launch.d.ts +1 -1
- package/src/launch.ts +37 -28
- package/src/logging/async-batch-writer.ts +55 -0
- package/src/logging/command-log.ts +7 -5
- package/src/logging/debug-config.ts +25 -0
- package/src/logging/debug-trace.ts +7 -2
- package/src/logging/log-redactor.ts +100 -0
- package/src/logging/log-tail-reader.ts +341 -0
- package/src/logging/logger.ts +14 -7
- package/src/mesh/contracts.ts +510 -0
- package/src/mesh/coordinator-prompt.ts +627 -34
- package/src/mesh/coordinator-registry.ts +121 -0
- package/src/mesh/mesh-active-work.ts +706 -0
- package/src/mesh/mesh-clone-grace.ts +68 -0
- package/src/mesh/mesh-completion-synthesis.ts +416 -0
- package/src/mesh/mesh-coordinator-config.ts +97 -0
- package/src/mesh/mesh-delivery-policy.ts +284 -0
- package/src/mesh/mesh-event-classify.ts +78 -0
- package/src/mesh/mesh-event-forwarding.ts +2030 -0
- package/src/mesh/mesh-event-trace.ts +67 -0
- package/src/mesh/mesh-events-coordinator.ts +32 -0
- package/src/mesh/mesh-events-pending.ts +1548 -0
- package/src/mesh/mesh-events-stale.ts +452 -0
- package/src/mesh/mesh-events-utils.ts +443 -0
- package/src/mesh/mesh-events.ts +43 -1035
- package/src/mesh/mesh-fast-forward.ts +856 -0
- package/src/mesh/mesh-host-ownership.ts +41 -3
- package/src/mesh/mesh-init.ts +350 -0
- package/src/mesh/mesh-ledger-reconciliation.ts +12 -7
- package/src/mesh/mesh-ledger.ts +897 -102
- package/src/mesh/mesh-magi-status.ts +223 -0
- package/src/mesh/mesh-missions.ts +695 -0
- package/src/mesh/mesh-node-identity.ts +2065 -0
- package/src/mesh/mesh-queue-assignment.ts +2498 -0
- package/src/mesh/mesh-reconcile-acked-hold.ts +230 -0
- package/src/mesh/mesh-reconcile-config.ts +66 -0
- package/src/mesh/mesh-reconcile-identity.ts +103 -0
- package/src/mesh/mesh-reconcile-loop.ts +1696 -0
- package/src/mesh/mesh-reconcile-v2-backstop.ts +62 -0
- package/src/mesh/mesh-refine-batch.ts +205 -0
- package/src/mesh/mesh-refine-gates.ts +1792 -0
- package/src/mesh/mesh-refine-status.ts +231 -0
- package/src/mesh/mesh-remote-event-pull.ts +279 -0
- package/src/mesh/mesh-review-inbox.ts +308 -0
- package/src/mesh/mesh-routing.ts +291 -0
- package/src/mesh/mesh-runtime-store.ts +2292 -0
- package/src/mesh/mesh-scheduling-runtime.ts +198 -0
- package/src/mesh/mesh-task-inflight.ts +70 -0
- package/src/mesh/mesh-task-stats.ts +161 -0
- package/src/mesh/mesh-unresolved-forward-outbox.ts +215 -0
- package/src/mesh/mesh-warmup-deadline.ts +152 -0
- package/src/mesh/mesh-work-queue.ts +1489 -145
- package/src/mesh/preview-freshness.ts +163 -0
- package/src/mesh/refine-config.ts +129 -12
- package/src/mesh/worktree-bootstrap-config.ts +599 -0
- package/src/providers/acp-provider-instance.ts +43 -10
- package/src/providers/approval-utils.d.ts +5 -0
- package/src/providers/approval-utils.ts +100 -6
- package/src/providers/chat-message-normalization.ts +126 -4
- package/src/providers/cli-provider-effect-format.ts +53 -0
- package/src/providers/cli-provider-history-dedup.ts +75 -0
- package/src/providers/cli-provider-input-prompt.ts +133 -0
- package/src/providers/cli-provider-instance-types.ts +131 -0
- package/src/providers/cli-provider-instance.ts +2771 -499
- package/src/providers/cli-provider-status-helpers.ts +123 -0
- package/src/providers/cli-provider-transcript-merge.ts +114 -0
- package/src/providers/contracts.d.ts +55 -0
- package/src/providers/contracts.ts +207 -6
- package/src/providers/extension-provider-instance.ts +12 -7
- package/src/providers/external-sources.ts +218 -0
- package/src/providers/ide-provider-instance.ts +35 -12
- package/src/providers/manual-attendance.ts +105 -0
- package/src/providers/native-history/antigravity-claim-registry.ts +138 -0
- package/src/providers/native-history/antigravity-cli-transcript.ts +1275 -0
- package/src/providers/native-history/claude-cli-transcript.ts +396 -0
- package/src/providers/native-history/codex-cli-transcript.ts +479 -0
- package/src/providers/native-history/constants.ts +19 -0
- package/src/providers/native-history/dispatcher.ts +617 -0
- package/src/providers/native-history/hermes-cli-transcript.ts +312 -0
- package/src/providers/native-history/index.ts +30 -0
- package/src/providers/provider-instance-manager.ts +71 -0
- package/src/providers/provider-instance.ts +24 -1
- package/src/providers/provider-loader.ts +700 -55
- package/src/providers/provider-schema.ts +93 -14
- package/src/providers/provider-trust.ts +114 -0
- package/src/providers/read-chat-contract.ts +76 -16
- package/src/providers/sdk/README.md +49 -0
- package/src/providers/sdk/v1/builders/acp/detect-status.ts +144 -0
- package/src/providers/sdk/v1/builders/cli/detect-status.ts +313 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval-squash.ts +158 -0
- package/src/providers/sdk/v1/builders/cli/parse-approval.ts +261 -0
- package/src/providers/sdk/v1/builders/cli/parse-session.ts +276 -0
- package/src/providers/sdk/v1/builders/cli/visible-region.ts +143 -0
- package/src/providers/sdk/v1/fixture-tooling/format.ts +130 -0
- package/src/providers/sdk/v1/fixture-tooling/index.ts +22 -0
- package/src/providers/sdk/v1/fixture-tooling/replay.ts +352 -0
- package/src/providers/sdk/v1/index.ts +152 -0
- package/src/providers/sdk/v1/sandbox/README-design.ts +195 -0
- package/src/providers/sdk/v1/sandbox/require-whitelist.ts +472 -0
- package/src/providers/sdk/v1/sandbox/script-runner.ts +150 -0
- package/src/providers/sdk/v1/schemas/cli/provider.schema.json +538 -0
- package/src/providers/sdk/v1/schemas/primitives/acp-session-protocol-v1.json +131 -0
- package/src/providers/sdk/v1/schemas/primitives/native-history-codex-rollout-v1.json +66 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-approval-squash-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-assistant-block-v1.json +91 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-cue-ordering-v1.json +47 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-dispatch-order-v1.json +32 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-footer-chrome-v1.json +42 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-index-finder-v1.json +27 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-modal-v1.json +125 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-prompt-marker-v1.json +45 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-session-id-extraction-v1.json +46 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-settled-prompt-v1.json +71 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-spinner-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-transcript-pty-v1.json +83 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-visible-region-v1.json +57 -0
- package/src/providers/sdk/v1/schemas/primitives/tui-welcome-screen-v1.json +35 -0
- package/src/providers/sdk/v1/types/cli/index.ts +365 -0
- package/src/providers/sdk/v1/types/common/index.ts +229 -0
- package/src/providers/sdk/v1/validators/index.ts +19 -0
- package/src/providers/sdk/v1/validators/manifest.ts +110 -0
- package/src/providers/sdk/v1/validators/taint.ts +309 -0
- package/src/providers/spec/adapter.ts +246 -0
- package/src/providers/spec/cli-adapter.ts +1064 -0
- package/src/providers/spec/evaluator.ts +407 -0
- package/src/providers/spec/fsm-driver.ts +1528 -0
- package/src/providers/spec/fsm-evaluator.ts +290 -0
- package/src/providers/spec/fsm-loader.ts +128 -0
- package/src/providers/spec/fsm-types.ts +301 -0
- package/src/providers/spec/native-history-executor.ts +1241 -0
- package/src/providers/spec/pre-launch-trust.ts +104 -0
- package/src/providers/spec/route.ts +51 -0
- package/src/providers/spec/types.ts +284 -0
- package/src/providers/status-monitor.d.ts +7 -7
- package/src/providers/status-monitor.ts +37 -22
- package/src/providers/transcript-v2.ts +567 -0
- package/src/providers/types/interactive-prompt.ts +536 -0
- package/src/providers/version-archive.ts +64 -24
- package/src/providers/working-dir.ts +23 -0
- package/src/repo-mesh-types.ts +861 -14
- package/src/runtime-defaults.ts +39 -0
- package/src/session-host/managed-host.ts +218 -0
- package/src/session-host/runtime-surface.ts +20 -80
- package/src/sessions/registry.ts +44 -0
- package/src/shared-types-extra.ts +2 -4
- package/src/shared-types.d.ts +8 -0
- package/src/shared-types.ts +148 -55
- package/src/status/builders.ts +69 -6
- package/src/status/chat-tail-hot-sessions.ts +117 -2
- package/src/status/normalize.ts +2 -0
- package/src/status/reporter.ts +19 -1
- package/src/status/snapshot.ts +100 -27
- package/src/system/hash.ts +23 -0
- package/src/system/host-memory.ts +29 -12
- package/src/system/load-better-sqlite3.ts +68 -0
- package/src/types.ts +26 -6
- package/dist/cli-adapters/terminal-backends/xterm-backend.d.ts +0 -17
- package/dist/mesh/mesh-sync.d.ts +0 -53
- package/src/cli-adapters/terminal-backends/ghostty-vt-backend.d.ts +0 -16
- package/src/cli-adapters/terminal-backends/xterm-backend.ts +0 -97
- package/src/mesh/mesh-sync.ts +0 -111
|
@@ -1,1846 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Chat Commands — readChat, sendChat, listChats, newChat, switchChat,
|
|
3
3
|
* setMode, changeModel, setThoughtLevel, resolveAction, chatHistory
|
|
4
|
+
*
|
|
5
|
+
* This module is a re-export barrel. The implementation was split into focused
|
|
6
|
+
* sub-modules (chat-commands-shared / -scope / -debug-bundle / -read / -write)
|
|
7
|
+
* as a pure move; the public export surface here is unchanged. handler.ts does
|
|
8
|
+
* `import * as Chat from './chat-commands.js'` and keeps working unchanged.
|
|
4
9
|
*/
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import type { ChatMessage } from '../types.js';
|
|
21
|
-
import type { SessionTransport } from '../shared-types.js';
|
|
22
|
-
import { filterUserFacingChatMessages, normalizeChatMessages } from '../providers/chat-message-normalization.js';
|
|
23
|
-
|
|
24
|
-
const RECENT_SEND_WINDOW_MS = 1200;
|
|
25
|
-
export const READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS = 25_000;
|
|
26
|
-
const HERMES_CLI_STARTING_SEND_SETTLE_MS = 2_000;
|
|
27
|
-
const recentSendByTarget = new Map<string, number>();
|
|
28
|
-
|
|
29
|
-
interface ApprovalSelectableInstance extends ProviderInstance {
|
|
30
|
-
recordApprovalSelection?(buttonText: string): void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
interface RuntimeChatMessageMerger extends ProviderInstance {
|
|
34
|
-
mergeRuntimeChatMessages?(messages: ChatMessage[]): ChatMessage[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
type LegacyStringScript = (params?: Record<string, unknown> | string) => string;
|
|
38
|
-
|
|
39
|
-
function getCurrentProviderType(h: CommandHelpers, fallback = ''): string {
|
|
40
|
-
return h.currentSession?.providerType || h.currentProviderType || fallback;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getCurrentManagerKey(h: CommandHelpers): string {
|
|
44
|
-
return h.currentSession?.cdpManagerKey || h.currentManagerKey || '';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getTargetedCliAdapter(h: CommandHelpers, args: any, providerType?: string): CliAdapter | null {
|
|
48
|
-
return h.getCliAdapter(args?.targetSessionId || providerType || h.currentSession?.providerType || h.currentManagerKey);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function getTargetInstance(h: CommandHelpers, args: any): ApprovalSelectableInstance | null {
|
|
52
|
-
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
53
|
-
const sessionId = targetSessionId || h.currentSession?.sessionId || '';
|
|
54
|
-
if (!sessionId) return null;
|
|
55
|
-
const session = h.ctx.sessionRegistry?.get(sessionId);
|
|
56
|
-
const instanceKey = session?.adapterKey || session?.instanceKey || sessionId;
|
|
57
|
-
return (h.ctx.instanceManager?.getInstance(instanceKey) as ApprovalSelectableInstance | undefined) || null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function getTargetTransport(h: CommandHelpers, provider?: ProviderModule): SessionTransport | null {
|
|
61
|
-
if (h.currentSession?.transport) return h.currentSession.transport;
|
|
62
|
-
switch (provider?.category) {
|
|
63
|
-
case 'cli':
|
|
64
|
-
return 'pty';
|
|
65
|
-
case 'acp':
|
|
66
|
-
return 'acp';
|
|
67
|
-
case 'extension':
|
|
68
|
-
return 'cdp-webview';
|
|
69
|
-
case 'ide':
|
|
70
|
-
return 'cdp-page';
|
|
71
|
-
default:
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function isCliLikeTransport(transport: SessionTransport | null): boolean {
|
|
77
|
-
return transport === 'pty' || transport === 'acp';
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function isExtensionTransport(transport: SessionTransport | null): boolean {
|
|
81
|
-
return transport === 'cdp-webview';
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function buildRecentSendKey(h: CommandHelpers, args: any, provider: ProviderModule | undefined, signature: string): string {
|
|
85
|
-
const transport = getTargetTransport(h, provider) || 'unknown';
|
|
86
|
-
const target =
|
|
87
|
-
args?.targetSessionId
|
|
88
|
-
|| args?.agentType
|
|
89
|
-
|| h.currentSession?.providerType
|
|
90
|
-
|| h.currentProviderType
|
|
91
|
-
|| h.currentManagerKey
|
|
92
|
-
|| 'unknown';
|
|
93
|
-
return `${transport}:${target}:${signature.trim()}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function summarizeSendInputPart(part: any): string {
|
|
97
|
-
if (!part || typeof part !== 'object') return String(part ?? '');
|
|
98
|
-
if (part.type === 'text') return `text:${String(part.text || '').trim()}`;
|
|
99
|
-
const fields = [
|
|
100
|
-
`type=${String(part.type || '')}`,
|
|
101
|
-
`mime=${String(part.mimeType || '')}`,
|
|
102
|
-
`uri=${String(part.uri || '')}`,
|
|
103
|
-
`name=${String(part.name || '')}`,
|
|
104
|
-
];
|
|
105
|
-
const data = typeof part.data === 'string'
|
|
106
|
-
? part.data
|
|
107
|
-
: typeof part.resource?.blob === 'string'
|
|
108
|
-
? part.resource.blob
|
|
109
|
-
: '';
|
|
110
|
-
if (data) fields.push(`dataLen=${data.length}`, `dataHash=${hashSignatureParts([data]).slice(0, 12)}`);
|
|
111
|
-
const textish = [part.alt, part.transcript, part.description, part.title, part.resource?.uri]
|
|
112
|
-
.filter((value) => typeof value === 'string' && value.trim())
|
|
113
|
-
.join('\u001f');
|
|
114
|
-
if (textish) fields.push(`meta=${hashSignatureParts([textish]).slice(0, 12)}`);
|
|
115
|
-
return fields.join(';');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function buildSendInputSignature(input: InputEnvelope): string {
|
|
119
|
-
const text = typeof input.textFallback === 'string' ? input.textFallback.trim() : '';
|
|
120
|
-
const partSummaries = (input.parts || []).map(summarizeSendInputPart);
|
|
121
|
-
return hashSignatureParts([text, ...partSummaries]);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function getSendChatInputEnvelope(args: any): InputEnvelope {
|
|
125
|
-
return normalizeInputEnvelope(args?.input ? { input: args.input } : args);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function sleep(ms: number): Promise<void> {
|
|
129
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function waitOnceForFreshHermesCliStart(adapter: CliAdapter, log: (msg: string) => void): Promise<void> {
|
|
133
|
-
if (adapter.cliType !== 'hermes-cli') return;
|
|
134
|
-
const status = typeof adapter.getStatus === 'function' ? adapter.getStatus()?.status : undefined;
|
|
135
|
-
if (status !== 'starting') return;
|
|
136
|
-
|
|
137
|
-
log(`Hermes CLI is still starting; waiting ${HERMES_CLI_STARTING_SEND_SETTLE_MS}ms before first send`);
|
|
138
|
-
await sleep(HERMES_CLI_STARTING_SEND_SETTLE_MS);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function getHistorySessionId(h: CommandHelpers, args: any): string | undefined {
|
|
142
|
-
const explicit = typeof args?.historySessionId === 'string' ? args.historySessionId.trim() : '';
|
|
143
|
-
if (explicit) return explicit;
|
|
144
|
-
|
|
145
|
-
const explicitProviderSessionId = typeof args?.providerSessionId === 'string' ? args.providerSessionId.trim() : '';
|
|
146
|
-
if (explicitProviderSessionId) return explicitProviderSessionId;
|
|
147
|
-
|
|
148
|
-
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
149
|
-
if (!targetSessionId) return undefined;
|
|
150
|
-
|
|
151
|
-
const instance = h.ctx.instanceManager?.getInstance(targetSessionId);
|
|
152
|
-
const state = instance?.getState?.();
|
|
153
|
-
const providerSessionId = typeof state?.providerSessionId === 'string' ? state.providerSessionId.trim() : '';
|
|
154
|
-
return providerSessionId || targetSessionId;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function getInteractionId(args: any): string | undefined {
|
|
158
|
-
return typeof args?._interactionId === 'string' && args._interactionId.trim()
|
|
159
|
-
? args._interactionId.trim()
|
|
160
|
-
: undefined;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function traceProviderEvent(
|
|
164
|
-
args: any,
|
|
165
|
-
category: 'provider' | 'parser',
|
|
166
|
-
stage: string,
|
|
167
|
-
options: {
|
|
168
|
-
h: CommandHelpers;
|
|
169
|
-
provider?: ProviderModule;
|
|
170
|
-
payload?: Record<string, unknown>;
|
|
171
|
-
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
172
|
-
},
|
|
173
|
-
): void {
|
|
174
|
-
recordDebugTrace({
|
|
175
|
-
interactionId: getInteractionId(args),
|
|
176
|
-
category,
|
|
177
|
-
stage,
|
|
178
|
-
level: options.level || 'info',
|
|
179
|
-
sessionId: typeof args?.targetSessionId === 'string' ? args.targetSessionId : options.h.currentSession?.sessionId,
|
|
180
|
-
providerType: options.provider?.type || options.h.currentProviderType || options.h.currentSession?.providerType,
|
|
181
|
-
payload: options.payload,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function callLegacyTextScript(script: ProviderScripts[keyof ProviderScripts] | undefined, text: string): string | null {
|
|
186
|
-
if (typeof script !== 'function') return null;
|
|
187
|
-
return (script as LegacyStringScript)(text);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function isRecentDuplicateSend(key: string): boolean {
|
|
191
|
-
const now = Date.now();
|
|
192
|
-
for (const [candidate, ts] of recentSendByTarget.entries()) {
|
|
193
|
-
if (now - ts > RECENT_SEND_WINDOW_MS) recentSendByTarget.delete(candidate);
|
|
194
|
-
}
|
|
195
|
-
const previous = recentSendByTarget.get(key);
|
|
196
|
-
if (previous && (now - previous) <= RECENT_SEND_WINDOW_MS) return true;
|
|
197
|
-
recentSendByTarget.set(key, now);
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function parseMaybeJson(value: any): any {
|
|
202
|
-
if (typeof value !== 'string') return value;
|
|
203
|
-
try {
|
|
204
|
-
return JSON.parse(value);
|
|
205
|
-
} catch {
|
|
206
|
-
return value;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function getChatMessageSignature(message: ChatMessage | null | undefined): string {
|
|
211
|
-
return buildChatMessageSignature(message);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function normalizeReadChatTailLimit(args: any): number {
|
|
215
|
-
const value = Number(args?.tailLimit || 0);
|
|
216
|
-
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function normalizeReadChatMessages(payload: Record<string, any>): ChatMessage[] {
|
|
220
|
-
const messages = Array.isArray(payload.messages) ? payload.messages as ChatMessage[] : [];
|
|
221
|
-
return normalizeChatMessages(messages);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
function deriveHistoryDedupKey(message: ChatMessage & { _unitKey?: string; _turnKey?: string }): string | undefined {
|
|
226
|
-
const unitKey = typeof message._unitKey === 'string' ? message._unitKey.trim() : '';
|
|
227
|
-
if (unitKey) return `read_chat:${unitKey}`;
|
|
228
|
-
|
|
229
|
-
const turnKey = typeof message._turnKey === 'string' ? message._turnKey.trim() : '';
|
|
230
|
-
if (!turnKey) return undefined;
|
|
231
|
-
|
|
232
|
-
let content = '';
|
|
233
|
-
try {
|
|
234
|
-
content = JSON.stringify(message.content ?? '');
|
|
235
|
-
} catch {
|
|
236
|
-
content = String(message.content ?? '');
|
|
237
|
-
}
|
|
238
|
-
return `read_chat:${turnKey}:${String(message.role || '').toLowerCase()}:${content}`;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function toHistoryPersistedMessages(messages: ChatMessage[]): Array<{
|
|
242
|
-
role: string;
|
|
243
|
-
content: string;
|
|
244
|
-
receivedAt?: number;
|
|
245
|
-
kind?: string;
|
|
246
|
-
senderName?: string;
|
|
247
|
-
historyDedupKey?: string;
|
|
248
|
-
}> {
|
|
249
|
-
return messages.map((message) => ({
|
|
250
|
-
role: message.role,
|
|
251
|
-
content: flattenContent(message.content),
|
|
252
|
-
receivedAt: typeof message.receivedAt === 'number' ? message.receivedAt : undefined,
|
|
253
|
-
kind: typeof message.kind === 'string' ? message.kind : undefined,
|
|
254
|
-
senderName: typeof message.senderName === 'string' ? message.senderName : undefined,
|
|
255
|
-
historyDedupKey: deriveHistoryDedupKey(message as ChatMessage & { _unitKey?: string; _turnKey?: string }),
|
|
256
|
-
}));
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function buildFullTail(messages: ChatMessage[], tailLimit: number): {
|
|
260
|
-
messages: ChatMessage[];
|
|
261
|
-
totalMessages: number;
|
|
262
|
-
} {
|
|
263
|
-
const totalMessages = messages.length;
|
|
264
|
-
const tailMessages = tailLimit > 0 ? messages.slice(-tailLimit) : messages;
|
|
265
|
-
return {
|
|
266
|
-
messages: tailMessages,
|
|
267
|
-
totalMessages,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function hasNonEmptyModalButtons(activeModal: unknown): boolean {
|
|
272
|
-
if (!activeModal || typeof activeModal !== 'object') return false;
|
|
273
|
-
const buttons = (activeModal as { buttons?: unknown }).buttons;
|
|
274
|
-
return Array.isArray(buttons) && buttons.some((button) => typeof button === 'string' && button.trim().length > 0);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function normalizeReadChatCommandStatus(status: unknown, activeModal: unknown): string {
|
|
278
|
-
const raw = typeof status === 'string' ? status.trim() : '';
|
|
279
|
-
if (!raw) {
|
|
280
|
-
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'idle';
|
|
281
|
-
}
|
|
282
|
-
switch (raw) {
|
|
283
|
-
case 'starting':
|
|
284
|
-
return hasNonEmptyModalButtons(activeModal) ? 'waiting_approval' : 'generating';
|
|
285
|
-
case 'stopped':
|
|
286
|
-
case 'disconnected':
|
|
287
|
-
case 'not_monitored':
|
|
288
|
-
return 'error';
|
|
289
|
-
default:
|
|
290
|
-
return raw;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function isGeneratingLikeStatus(status: unknown): boolean {
|
|
295
|
-
return status === 'generating' || status === 'streaming' || status === 'long_generating' || status === 'starting';
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function shouldTrustCliAdapterTerminalStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): boolean {
|
|
299
|
-
if (!isGeneratingLikeStatus(parsedStatus)) return false;
|
|
300
|
-
if (hasNonEmptyModalButtons(activeModal)) return false;
|
|
301
|
-
const adapterRawStatus = typeof adapterStatus?.status === 'string' ? adapterStatus.status.trim() : '';
|
|
302
|
-
if (adapterRawStatus !== 'idle') return false;
|
|
303
|
-
if (typeof adapter.isProcessing === 'function' && adapter.isProcessing()) return false;
|
|
304
|
-
return true;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function normalizeCliReadChatStatus(parsedStatus: unknown, activeModal: unknown, adapter: CliAdapter, adapterStatus: any): string {
|
|
308
|
-
if (shouldTrustCliAdapterTerminalStatus(parsedStatus, activeModal, adapter, adapterStatus)) return 'idle';
|
|
309
|
-
return typeof parsedStatus === 'string' && parsedStatus.trim() ? parsedStatus : 'idle';
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
function finalizeStreamingMessagesWhenIdle(messages: ChatMessage[], status: string): ChatMessage[] {
|
|
313
|
-
if (status !== 'idle') return messages;
|
|
314
|
-
return messages.map((message) => {
|
|
315
|
-
const meta = message.meta && typeof message.meta === 'object'
|
|
316
|
-
? message.meta as Record<string, unknown>
|
|
317
|
-
: undefined;
|
|
318
|
-
const hasStreamingMeta = meta?.streaming === true;
|
|
319
|
-
if (message.bubbleState !== 'streaming' && !hasStreamingMeta) return message;
|
|
320
|
-
return {
|
|
321
|
-
...message,
|
|
322
|
-
...(message.bubbleState === 'streaming' ? { bubbleState: 'final' as const } : {}),
|
|
323
|
-
...(hasStreamingMeta ? { meta: { ...meta, streaming: false } } : {}),
|
|
324
|
-
};
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function buildReadChatCommandResult(payload: Record<string, any>, args: any): CommandResult {
|
|
329
|
-
let validatedPayload: Record<string, any>;
|
|
330
|
-
const debugReadChat = payload?.debugReadChat && typeof payload.debugReadChat === 'object'
|
|
331
|
-
? payload.debugReadChat
|
|
332
|
-
: undefined;
|
|
333
|
-
try {
|
|
334
|
-
validatedPayload = validateReadChatResultPayload({
|
|
335
|
-
...payload,
|
|
336
|
-
status: normalizeReadChatCommandStatus(payload?.status, payload?.activeModal),
|
|
337
|
-
}, 'read_chat command result') as Record<string, any>;
|
|
338
|
-
} catch (error: any) {
|
|
339
|
-
return { success: false, error: error?.message || String(error) };
|
|
340
|
-
}
|
|
341
|
-
const messages = normalizeReadChatMessages(validatedPayload);
|
|
342
|
-
const visibleMessages = filterUserFacingChatMessages(messages);
|
|
343
|
-
const sync = buildFullTail(visibleMessages, normalizeReadChatTailLimit(args));
|
|
344
|
-
const hiddenMsgCount = Math.max(0, messages.length - visibleMessages.length);
|
|
345
|
-
const returnedDebugReadChat = debugReadChat
|
|
346
|
-
? {
|
|
347
|
-
...debugReadChat,
|
|
348
|
-
fullMsgCount: typeof debugReadChat.fullMsgCount === 'number'
|
|
349
|
-
? debugReadChat.fullMsgCount
|
|
350
|
-
: messages.length,
|
|
351
|
-
visibleMsgCount: visibleMessages.length,
|
|
352
|
-
hiddenMsgCount,
|
|
353
|
-
returnedMsgCount: sync.messages.length,
|
|
354
|
-
}
|
|
355
|
-
: undefined;
|
|
356
|
-
return {
|
|
357
|
-
success: true,
|
|
358
|
-
...validatedPayload,
|
|
359
|
-
messages: sync.messages,
|
|
360
|
-
totalMessages: sync.totalMessages,
|
|
361
|
-
...(returnedDebugReadChat ? { debugReadChat: returnedDebugReadChat } : {}),
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
interface DebugSanitizeOptions {
|
|
367
|
-
maxDepth?: number;
|
|
368
|
-
maxArrayLength?: number;
|
|
369
|
-
maxObjectKeys?: number;
|
|
370
|
-
maxStringLength?: number;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const DEFAULT_DEBUG_SANITIZE_OPTIONS: Required<DebugSanitizeOptions> = {
|
|
374
|
-
maxDepth: 8,
|
|
375
|
-
maxArrayLength: 80,
|
|
376
|
-
maxObjectKeys: 120,
|
|
377
|
-
maxStringLength: 16_000,
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
const SECRET_KEY_PATTERN = /(?:token|secret|password|passwd|authorization|cookie|api[_-]?key|access[_-]?key|refresh[_-]?token|client[_-]?secret|private[_-]?key)/i;
|
|
381
|
-
|
|
382
|
-
function truncateDebugString(value: string, maxLength: number): string {
|
|
383
|
-
if (value.length <= maxLength) return value;
|
|
384
|
-
return `${value.slice(0, maxLength)}…[truncated ${value.length - maxLength} chars]`;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function redactDebugSecrets(value: string): string {
|
|
388
|
-
return value
|
|
389
|
-
.replace(/(Authorization\s*:\s*Bearer\s+)[^\s'"`]+/gi, '$1[REDACTED:bearer]')
|
|
390
|
-
.replace(/(Bearer\s+)[A-Za-z0-9._~+\/-]{16,}=*/gi, '$1[REDACTED:bearer]')
|
|
391
|
-
.replace(/\b(?:gh[pousr]|github_pat)_[A-Za-z0-9_]{20,}\b/g, '[REDACTED:github-token]')
|
|
392
|
-
.replace(/\bsk-[A-Za-z0-9_-]{16,}\b/g, '[REDACTED:api-key]')
|
|
393
|
-
.replace(/\bxox[baprs]-[A-Za-z0-9-]{12,}\b/g, '[REDACTED:slack-token]')
|
|
394
|
-
.replace(/\b(?:adk|adm)_[A-Za-z0-9_-]{16,}\b/g, '[REDACTED:adhdev-token]')
|
|
395
|
-
.replace(/((?:api[_-]?key|token|secret|password|passwd|client[_-]?secret)\s*[:=]\s*)[^\s,'"`}&]+/gi, '$1[REDACTED:secret]')
|
|
396
|
-
.replace(/([?&](?:api[_-]?key|token|secret|password|client_secret)=)[^&#\s]+/gi, '$1[REDACTED:secret]');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
export function sanitizeDebugBundleValue(
|
|
400
|
-
value: unknown,
|
|
401
|
-
options: DebugSanitizeOptions = {},
|
|
402
|
-
depth = 0,
|
|
403
|
-
keyHint = '',
|
|
404
|
-
): unknown {
|
|
405
|
-
const normalizedOptions = { ...DEFAULT_DEBUG_SANITIZE_OPTIONS, ...options };
|
|
406
|
-
if (value === null || value === undefined) return value;
|
|
407
|
-
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
408
|
-
if (typeof value === 'bigint') return String(value);
|
|
409
|
-
if (typeof value === 'string') {
|
|
410
|
-
if (SECRET_KEY_PATTERN.test(keyHint) && value.trim()) return '[REDACTED:secret-field]';
|
|
411
|
-
return truncateDebugString(redactDebugSecrets(value), normalizedOptions.maxStringLength);
|
|
412
|
-
}
|
|
413
|
-
if (typeof value === 'function') return `[Function ${value.name || 'anonymous'}]`;
|
|
414
|
-
if (typeof value !== 'object') return String(value);
|
|
415
|
-
if (depth >= normalizedOptions.maxDepth) return '[MaxDepth]';
|
|
416
|
-
|
|
417
|
-
if (Array.isArray(value)) {
|
|
418
|
-
const items = value
|
|
419
|
-
.slice(0, normalizedOptions.maxArrayLength)
|
|
420
|
-
.map((item) => sanitizeDebugBundleValue(item, normalizedOptions, depth + 1, keyHint));
|
|
421
|
-
if (value.length > normalizedOptions.maxArrayLength) {
|
|
422
|
-
items.push(`[truncated ${value.length - normalizedOptions.maxArrayLength} items]`);
|
|
423
|
-
}
|
|
424
|
-
return items;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const record = value as Record<string, unknown>;
|
|
428
|
-
const result: Record<string, unknown> = {};
|
|
429
|
-
const entries = Object.entries(record).slice(0, normalizedOptions.maxObjectKeys);
|
|
430
|
-
for (const [key, item] of entries) {
|
|
431
|
-
result[key] = sanitizeDebugBundleValue(item, normalizedOptions, depth + 1, key);
|
|
432
|
-
}
|
|
433
|
-
const remaining = Object.keys(record).length - entries.length;
|
|
434
|
-
if (remaining > 0) result.__truncatedKeys = remaining;
|
|
435
|
-
return result;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function summarizeProviderForDebug(provider: ProviderModule | undefined): Record<string, unknown> | null {
|
|
439
|
-
if (!provider) return null;
|
|
440
|
-
const scripts = provider.scripts && typeof provider.scripts === 'object'
|
|
441
|
-
? Object.keys(provider.scripts)
|
|
442
|
-
: [];
|
|
443
|
-
const controls = Array.isArray((provider as any).controls)
|
|
444
|
-
? (provider as any).controls.map((control: any) => ({
|
|
445
|
-
id: control?.id,
|
|
446
|
-
label: control?.label,
|
|
447
|
-
type: control?.type,
|
|
448
|
-
settingKey: control?.settingKey,
|
|
449
|
-
invokeScript: control?.invokeScript,
|
|
450
|
-
listScript: control?.listScript,
|
|
451
|
-
location: control?.location,
|
|
452
|
-
}))
|
|
453
|
-
: [];
|
|
454
|
-
return {
|
|
455
|
-
type: provider.type,
|
|
456
|
-
name: provider.name,
|
|
457
|
-
category: provider.category,
|
|
458
|
-
version: (provider as any).version,
|
|
459
|
-
canonicalHistory: provider.canonicalHistory,
|
|
460
|
-
historyBehavior: provider.historyBehavior,
|
|
461
|
-
webviewMatchText: provider.webviewMatchText,
|
|
462
|
-
scriptNames: scripts,
|
|
463
|
-
controls,
|
|
464
|
-
resume: provider.resume,
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
function summarizeSessionForDebug(session: any): Record<string, unknown> | null {
|
|
469
|
-
if (!session || typeof session !== 'object') return null;
|
|
470
|
-
return {
|
|
471
|
-
sessionId: session.sessionId,
|
|
472
|
-
instanceKey: session.instanceKey,
|
|
473
|
-
adapterKey: session.adapterKey,
|
|
474
|
-
providerType: session.providerType,
|
|
475
|
-
providerName: session.providerName,
|
|
476
|
-
transport: session.transport,
|
|
477
|
-
kind: session.kind,
|
|
478
|
-
cdpManagerKey: session.cdpManagerKey,
|
|
479
|
-
parentSessionId: session.parentSessionId,
|
|
480
|
-
providerSessionId: session.providerSessionId,
|
|
481
|
-
workspace: session.workspace,
|
|
482
|
-
title: session.title,
|
|
483
|
-
status: session.status,
|
|
484
|
-
mode: session.mode,
|
|
485
|
-
capabilities: session.capabilities,
|
|
486
|
-
};
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
function summarizeStateForDebug(state: any): Record<string, unknown> | null {
|
|
490
|
-
if (!state || typeof state !== 'object') return null;
|
|
491
|
-
const activeChat = state.activeChat && typeof state.activeChat === 'object' ? state.activeChat : null;
|
|
492
|
-
return {
|
|
493
|
-
type: state.type,
|
|
494
|
-
name: state.name,
|
|
495
|
-
category: state.category,
|
|
496
|
-
status: state.status,
|
|
497
|
-
instanceId: state.instanceId,
|
|
498
|
-
providerSessionId: state.providerSessionId,
|
|
499
|
-
title: state.title,
|
|
500
|
-
transport: state.transport,
|
|
501
|
-
mode: state.mode,
|
|
502
|
-
workspace: state.workspace,
|
|
503
|
-
runtime: state.runtime,
|
|
504
|
-
errorMessage: state.errorMessage,
|
|
505
|
-
errorReason: state.errorReason,
|
|
506
|
-
activeChat: activeChat ? {
|
|
507
|
-
status: activeChat.status,
|
|
508
|
-
title: activeChat.title,
|
|
509
|
-
messageCount: Array.isArray(activeChat.messages) ? activeChat.messages.length : undefined,
|
|
510
|
-
activeModal: activeChat.activeModal,
|
|
511
|
-
messagesTail: Array.isArray(activeChat.messages) ? activeChat.messages.slice(-10) : undefined,
|
|
512
|
-
} : null,
|
|
513
|
-
controlValues: state.controlValues,
|
|
514
|
-
summaryMetadata: state.summaryMetadata,
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
function buildDebugBundleText(bundle: Record<string, unknown>): string {
|
|
519
|
-
return [
|
|
520
|
-
'# ADHDev Chat Debug Bundle',
|
|
521
|
-
'',
|
|
522
|
-
'```json',
|
|
523
|
-
JSON.stringify(bundle, null, 2),
|
|
524
|
-
'```',
|
|
525
|
-
].join('\n');
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
function getChatDebugBundleDir(): string {
|
|
529
|
-
const override = typeof process.env.ADHDEV_DEBUG_BUNDLE_DIR === 'string'
|
|
530
|
-
? process.env.ADHDEV_DEBUG_BUNDLE_DIR.trim()
|
|
531
|
-
: '';
|
|
532
|
-
return override || path.join(os.homedir(), '.adhdev', 'debug-bundles', 'chat');
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
function safeBundleIdSegment(value: unknown, fallback: string): string {
|
|
536
|
-
const normalized = String(value || fallback)
|
|
537
|
-
.trim()
|
|
538
|
-
.replace(/[^A-Za-z0-9_.-]+/g, '-')
|
|
539
|
-
.replace(/^-+|-+$/g, '')
|
|
540
|
-
.slice(0, 80);
|
|
541
|
-
return normalized || fallback;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function createChatDebugBundleId(targetSessionId: string): string {
|
|
545
|
-
const timestamp = new Date().toISOString().replace(/[-:.]/g, '').replace('T', 'T').replace('Z', 'Z');
|
|
546
|
-
const sessionSegment = safeBundleIdSegment(targetSessionId, 'unknown-session');
|
|
547
|
-
return `chat-debug-${timestamp}-${sessionSegment}-${randomUUID().slice(0, 8)}`;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
function buildChatDebugBundleSummary(bundle: Record<string, unknown>): Record<string, unknown> {
|
|
551
|
-
const target = bundle.target && typeof bundle.target === 'object' ? bundle.target as Record<string, unknown> : {};
|
|
552
|
-
const readChat = bundle.readChat && typeof bundle.readChat === 'object' ? bundle.readChat as Record<string, unknown> : {};
|
|
553
|
-
const cli = bundle.cli && typeof bundle.cli === 'object' ? bundle.cli as Record<string, unknown> : null;
|
|
554
|
-
const frontend = bundle.frontend && typeof bundle.frontend === 'object' ? bundle.frontend as Record<string, unknown> : null;
|
|
555
|
-
const debugReadChat = readChat.debugReadChat && typeof readChat.debugReadChat === 'object'
|
|
556
|
-
? readChat.debugReadChat as Record<string, unknown>
|
|
557
|
-
: {};
|
|
558
|
-
const parsedStatus = cli?.parsedStatus && typeof cli.parsedStatus === 'object'
|
|
559
|
-
? cli.parsedStatus as Record<string, unknown>
|
|
560
|
-
: null;
|
|
561
|
-
const cliParsedMessageCount = Array.isArray(parsedStatus?.messages) ? parsedStatus.messages.length : undefined;
|
|
562
|
-
const readChatReturnedMessages = Array.isArray(readChat.messagesTail) ? readChat.messagesTail.length : undefined;
|
|
563
|
-
const cliPartialResponse = typeof cli?.partialResponse === 'string' ? cli.partialResponse : '';
|
|
564
|
-
const readChatStatus = typeof readChat.status === 'string' ? readChat.status : '';
|
|
565
|
-
const cliStatus = typeof cli?.status === 'string' ? cli.status : '';
|
|
566
|
-
const cliParsedStatus = typeof parsedStatus?.status === 'string' ? parsedStatus.status : '';
|
|
567
|
-
return {
|
|
568
|
-
createdAt: bundle.createdAt,
|
|
569
|
-
targetSessionId: target.targetSessionId,
|
|
570
|
-
providerType: target.providerType,
|
|
571
|
-
transport: target.transport,
|
|
572
|
-
readChatSuccess: readChat.success,
|
|
573
|
-
readChatStatus: readChat.status,
|
|
574
|
-
readChatTotalMessages: readChat.totalMessages,
|
|
575
|
-
readChatReturnedMessages,
|
|
576
|
-
cliStatus: cli?.status,
|
|
577
|
-
cliParsedStatus: cliParsedStatus || undefined,
|
|
578
|
-
cliMessageCount: cli?.messageCount,
|
|
579
|
-
cliParsedMessageCount,
|
|
580
|
-
cliPartialResponseChars: cliPartialResponse.length,
|
|
581
|
-
parserAdapterStatusMismatch: Boolean(cliStatus && cliParsedStatus && cliStatus !== cliParsedStatus),
|
|
582
|
-
parserReadChatStatusMismatch: Boolean(readChatStatus && cliParsedStatus && readChatStatus !== cliParsedStatus),
|
|
583
|
-
readChatDebug: Object.keys(debugReadChat).length ? {
|
|
584
|
-
adapterStatus: debugReadChat.adapterStatus,
|
|
585
|
-
parsedStatus: debugReadChat.parsedStatus,
|
|
586
|
-
returnedStatus: debugReadChat.returnedStatus,
|
|
587
|
-
parsedMsgCount: debugReadChat.parsedMsgCount,
|
|
588
|
-
returnedMsgCount: debugReadChat.returnedMsgCount,
|
|
589
|
-
shouldPreferAdapterMessages: debugReadChat.shouldPreferAdapterMessages,
|
|
590
|
-
} : undefined,
|
|
591
|
-
hasFrontendSnapshot: !!frontend,
|
|
592
|
-
};
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
function storeChatDebugBundleOnDaemon(bundle: Record<string, unknown>, targetSessionId: string): { bundleId: string; savedPath: string; sizeBytes: number } {
|
|
596
|
-
const bundleId = createChatDebugBundleId(targetSessionId);
|
|
597
|
-
const dir = getChatDebugBundleDir();
|
|
598
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
599
|
-
const savedPath = path.join(dir, `${bundleId}.json`);
|
|
600
|
-
const json = `${JSON.stringify(bundle, null, 2)}\n`;
|
|
601
|
-
fs.writeFileSync(savedPath, json, { encoding: 'utf8', mode: 0o600 });
|
|
602
|
-
return { bundleId, savedPath, sizeBytes: Buffer.byteLength(json, 'utf8') };
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
function isDaemonFileDebugDelivery(args: any): boolean {
|
|
606
|
-
return args?.delivery === 'daemon_file' || args?.delivery === 'file';
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
export async function handleGetChatDebugBundle(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
610
|
-
const targetSessionId = typeof args?.targetSessionId === 'string' ? args.targetSessionId.trim() : '';
|
|
611
|
-
if (!targetSessionId && !h.currentSession) {
|
|
612
|
-
return { success: false, error: 'No targetSessionId specified — cannot route command' };
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const provider = h.getProvider(args?.agentType);
|
|
616
|
-
const transport = getTargetTransport(h, provider);
|
|
617
|
-
const providerType = provider?.type || getCurrentProviderType(h, args?.agentType || '');
|
|
618
|
-
const adapter = isCliLikeTransport(transport) ? getTargetedCliAdapter(h, args, provider?.type) : null;
|
|
619
|
-
const targetInstance = getTargetInstance(h, args);
|
|
620
|
-
|
|
621
|
-
let adapterStatus: unknown = null;
|
|
622
|
-
let parsedStatus: unknown = null;
|
|
623
|
-
let adapterDebugSnapshot: unknown = null;
|
|
624
|
-
let partialResponse = '';
|
|
625
|
-
if (adapter) {
|
|
626
|
-
try { adapterStatus = adapter.getStatus?.(); } catch (error: any) { adapterStatus = { error: error?.message || String(error) }; }
|
|
627
|
-
try { parsedStatus = typeof adapter.getScriptParsedStatus === 'function' ? parseMaybeJson(adapter.getScriptParsedStatus()) : null; } catch (error: any) { parsedStatus = { error: error?.message || String(error) }; }
|
|
628
|
-
try { adapterDebugSnapshot = typeof adapter.getDebugSnapshot === 'function' ? adapter.getDebugSnapshot() : null; } catch (error: any) { adapterDebugSnapshot = { error: error?.message || String(error) }; }
|
|
629
|
-
try { partialResponse = adapter.getPartialResponse?.() || ''; } catch { partialResponse = ''; }
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
let instanceState: unknown = null;
|
|
633
|
-
if (targetInstance?.getState) {
|
|
634
|
-
try { instanceState = summarizeStateForDebug(targetInstance.getState()); } catch (error: any) { instanceState = { error: error?.message || String(error) }; }
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
let readChat: unknown = null;
|
|
638
|
-
try {
|
|
639
|
-
const readResult = await handleReadChat(h, { ...args, tailLimit: Math.max(1, Math.min(40, Number(args?.tailLimit || 40))) });
|
|
640
|
-
readChat = readResult.success
|
|
641
|
-
? {
|
|
642
|
-
success: true,
|
|
643
|
-
status: readResult.status,
|
|
644
|
-
title: readResult.title,
|
|
645
|
-
totalMessages: readResult.totalMessages,
|
|
646
|
-
providerSessionId: readResult.providerSessionId,
|
|
647
|
-
transcriptAuthority: readResult.transcriptAuthority,
|
|
648
|
-
coverage: readResult.coverage,
|
|
649
|
-
activeModal: readResult.activeModal,
|
|
650
|
-
messagesTail: Array.isArray(readResult.messages) ? readResult.messages.slice(-20) : [],
|
|
651
|
-
debugReadChat: readResult.debugReadChat,
|
|
652
|
-
}
|
|
653
|
-
: { success: false, error: readResult.error };
|
|
654
|
-
} catch (error: any) {
|
|
655
|
-
readChat = { success: false, error: error?.message || String(error) };
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const cdp = h.getCdp();
|
|
659
|
-
const rawBundle: Record<string, unknown> = {
|
|
660
|
-
version: 1,
|
|
661
|
-
createdAt: new Date().toISOString(),
|
|
662
|
-
target: {
|
|
663
|
-
targetSessionId,
|
|
664
|
-
providerType,
|
|
665
|
-
transport,
|
|
666
|
-
routeManagerKey: h.currentManagerKey,
|
|
667
|
-
currentIdeType: h.currentIdeType,
|
|
668
|
-
},
|
|
669
|
-
session: summarizeSessionForDebug(h.currentSession),
|
|
670
|
-
provider: summarizeProviderForDebug(provider),
|
|
671
|
-
daemon: {
|
|
672
|
-
pid: process.pid,
|
|
673
|
-
platform: process.platform,
|
|
674
|
-
nodeVersion: process.version,
|
|
675
|
-
cwd: process.cwd(),
|
|
676
|
-
},
|
|
677
|
-
cdp: {
|
|
678
|
-
requested: !!cdp,
|
|
679
|
-
connected: !!cdp?.isConnected,
|
|
680
|
-
managerKey: getCurrentManagerKey(h),
|
|
681
|
-
},
|
|
682
|
-
instanceState,
|
|
683
|
-
cli: adapter ? {
|
|
684
|
-
cliType: adapter.cliType,
|
|
685
|
-
cliName: adapter.cliName,
|
|
686
|
-
workingDir: adapter.workingDir,
|
|
687
|
-
status: (adapterStatus as any)?.status,
|
|
688
|
-
activeModal: (adapterStatus as any)?.activeModal,
|
|
689
|
-
messageCount: Array.isArray((adapterStatus as any)?.messages) ? (adapterStatus as any).messages.length : undefined,
|
|
690
|
-
messagesTail: Array.isArray((adapterStatus as any)?.messages) ? (adapterStatus as any).messages.slice(-20) : undefined,
|
|
691
|
-
parsedStatus,
|
|
692
|
-
partialResponse,
|
|
693
|
-
ready: typeof adapter.isReady === 'function' ? adapter.isReady() : undefined,
|
|
694
|
-
processing: typeof adapter.isProcessing === 'function' ? adapter.isProcessing() : undefined,
|
|
695
|
-
debugSnapshot: adapterDebugSnapshot,
|
|
696
|
-
} : null,
|
|
697
|
-
readChat,
|
|
698
|
-
frontend: args?.frontendSnapshot && typeof args.frontendSnapshot === 'object' ? args.frontendSnapshot : null,
|
|
699
|
-
recentLogs: getRecentLogs(80, 'debug'),
|
|
700
|
-
recentDebugTrace: getRecentDebugTrace({ limit: 120 }),
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
const bundle = sanitizeDebugBundleValue(rawBundle) as Record<string, unknown>;
|
|
704
|
-
if (isDaemonFileDebugDelivery(args)) {
|
|
705
|
-
const summary = buildChatDebugBundleSummary(bundle);
|
|
706
|
-
const stored = storeChatDebugBundleOnDaemon(bundle, targetSessionId || String(summary.targetSessionId || 'unknown-session'));
|
|
707
|
-
LOG.info('Command', `[get_chat_debug_bundle] saved daemon_file bundle id=${stored.bundleId} path=${stored.savedPath} sizeBytes=${stored.sizeBytes} targetSessionId=${summary.targetSessionId || ''} providerType=${summary.providerType || ''} transport=${summary.transport || ''}`);
|
|
708
|
-
return {
|
|
709
|
-
success: true,
|
|
710
|
-
delivery: 'daemon_file',
|
|
711
|
-
bundleId: stored.bundleId,
|
|
712
|
-
savedPath: stored.savedPath,
|
|
713
|
-
sizeBytes: stored.sizeBytes,
|
|
714
|
-
createdAt: bundle.createdAt,
|
|
715
|
-
summary,
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
return {
|
|
719
|
-
success: true,
|
|
720
|
-
bundle,
|
|
721
|
-
text: buildDebugBundleText(bundle),
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
function didProviderConfirmSend(result: any): boolean {
|
|
726
|
-
const parsed = parseMaybeJson(result);
|
|
727
|
-
if (parsed === true) return true;
|
|
728
|
-
if (typeof parsed === 'string') {
|
|
729
|
-
const normalized = parsed.trim().toLowerCase();
|
|
730
|
-
return normalized === 'ok' || normalized === 'sent' || normalized === 'success' || normalized === 'true';
|
|
731
|
-
}
|
|
732
|
-
if (!parsed || typeof parsed !== 'object') return false;
|
|
733
|
-
|
|
734
|
-
return parsed.sent === true
|
|
735
|
-
|| parsed.success === true
|
|
736
|
-
|| parsed.ok === true
|
|
737
|
-
|| parsed.submitted === true
|
|
738
|
-
|| parsed.dispatched === true;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
async function readExtensionChatState(h: CommandHelpers): Promise<any | null> {
|
|
742
|
-
try {
|
|
743
|
-
const evalResult = await h.evaluateProviderScript('readChat', undefined, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS);
|
|
744
|
-
if (!evalResult?.result) return null;
|
|
745
|
-
const parsed = parseMaybeJson(evalResult.result);
|
|
746
|
-
return parsed && typeof parsed === 'object' ? parsed : null;
|
|
747
|
-
} catch {
|
|
748
|
-
return null;
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
function getStateMessageCount(state: any): number {
|
|
753
|
-
return Array.isArray(state?.messages) ? state.messages.length : 0;
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
function getStateLastSignature(state: any): string {
|
|
757
|
-
const messages = Array.isArray(state?.messages) ? state.messages : [];
|
|
758
|
-
const last = messages[messages.length - 1];
|
|
759
|
-
if (!last) return '';
|
|
760
|
-
return `${last.role || ''}:${String(last.content || '').replace(/\s+/g, ' ').trim()}`;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
function toNonNegativeNumber(value: any): number {
|
|
764
|
-
const numeric = Number(value ?? 0);
|
|
765
|
-
return Number.isFinite(numeric) ? Math.max(0, numeric) : 0;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
function getCliVisibleTranscriptCount(adapter: any): number {
|
|
769
|
-
if (typeof adapter?.getScriptParsedStatus !== 'function') return 0;
|
|
770
|
-
try {
|
|
771
|
-
const parsed = parseMaybeJson(adapter.getScriptParsedStatus());
|
|
772
|
-
return Array.isArray(parsed?.messages) ? parsed.messages.length : 0;
|
|
773
|
-
} catch {
|
|
774
|
-
return 0;
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
async function getStableExtensionBaseline(h: CommandHelpers): Promise<any | null> {
|
|
779
|
-
const first = await readExtensionChatState(h);
|
|
780
|
-
if (getStateMessageCount(first) > 0 || getStateLastSignature(first)) return first;
|
|
781
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
782
|
-
const second = await readExtensionChatState(h);
|
|
783
|
-
return getStateMessageCount(second) >= getStateMessageCount(first) ? second : first;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
async function verifyExtensionSendObserved(h: CommandHelpers, before: any): Promise<boolean> {
|
|
787
|
-
const beforeCount = getStateMessageCount(before);
|
|
788
|
-
const beforeSignature = getStateLastSignature(before);
|
|
789
|
-
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
790
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
791
|
-
const state = await readExtensionChatState(h);
|
|
792
|
-
if (state?.status === 'waiting_approval') return true;
|
|
793
|
-
const afterCount = getStateMessageCount(state);
|
|
794
|
-
const afterSignature = getStateLastSignature(state);
|
|
795
|
-
if (afterCount > beforeCount) return true;
|
|
796
|
-
if (afterSignature && afterSignature !== beforeSignature) return true;
|
|
797
|
-
}
|
|
798
|
-
return false;
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
export async function handleChatHistory(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
802
|
-
const { agentType, offset, limit } = args;
|
|
803
|
-
const historySessionId = getHistorySessionId(h, args);
|
|
804
|
-
try {
|
|
805
|
-
const provider = h.getProvider(agentType);
|
|
806
|
-
const agentStr = provider?.type || agentType || getCurrentProviderType(h);
|
|
807
|
-
const transport = getTargetTransport(h, provider);
|
|
808
|
-
const hasExplicitExcludeRecentCount = args?.excludeRecentCount !== undefined && args?.excludeRecentCount !== null;
|
|
809
|
-
let excludeRecentCount = toNonNegativeNumber(args?.excludeRecentCount);
|
|
810
|
-
if (!hasExplicitExcludeRecentCount && isCliLikeTransport(transport)) {
|
|
811
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
812
|
-
const visibleCount = getCliVisibleTranscriptCount(adapter);
|
|
813
|
-
if (visibleCount > excludeRecentCount) excludeRecentCount = visibleCount;
|
|
814
|
-
}
|
|
815
|
-
const workspace = typeof args?.workspace === 'string'
|
|
816
|
-
? args.workspace
|
|
817
|
-
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
818
|
-
? (h.currentSession as any).workspace
|
|
819
|
-
: undefined;
|
|
820
|
-
const result = readProviderChatHistory(agentStr, {
|
|
821
|
-
canonicalHistory: provider?.canonicalHistory,
|
|
822
|
-
historySessionId,
|
|
823
|
-
workspace,
|
|
824
|
-
offset: offset || 0,
|
|
825
|
-
limit: limit || 30,
|
|
826
|
-
excludeRecentCount,
|
|
827
|
-
historyBehavior: provider?.historyBehavior,
|
|
828
|
-
scripts: provider?.scripts as any,
|
|
829
|
-
});
|
|
830
|
-
return { success: true, ...result, agent: agentStr };
|
|
831
|
-
} catch (e: any) {
|
|
832
|
-
return { success: false, error: e.message };
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
export async function handleReadChat(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
837
|
-
const provider = h.getProvider(args?.agentType || args?.providerType);
|
|
838
|
-
const transport = getTargetTransport(h, provider);
|
|
839
|
-
const historySessionId = getHistorySessionId(h, args);
|
|
840
|
-
|
|
841
|
-
const _log = (msg: string) => LOG.debug('Command', `[read_chat] ${msg}`);
|
|
842
|
-
|
|
843
|
-
// PTY / ACP transport: read from adapter
|
|
844
|
-
if (isCliLikeTransport(transport)) {
|
|
845
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
846
|
-
if (adapter) {
|
|
847
|
-
_log(`${transport} adapter: ${adapter.cliType}`);
|
|
848
|
-
if (typeof adapter.getScriptParsedStatus !== 'function') {
|
|
849
|
-
return { success: false, error: `${transport} adapter parseSession unavailable` };
|
|
850
|
-
}
|
|
851
|
-
let parsedStatus: any = null;
|
|
852
|
-
try {
|
|
853
|
-
parsedStatus = parseMaybeJson(adapter.getScriptParsedStatus());
|
|
854
|
-
} catch (error: any) {
|
|
855
|
-
return { success: false, error: error?.message || String(error) };
|
|
856
|
-
}
|
|
857
|
-
const parsedRecord = parsedStatus && typeof parsedStatus === 'object'
|
|
858
|
-
? parsedStatus as Record<string, any>
|
|
859
|
-
: null;
|
|
860
|
-
if (!parsedRecord || !Array.isArray(parsedRecord.messages)) {
|
|
861
|
-
return { success: false, error: `${transport} parser did not return messages` };
|
|
862
|
-
}
|
|
863
|
-
const adapterStatus = typeof adapter.getStatus === 'function'
|
|
864
|
-
? adapter.getStatus()
|
|
865
|
-
: {};
|
|
866
|
-
const title = typeof parsedRecord.title === 'string' ? parsedRecord.title : undefined;
|
|
867
|
-
const providerSessionId = typeof parsedRecord.providerSessionId === 'string'
|
|
868
|
-
? parsedRecord.providerSessionId
|
|
869
|
-
: undefined;
|
|
870
|
-
const transcriptAuthority = parsedRecord.transcriptAuthority === 'provider' || parsedRecord.transcriptAuthority === 'daemon'
|
|
871
|
-
? parsedRecord.transcriptAuthority
|
|
872
|
-
: undefined;
|
|
873
|
-
const coverage = parsedRecord.coverage === 'full' || parsedRecord.coverage === 'tail' || parsedRecord.coverage === 'current-turn'
|
|
874
|
-
? parsedRecord.coverage
|
|
875
|
-
: undefined;
|
|
876
|
-
const activeModal = parsedRecord.activeModal ?? parsedRecord.modal ?? null;
|
|
877
|
-
const returnedStatus = normalizeCliReadChatStatus(parsedRecord.status, activeModal, adapter, adapterStatus);
|
|
878
|
-
const runtimeMessageMerger = getTargetInstance(h, args) as RuntimeChatMessageMerger | null;
|
|
879
|
-
const parsedMessages = finalizeStreamingMessagesWhenIdle(parsedRecord.messages as ChatMessage[], returnedStatus);
|
|
880
|
-
const returnedMessages = runtimeMessageMerger?.category === 'cli'
|
|
881
|
-
&& runtimeMessageMerger.type === adapter.cliType
|
|
882
|
-
&& typeof runtimeMessageMerger.mergeRuntimeChatMessages === 'function'
|
|
883
|
-
? runtimeMessageMerger.mergeRuntimeChatMessages(parsedMessages)
|
|
884
|
-
: parsedMessages;
|
|
885
|
-
LOG.debug('Command', `[read_chat] cli-like parsed provider=${adapter.cliType} target=${String(args?.targetSessionId || '')} adapterStatus=${String(adapterStatus.status || '')} parsedStatus=${String(parsedRecord.status || '')} parsedMsgCount=${parsedRecord.messages.length} returnedMsgCount=${returnedMessages.length}`);
|
|
886
|
-
return buildReadChatCommandResult({
|
|
887
|
-
messages: returnedMessages,
|
|
888
|
-
status: returnedStatus,
|
|
889
|
-
activeModal,
|
|
890
|
-
debugReadChat: {
|
|
891
|
-
provider: adapter.cliType,
|
|
892
|
-
targetSessionId: String(args?.targetSessionId || ''),
|
|
893
|
-
adapterStatus: String(adapterStatus.status || ''),
|
|
894
|
-
parsedStatus: String(parsedRecord.status || ''),
|
|
895
|
-
returnedStatus: String(returnedStatus || ''),
|
|
896
|
-
shouldPreferAdapterMessages: false,
|
|
897
|
-
parsedMsgCount: parsedRecord.messages.length,
|
|
898
|
-
returnedMsgCount: returnedMessages.length,
|
|
899
|
-
},
|
|
900
|
-
...(title ? { title } : {}),
|
|
901
|
-
...(providerSessionId ? { providerSessionId } : {}),
|
|
902
|
-
...(transcriptAuthority ? { transcriptAuthority } : {}),
|
|
903
|
-
...(coverage ? { coverage } : {}),
|
|
904
|
-
}, args);
|
|
905
|
-
}
|
|
906
|
-
const historyLimit = normalizeReadChatTailLimit(args);
|
|
907
|
-
try {
|
|
908
|
-
const agentStr = provider?.type || args?.agentType || getCurrentProviderType(h);
|
|
909
|
-
const workspace = typeof args?.workspace === 'string'
|
|
910
|
-
? args.workspace
|
|
911
|
-
: typeof (h.currentSession as any)?.workspace === 'string'
|
|
912
|
-
? (h.currentSession as any).workspace
|
|
913
|
-
: undefined;
|
|
914
|
-
const history = readProviderChatHistory(agentStr, {
|
|
915
|
-
canonicalHistory: provider?.canonicalHistory,
|
|
916
|
-
historySessionId,
|
|
917
|
-
workspace,
|
|
918
|
-
offset: 0,
|
|
919
|
-
limit: historyLimit,
|
|
920
|
-
excludeRecentCount: 0,
|
|
921
|
-
historyBehavior: provider?.historyBehavior,
|
|
922
|
-
scripts: provider?.scripts as any,
|
|
923
|
-
});
|
|
924
|
-
const historyProviderSessionId = typeof (history as any)?.providerSessionId === 'string'
|
|
925
|
-
? (history as any).providerSessionId
|
|
926
|
-
: historySessionId;
|
|
927
|
-
return buildReadChatCommandResult({
|
|
928
|
-
messages: Array.isArray((history as any)?.messages) ? (history as any).messages : [],
|
|
929
|
-
status: 'idle',
|
|
930
|
-
...(typeof (history as any)?.title === 'string' ? { title: (history as any).title } : {}),
|
|
931
|
-
...(historyProviderSessionId ? { providerSessionId: historyProviderSessionId } : {}),
|
|
932
|
-
...(((provider?.historyBehavior as any)?.transcriptAuthority === 'provider' || (provider?.historyBehavior as any)?.transcriptAuthority === 'daemon')
|
|
933
|
-
? { transcriptAuthority: (provider?.historyBehavior as any).transcriptAuthority }
|
|
934
|
-
: {}),
|
|
935
|
-
coverage: 'tail',
|
|
936
|
-
}, args);
|
|
937
|
-
} catch (error: any) {
|
|
938
|
-
return { success: false, error: error?.message || `${transport} adapter not found` };
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
// Extension transport: evaluateInSession
|
|
943
|
-
if (isExtensionTransport(transport)) {
|
|
944
|
-
let extensionReadChatError = '';
|
|
945
|
-
try {
|
|
946
|
-
const evalResult = await h.evaluateProviderScript('readChat', undefined, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS);
|
|
947
|
-
if (evalResult?.result) {
|
|
948
|
-
let parsed = evalResult.result;
|
|
949
|
-
if (typeof parsed === 'string') {
|
|
950
|
-
try {
|
|
951
|
-
parsed = JSON.parse(parsed);
|
|
952
|
-
} catch (e: any) {
|
|
953
|
-
extensionReadChatError = `extension read_chat parse failed: ${e?.message || String(e)}`;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
if (parsed && typeof parsed === 'object') {
|
|
957
|
-
const validated = validateReadChatResultPayload(parsed, 'extension read_chat');
|
|
958
|
-
_log(`Extension OK: ${validated.messages?.length || 0} msgs`);
|
|
959
|
-
traceProviderEvent(args, 'provider', 'extension.read_chat.success', {
|
|
960
|
-
h,
|
|
961
|
-
provider,
|
|
962
|
-
payload: {
|
|
963
|
-
method: 'evaluateProviderScript',
|
|
964
|
-
result: evalResult.result,
|
|
965
|
-
parsed: validated,
|
|
966
|
-
messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0,
|
|
967
|
-
},
|
|
968
|
-
});
|
|
969
|
-
h.historyWriter.appendNewMessages(
|
|
970
|
-
provider?.type || 'unknown_extension',
|
|
971
|
-
toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
|
|
972
|
-
validated.title,
|
|
973
|
-
args?.targetSessionId,
|
|
974
|
-
historySessionId,
|
|
975
|
-
);
|
|
976
|
-
return buildReadChatCommandResult(validated as Record<string, any>, args);
|
|
977
|
-
}
|
|
978
|
-
if (!extensionReadChatError) {
|
|
979
|
-
extensionReadChatError = 'extension read_chat returned a non-object payload';
|
|
980
|
-
}
|
|
981
|
-
} else {
|
|
982
|
-
extensionReadChatError = 'extension read_chat returned no payload';
|
|
983
|
-
}
|
|
984
|
-
} catch (e: any) {
|
|
985
|
-
extensionReadChatError = `extension read_chat failed: ${e?.message || String(e)}`;
|
|
986
|
-
_log(`Extension error: ${e.message}`);
|
|
987
|
-
traceProviderEvent(args, 'provider', 'extension.read_chat.error', {
|
|
988
|
-
h,
|
|
989
|
-
provider,
|
|
990
|
-
level: 'warn',
|
|
991
|
-
payload: { method: 'evaluateProviderScript', error: e.message },
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
// Alternative: AgentStreamManager (script fail when)
|
|
995
|
-
if (h.agentStream) {
|
|
996
|
-
const cdp = h.getCdp();
|
|
997
|
-
const parentSessionId = h.currentSession?.parentSessionId;
|
|
998
|
-
if (cdp && parentSessionId) {
|
|
999
|
-
const stream = await h.agentStream.collectActiveSession(cdp, parentSessionId);
|
|
1000
|
-
if (stream && stream.agentType !== provider?.type) {
|
|
1001
|
-
return { success: false, error: `extension read_chat stream agent mismatch for ${provider?.type || 'unknown_extension'}` };
|
|
1002
|
-
}
|
|
1003
|
-
if (stream) {
|
|
1004
|
-
h.historyWriter.appendNewMessages(
|
|
1005
|
-
stream.agentType,
|
|
1006
|
-
toHistoryPersistedMessages(stream.messages || []),
|
|
1007
|
-
undefined,
|
|
1008
|
-
args?.targetSessionId,
|
|
1009
|
-
historySessionId,
|
|
1010
|
-
);
|
|
1011
|
-
return buildReadChatCommandResult({
|
|
1012
|
-
messages: stream.messages || [],
|
|
1013
|
-
status: stream.status,
|
|
1014
|
-
agentType: stream.agentType,
|
|
1015
|
-
}, args);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
return { success: false, error: extensionReadChatError || 'extension read_chat unavailable' };
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
// IDE category (default): cdp.evaluate
|
|
1023
|
-
const cdp = h.getCdp();
|
|
1024
|
-
if (!cdp?.isConnected) return { success: false, error: 'CDP not connected' };
|
|
1025
|
-
|
|
1026
|
-
// webview IDE (Kiro, PearAI) → evaluateInWebviewFrame directly use
|
|
1027
|
-
const webviewScript = h.getProviderScript('webviewReadChat') || h.getProviderScript('webview_read_chat');
|
|
1028
|
-
if (webviewScript) {
|
|
1029
|
-
let webviewReadChatError = '';
|
|
1030
|
-
try {
|
|
1031
|
-
const matchText = provider?.webviewMatchText;
|
|
1032
|
-
const matchFn = matchText
|
|
1033
|
-
? (body: string) => body.includes(matchText)
|
|
1034
|
-
: undefined;
|
|
1035
|
-
const raw = await cdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
1036
|
-
if (raw) {
|
|
1037
|
-
let parsed: any = raw;
|
|
1038
|
-
if (typeof parsed === 'string') {
|
|
1039
|
-
try {
|
|
1040
|
-
parsed = JSON.parse(parsed);
|
|
1041
|
-
} catch (e: any) {
|
|
1042
|
-
webviewReadChatError = `webview read_chat parse failed: ${e?.message || String(e)}`;
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
if (parsed && typeof parsed === 'object') {
|
|
1046
|
-
const validated = validateReadChatResultPayload(parsed, 'webview read_chat');
|
|
1047
|
-
_log(`Webview OK: ${validated.messages?.length || 0} msgs`);
|
|
1048
|
-
h.historyWriter.appendNewMessages(
|
|
1049
|
-
provider?.type || getCurrentProviderType(h, 'unknown_webview'),
|
|
1050
|
-
toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
|
|
1051
|
-
validated.title,
|
|
1052
|
-
args?.targetSessionId,
|
|
1053
|
-
historySessionId,
|
|
1054
|
-
);
|
|
1055
|
-
return buildReadChatCommandResult(validated as Record<string, any>, args);
|
|
1056
|
-
}
|
|
1057
|
-
if (!webviewReadChatError) {
|
|
1058
|
-
webviewReadChatError = 'webview read_chat returned a non-object payload';
|
|
1059
|
-
}
|
|
1060
|
-
} else {
|
|
1061
|
-
webviewReadChatError = 'webview read_chat returned no payload';
|
|
1062
|
-
}
|
|
1063
|
-
} catch (e: any) {
|
|
1064
|
-
webviewReadChatError = `webview read_chat failed: ${e?.message || String(e)}`;
|
|
1065
|
-
_log(`Webview readChat error: ${e.message}`);
|
|
1066
|
-
}
|
|
1067
|
-
return { success: false, error: webviewReadChatError || 'webview read_chat unavailable' };
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
// Regular IDE (Cursor, Windsurf, Trae etc) → main DOM evaluate
|
|
1071
|
-
const script = h.getProviderScript('readChat') || h.getProviderScript('read_chat');
|
|
1072
|
-
if (script) {
|
|
1073
|
-
let ideReadChatError = '';
|
|
1074
|
-
try {
|
|
1075
|
-
const evalResult = await h.evaluateProviderScript('readChat', undefined, READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS);
|
|
1076
|
-
if (evalResult?.result) {
|
|
1077
|
-
let parsed: any = evalResult.result;
|
|
1078
|
-
if (typeof parsed === 'string') {
|
|
1079
|
-
try {
|
|
1080
|
-
parsed = JSON.parse(parsed);
|
|
1081
|
-
} catch (e: any) {
|
|
1082
|
-
ideReadChatError = `ide read_chat parse failed: ${e?.message || String(e)}`;
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
if (parsed && typeof parsed === 'object') {
|
|
1086
|
-
const validated = validateReadChatResultPayload(parsed, 'ide read_chat');
|
|
1087
|
-
_log(`OK: ${validated.messages?.length || 0} msgs`);
|
|
1088
|
-
traceProviderEvent(args, 'provider', 'ide.read_chat.success', {
|
|
1089
|
-
h,
|
|
1090
|
-
provider,
|
|
1091
|
-
payload: {
|
|
1092
|
-
method: 'evaluate',
|
|
1093
|
-
result: evalResult.result,
|
|
1094
|
-
parsed: validated,
|
|
1095
|
-
messageCount: Array.isArray(validated.messages) ? validated.messages.length : 0,
|
|
1096
|
-
},
|
|
1097
|
-
});
|
|
1098
|
-
h.historyWriter.appendNewMessages(
|
|
1099
|
-
provider?.type || getCurrentProviderType(h, 'unknown_ide'),
|
|
1100
|
-
toHistoryPersistedMessages(normalizeReadChatMessages(validated)),
|
|
1101
|
-
validated.title,
|
|
1102
|
-
args?.targetSessionId,
|
|
1103
|
-
historySessionId,
|
|
1104
|
-
);
|
|
1105
|
-
return buildReadChatCommandResult(validated as Record<string, any>, args);
|
|
1106
|
-
}
|
|
1107
|
-
if (!ideReadChatError) {
|
|
1108
|
-
ideReadChatError = 'ide read_chat returned a non-object payload';
|
|
1109
|
-
}
|
|
1110
|
-
} else {
|
|
1111
|
-
ideReadChatError = 'ide read_chat returned no payload';
|
|
1112
|
-
}
|
|
1113
|
-
} catch (e: any) {
|
|
1114
|
-
ideReadChatError = `ide read_chat failed: ${e?.message || String(e)}`;
|
|
1115
|
-
LOG.info('Command', `[read_chat] Script error: ${e.message}`);
|
|
1116
|
-
traceProviderEvent(args, 'provider', 'ide.read_chat.error', {
|
|
1117
|
-
h,
|
|
1118
|
-
provider,
|
|
1119
|
-
level: 'warn',
|
|
1120
|
-
payload: { method: 'evaluate', error: e.message },
|
|
1121
|
-
});
|
|
1122
|
-
}
|
|
1123
|
-
return { success: false, error: ideReadChatError || 'ide read_chat unavailable' };
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
return { success: false, error: 'read_chat unavailable' };
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
export async function handleSendChat(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1130
|
-
const input = getSendChatInputEnvelope(args);
|
|
1131
|
-
const text = input.textFallback;
|
|
1132
|
-
const hasInput = input.parts.length > 0 || (typeof text === 'string' && text.trim().length > 0);
|
|
1133
|
-
if (!hasInput) return { success: false, error: 'input required' };
|
|
1134
|
-
const _log = (msg: string) => LOG.debug('Command', `[send_chat] ${msg}`);
|
|
1135
|
-
const provider = h.getProvider(args?.agentType);
|
|
1136
|
-
const transport = getTargetTransport(h, provider);
|
|
1137
|
-
const dedupeKey = buildRecentSendKey(h, args, provider, buildSendInputSignature(input));
|
|
1138
|
-
|
|
1139
|
-
const _logSendSuccess = (method: string, targetAgent?: string) => {
|
|
1140
|
-
// Sending and transcript persistence are intentionally decoupled.
|
|
1141
|
-
// User turns should reach history through read_chat/runtime transcript sync,
|
|
1142
|
-
// not by eagerly appending the outgoing input here.
|
|
1143
|
-
return { success: true, sent: true, method, targetAgent };
|
|
1144
|
-
};
|
|
1145
|
-
|
|
1146
|
-
if (isRecentDuplicateSend(dedupeKey)) {
|
|
1147
|
-
_log(`Suppressed duplicate send for ${dedupeKey}`);
|
|
1148
|
-
return { success: true, sent: false, deduplicated: true };
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
if (transport === 'acp') {
|
|
1152
|
-
const target = getTargetInstance(h, args);
|
|
1153
|
-
if (!target || target.category !== 'acp') {
|
|
1154
|
-
return { success: false, error: `ACP instance not found for ${provider?.type || args?.agentType || 'unknown'}` };
|
|
1155
|
-
}
|
|
1156
|
-
try {
|
|
1157
|
-
assertProviderSupportsDeclaredInput(provider, input);
|
|
1158
|
-
target.onEvent('send_message', { input });
|
|
1159
|
-
return _logSendSuccess('acp-instance', target.type);
|
|
1160
|
-
} catch (e: any) {
|
|
1161
|
-
return { success: false, error: `acp send failed: ${e.message}` };
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
// PTY transport: route structured input through the provider instance so
|
|
1166
|
-
// provider-specific CLI attachment strategies (for example Hermes file-path
|
|
1167
|
-
// image prompts) are applied instead of collapsing everything to text.
|
|
1168
|
-
if (transport === 'pty') {
|
|
1169
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1170
|
-
if (adapter) {
|
|
1171
|
-
_log(`${transport} adapter: ${adapter.cliType}`);
|
|
1172
|
-
try {
|
|
1173
|
-
const hasStructuredParts = input.parts.some((part) => part.type !== 'text');
|
|
1174
|
-
if (hasStructuredParts) {
|
|
1175
|
-
const target = getTargetInstance(h, args);
|
|
1176
|
-
if (!target || target.category !== 'cli') {
|
|
1177
|
-
return { success: false, error: `CLI instance not found for ${provider?.type || args?.agentType || 'unknown'}` };
|
|
1178
|
-
}
|
|
1179
|
-
assertProviderSupportsDeclaredInput(provider, input);
|
|
1180
|
-
await waitOnceForFreshHermesCliStart(adapter, _log);
|
|
1181
|
-
target.onEvent('send_message', { input });
|
|
1182
|
-
return _logSendSuccess(`${transport}-instance`, target.type);
|
|
1183
|
-
}
|
|
1184
|
-
assertTextOnlyInput(provider, input);
|
|
1185
|
-
if (!text) return { success: false, error: 'text required for PTY send' };
|
|
1186
|
-
await waitOnceForFreshHermesCliStart(adapter, _log);
|
|
1187
|
-
await adapter.sendMessage(text);
|
|
1188
|
-
return _logSendSuccess(`${transport}-adapter`, adapter.cliType);
|
|
1189
|
-
} catch (e: any) {
|
|
1190
|
-
return { success: false, error: `${transport} send failed: ${e.message}` };
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
assertTextOnlyInput(provider, input);
|
|
1196
|
-
if (!text) return { success: false, error: 'text required' };
|
|
1197
|
-
|
|
1198
|
-
// Extension transport: via AgentStreamManager
|
|
1199
|
-
if (isExtensionTransport(transport)) {
|
|
1200
|
-
_log(`Extension: ${provider?.type || 'unknown_extension'}`);
|
|
1201
|
-
// Method 1: provider sendMessage script via evaluateInSession
|
|
1202
|
-
try {
|
|
1203
|
-
const beforeState = await getStableExtensionBaseline(h);
|
|
1204
|
-
const evalResult = await h.evaluateProviderScript('sendMessage', { message: text }, 30000);
|
|
1205
|
-
if (evalResult?.result) {
|
|
1206
|
-
const parsed = parseMaybeJson(evalResult.result);
|
|
1207
|
-
if (didProviderConfirmSend(parsed)) {
|
|
1208
|
-
const observed = await verifyExtensionSendObserved(h, beforeState);
|
|
1209
|
-
if (observed) {
|
|
1210
|
-
_log(`Extension script sent OK`);
|
|
1211
|
-
return _logSendSuccess('extension-script');
|
|
1212
|
-
}
|
|
1213
|
-
_log(`Extension script reported send but no chat-state change was observed`);
|
|
1214
|
-
}
|
|
1215
|
-
if (parsed?.needsTypeAndSend) {
|
|
1216
|
-
_log(`Extension needsTypeAndSend → AgentStreamManager`);
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
} catch (e: any) {
|
|
1220
|
-
_log(`Extension script error: ${e.message}`);
|
|
1221
|
-
}
|
|
1222
|
-
// Method 2: AgentStreamManager
|
|
1223
|
-
const extensionSessionId = h.currentSession?.sessionId;
|
|
1224
|
-
if (h.agentStream && h.getCdp() && extensionSessionId) {
|
|
1225
|
-
const ok = await h.agentStream.sendToSession(h.getCdp()!, extensionSessionId, text);
|
|
1226
|
-
if (ok) {
|
|
1227
|
-
_log(`AgentStreamManager sent OK`);
|
|
1228
|
-
return _logSendSuccess('agent-stream');
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1231
|
-
return { success: false, error: `Extension '${provider?.type || 'unknown_extension'}' send failed` };
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
// IDE category (default): provider sendMessage script is authoritative when present.
|
|
1235
|
-
const targetCdp = h.getCdp();
|
|
1236
|
-
if (!targetCdp?.isConnected) {
|
|
1237
|
-
const managerKey = getCurrentManagerKey(h);
|
|
1238
|
-
_log(`No CDP for ${managerKey}`);
|
|
1239
|
-
return { success: false, error: `CDP for ${managerKey || 'unknown'} not connected` };
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
_log(`Targeting IDE: ${getCurrentManagerKey(h)}`);
|
|
1243
|
-
const sendScript = h.getProviderScript('sendMessage', { message: text });
|
|
1244
|
-
if (sendScript) {
|
|
1245
|
-
try {
|
|
1246
|
-
const result = await targetCdp.evaluate(sendScript, 30000);
|
|
1247
|
-
const parsed: any = parseMaybeJson(result);
|
|
1248
|
-
if (didProviderConfirmSend(parsed)) {
|
|
1249
|
-
_log(`sendMessage script OK`);
|
|
1250
|
-
return _logSendSuccess('script');
|
|
1251
|
-
}
|
|
1252
|
-
if (parsed?.needsTypeAndSend && parsed?.selector) {
|
|
1253
|
-
try {
|
|
1254
|
-
const sent = await targetCdp.typeAndSend(parsed.selector, text);
|
|
1255
|
-
if (sent) {
|
|
1256
|
-
_log(`typeAndSend(script.selector=${parsed.selector}) success`);
|
|
1257
|
-
return _logSendSuccess('typeAndSend-script');
|
|
1258
|
-
}
|
|
1259
|
-
} catch (e: any) {
|
|
1260
|
-
_log(`typeAndSend(script.selector) failed: ${e.message}`);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
|
|
1264
|
-
try {
|
|
1265
|
-
const { x, y } = parsed.clickCoords;
|
|
1266
|
-
const sent = await targetCdp.typeAndSendAt(x, y, text);
|
|
1267
|
-
if (sent) {
|
|
1268
|
-
_log(`typeAndSendAt(${x},${y}) success`);
|
|
1269
|
-
return _logSendSuccess('typeAndSendAt-script');
|
|
1270
|
-
}
|
|
1271
|
-
} catch (e: any) {
|
|
1272
|
-
_log(`typeAndSendAt failed: ${e.message}`);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
if (parsed?.needsTypeAndSend && provider?.inputMethod === 'cdp-type-and-send' && provider.inputSelector) {
|
|
1276
|
-
try {
|
|
1277
|
-
const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
|
|
1278
|
-
if (sent) {
|
|
1279
|
-
_log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
|
|
1280
|
-
return _logSendSuccess('typeAndSend-provider');
|
|
1281
|
-
}
|
|
1282
|
-
} catch (e: any) {
|
|
1283
|
-
_log(`typeAndSend(provider) failed: ${e.message}`);
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
if (parsed?.needsTypeAndSend && provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
|
|
1287
|
-
try {
|
|
1288
|
-
const webviewScript = callLegacyTextScript(provider.scripts.webviewSendMessage, text);
|
|
1289
|
-
if (webviewScript && targetCdp.evaluateInWebviewFrame) {
|
|
1290
|
-
const matchText = provider.webviewMatchText;
|
|
1291
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1292
|
-
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
1293
|
-
const wvParsed: any = parseMaybeJson(wvResult);
|
|
1294
|
-
if (didProviderConfirmSend(wvParsed)) {
|
|
1295
|
-
_log(`webviewSendMessage OK`);
|
|
1296
|
-
return _logSendSuccess('webview-script');
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
} catch (e: any) {
|
|
1300
|
-
_log(`webviewSendMessage failed: ${e.message}`);
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
return { success: false, error: parsed?.error || 'Provider sendMessage did not confirm send' };
|
|
1304
|
-
} catch (e: any) {
|
|
1305
|
-
_log(`sendMessage script failed: ${e.message}`);
|
|
1306
|
-
return { success: false, error: `Provider sendMessage failed: ${e.message}` };
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
|
|
1310
|
-
if (provider?.webviewMatchText && provider?.scripts?.webviewSendMessage) {
|
|
1311
|
-
try {
|
|
1312
|
-
const webviewScript = callLegacyTextScript(provider.scripts.webviewSendMessage, text);
|
|
1313
|
-
if (webviewScript && targetCdp.evaluateInWebviewFrame) {
|
|
1314
|
-
const matchText = provider.webviewMatchText;
|
|
1315
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1316
|
-
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
1317
|
-
const wvParsed: any = parseMaybeJson(wvResult);
|
|
1318
|
-
if (didProviderConfirmSend(wvParsed)) {
|
|
1319
|
-
_log(`webviewSendMessage OK`);
|
|
1320
|
-
return _logSendSuccess('webview-script');
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
} catch (e: any) {
|
|
1324
|
-
_log(`webviewSendMessage failed: ${e.message}`);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
if (provider?.inputMethod === 'cdp-type-and-send' && provider.inputSelector) {
|
|
1329
|
-
try {
|
|
1330
|
-
const sent = await targetCdp.typeAndSend(provider.inputSelector, text);
|
|
1331
|
-
if (sent) {
|
|
1332
|
-
_log(`typeAndSend(provider.inputSelector=${provider.inputSelector}) success`);
|
|
1333
|
-
return _logSendSuccess('typeAndSend-provider');
|
|
1334
|
-
}
|
|
1335
|
-
} catch (e: any) {
|
|
1336
|
-
_log(`typeAndSend(provider) failed: ${e.message}`);
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
_log('All methods failed');
|
|
1341
|
-
return { success: false, error: 'No provider method could send the message' };
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
export async function handleListChats(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1345
|
-
const provider = h.getProvider(args?.agentType);
|
|
1346
|
-
const transport = getTargetTransport(h, provider);
|
|
1347
|
-
|
|
1348
|
-
// Extension transport: via AgentStreamManager
|
|
1349
|
-
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
1350
|
-
try {
|
|
1351
|
-
const chats = await h.agentStream.listSessionChats(h.getCdp()!, h.currentSession.sessionId);
|
|
1352
|
-
LOG.info('Command', `[list_chats] Extension: ${chats.length} chats`);
|
|
1353
|
-
return { success: true, chats };
|
|
1354
|
-
} catch (e: any) {
|
|
1355
|
-
LOG.info('Command', `[list_chats] Extension error: ${e.message}`);
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
// webview IDE
|
|
1360
|
-
try {
|
|
1361
|
-
const webviewScript = h.getProviderScript('webviewListSessions') || h.getProviderScript('webview_list_sessions');
|
|
1362
|
-
if (webviewScript) {
|
|
1363
|
-
const matchText = provider?.webviewMatchText;
|
|
1364
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1365
|
-
const raw = await h.getCdp()?.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
1366
|
-
let parsed: any = raw;
|
|
1367
|
-
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
1368
|
-
if (parsed?.sessions) {
|
|
1369
|
-
LOG.info('Command', `[list_chats] Webview OK: ${parsed.sessions.length} chats`);
|
|
1370
|
-
return { success: true, chats: parsed.sessions };
|
|
1371
|
-
}
|
|
1372
|
-
}
|
|
1373
|
-
} catch (e: any) {
|
|
1374
|
-
LOG.info('Command', `[list_chats] Webview error: ${e.message}`);
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// IDE/default: evaluateProviderScript
|
|
1378
|
-
try {
|
|
1379
|
-
const evalResult = await h.evaluateProviderScript('listSessions');
|
|
1380
|
-
if (evalResult) {
|
|
1381
|
-
let parsed = evalResult.result;
|
|
1382
|
-
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
1383
|
-
if (parsed?.sessions && Array.isArray(parsed.sessions)) {
|
|
1384
|
-
LOG.info('Command', `[list_chats] OK: ${parsed.sessions.length} chats`);
|
|
1385
|
-
return { success: true, chats: parsed.sessions };
|
|
1386
|
-
}
|
|
1387
|
-
if (parsed?.chats && Array.isArray(parsed.chats)) {
|
|
1388
|
-
LOG.info('Command', `[list_chats] OK: ${parsed.chats.length} chats`);
|
|
1389
|
-
return { success: true, chats: parsed.chats };
|
|
1390
|
-
}
|
|
1391
|
-
if (Array.isArray(parsed)) {
|
|
1392
|
-
LOG.info('Command', `[list_chats] OK: ${parsed.length} chats`);
|
|
1393
|
-
return { success: true, chats: parsed };
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
} catch (e: any) {
|
|
1397
|
-
LOG.info('Command', `[list_chats] error: ${e.message}`);
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
return { success: false, error: 'listSessions script not available for this provider' };
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
export async function handleNewChat(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1404
|
-
const provider = h.getProvider(args?.agentType);
|
|
1405
|
-
const transport = getTargetTransport(h, provider);
|
|
1406
|
-
|
|
1407
|
-
if (transport === 'pty') {
|
|
1408
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1409
|
-
if (!adapter) return { success: false, error: 'CLI adapter not running' };
|
|
1410
|
-
if (typeof adapter.clearHistory === 'function') {
|
|
1411
|
-
adapter.clearHistory();
|
|
1412
|
-
return { success: true, cleared: true };
|
|
1413
|
-
}
|
|
1414
|
-
return { success: false, error: 'new_chat not supported by this CLI provider' };
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
1418
|
-
const ok = await h.agentStream.newSession(h.getCdp()!, h.currentSession.sessionId);
|
|
1419
|
-
return { success: ok };
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
// webview IDE
|
|
1423
|
-
try {
|
|
1424
|
-
const webviewScript = h.getProviderScript('webviewNewSession') || h.getProviderScript('webview_new_session');
|
|
1425
|
-
if (webviewScript) {
|
|
1426
|
-
const matchText = provider?.webviewMatchText;
|
|
1427
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1428
|
-
const raw = await h.getCdp()?.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
1429
|
-
if (raw) return { success: true, result: raw };
|
|
1430
|
-
}
|
|
1431
|
-
} catch (e: any) {
|
|
1432
|
-
return { success: false, error: `webviewNewSession failed: ${e.message}` };
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
try {
|
|
1436
|
-
const evalResult = await h.evaluateProviderScript('newSession');
|
|
1437
|
-
if (evalResult) return { success: true };
|
|
1438
|
-
} catch (e: any) {
|
|
1439
|
-
return { success: false, error: `newSession failed: ${e.message}` };
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
return { success: false, error: 'newSession script not available for this provider' };
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
export async function handleSwitchChat(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1446
|
-
const provider = h.getProvider(args?.agentType);
|
|
1447
|
-
const transport = getTargetTransport(h, provider);
|
|
1448
|
-
const managerKey = getCurrentManagerKey(h);
|
|
1449
|
-
const sessionId = args?.sessionId || args?.id || args?.chatId;
|
|
1450
|
-
if (!sessionId) return { success: false, error: 'sessionId required' };
|
|
1451
|
-
LOG.info('Command', `[switch_chat] sessionId=${sessionId}, manager=${managerKey}`);
|
|
1452
|
-
|
|
1453
|
-
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
1454
|
-
const ok = await h.agentStream.switchConversation(h.getCdp()!, h.currentSession.sessionId, sessionId);
|
|
1455
|
-
return { success: ok, result: ok ? 'switched' : 'failed' };
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
const cdp = h.getCdp(managerKey);
|
|
1459
|
-
if (!cdp?.isConnected) return { success: false, error: 'CDP not connected' };
|
|
1460
|
-
|
|
1461
|
-
// webview IDE
|
|
1462
|
-
try {
|
|
1463
|
-
const webviewScript = h.getProviderScript('webviewSwitchSession', { SESSION_ID: JSON.stringify(sessionId) });
|
|
1464
|
-
if (webviewScript) {
|
|
1465
|
-
const matchText = provider?.webviewMatchText;
|
|
1466
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1467
|
-
const raw = await cdp.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
1468
|
-
if (raw) return { success: true, result: raw };
|
|
1469
|
-
}
|
|
1470
|
-
} catch (e: any) {
|
|
1471
|
-
return { success: false, error: `webviewSwitchSession failed: ${e.message}` };
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
const switchParams = {
|
|
1475
|
-
sessionId,
|
|
1476
|
-
title: sessionId,
|
|
1477
|
-
id: sessionId,
|
|
1478
|
-
SESSION_ID: JSON.stringify(sessionId),
|
|
1479
|
-
};
|
|
1480
|
-
const script = h.getProviderScript('switchSession', switchParams)
|
|
1481
|
-
|| h.getProviderScript('switch_session', switchParams);
|
|
1482
|
-
if (!script) return { success: false, error: 'switch_session script not available' };
|
|
1483
|
-
|
|
1484
|
-
try {
|
|
1485
|
-
const raw = await cdp.evaluate(script, 15000);
|
|
1486
|
-
LOG.info('Command', `[switch_chat] result: ${raw}`);
|
|
1487
|
-
|
|
1488
|
-
let parsed: any = null;
|
|
1489
|
-
try { parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch { }
|
|
1490
|
-
|
|
1491
|
-
if (parsed?.action === 'click' && parsed.clickX && parsed.clickY) {
|
|
1492
|
-
const x = Math.round(parsed.clickX);
|
|
1493
|
-
const y = Math.round(parsed.clickY);
|
|
1494
|
-
LOG.info('Command', `[switch_chat] CDP click at (${x}, ${y}) for "${parsed.title}"`);
|
|
1495
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1496
|
-
type: 'mousePressed', x, y, button: 'left', clickCount: 1
|
|
1497
|
-
});
|
|
1498
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1499
|
-
type: 'mouseReleased', x, y, button: 'left', clickCount: 1
|
|
1500
|
-
});
|
|
1501
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
1502
|
-
|
|
1503
|
-
// Auto-handle workspace selection dialog
|
|
1504
|
-
const wsResult = await cdp.evaluate(`
|
|
1505
|
-
(() => {
|
|
1506
|
-
const inp = Array.from(document.querySelectorAll('input[type="text"]'))
|
|
1507
|
-
.find(i => i.offsetWidth > 0 && (i.placeholder || '').includes('Select where'));
|
|
1508
|
-
if (!inp) return null;
|
|
1509
|
-
const rows = inp.closest('[class*="quickInput"]')?.querySelectorAll('[class*="cursor-pointer"]');
|
|
1510
|
-
if (rows && rows.length > 0) {
|
|
1511
|
-
const r = rows[0].getBoundingClientRect();
|
|
1512
|
-
return JSON.stringify({ x: Math.round(r.left + r.width/2), y: Math.round(r.top + r.height/2) });
|
|
1513
|
-
}
|
|
1514
|
-
return null;
|
|
1515
|
-
})()
|
|
1516
|
-
`, 5000);
|
|
1517
|
-
if (wsResult) {
|
|
1518
|
-
try {
|
|
1519
|
-
const ws = JSON.parse(wsResult as string);
|
|
1520
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1521
|
-
type: 'mousePressed', x: ws.x, y: ws.y, button: 'left', clickCount: 1
|
|
1522
|
-
});
|
|
1523
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1524
|
-
type: 'mouseReleased', x: ws.x, y: ws.y, button: 'left', clickCount: 1
|
|
1525
|
-
});
|
|
1526
|
-
} catch { }
|
|
1527
|
-
}
|
|
1528
|
-
return { success: true, result: 'switched' };
|
|
1529
|
-
}
|
|
1530
|
-
|
|
1531
|
-
if (parsed?.error) return { success: false, error: parsed.error };
|
|
1532
|
-
return { success: true, result: raw };
|
|
1533
|
-
} catch (e: any) {
|
|
1534
|
-
LOG.error('Command', `[switch_chat] error: ${e.message}`);
|
|
1535
|
-
return { success: false, error: e.message };
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
export async function handleSetMode(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1540
|
-
const provider = h.getProvider(args?.agentType);
|
|
1541
|
-
const transport = getTargetTransport(h, provider);
|
|
1542
|
-
const mode = args?.mode || 'agent';
|
|
1543
|
-
|
|
1544
|
-
// ACP transport
|
|
1545
|
-
if (transport === 'acp') {
|
|
1546
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1547
|
-
const acpInstance = adapter?._acpInstance;
|
|
1548
|
-
if (acpInstance && typeof acpInstance.setMode === 'function') {
|
|
1549
|
-
await acpInstance.setMode(mode);
|
|
1550
|
-
return { success: true, mode };
|
|
1551
|
-
}
|
|
1552
|
-
return { success: false, error: 'ACP adapter not found' };
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
// 1. webview setMode
|
|
1556
|
-
const webviewScript = h.getProviderScript('webviewSetMode', { MODE: JSON.stringify(mode) });
|
|
1557
|
-
if (webviewScript) {
|
|
1558
|
-
const cdp = h.getCdp();
|
|
1559
|
-
if (cdp?.isConnected) {
|
|
1560
|
-
try {
|
|
1561
|
-
const matchText = provider?.webviewMatchText;
|
|
1562
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1563
|
-
const raw = await cdp.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
1564
|
-
let result: any = raw;
|
|
1565
|
-
if (typeof raw === 'string') { try { result = JSON.parse(raw); } catch { } }
|
|
1566
|
-
if (result?.success) return { success: true, mode, method: 'webview-script' };
|
|
1567
|
-
} catch (e: any) {
|
|
1568
|
-
LOG.info('Command', `[set_mode] webview script error: ${e.message}`);
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
// 2. main frame setMode
|
|
1574
|
-
const mainScript = h.getProviderScript('setMode', { MODE: JSON.stringify(mode) });
|
|
1575
|
-
if (mainScript) {
|
|
1576
|
-
try {
|
|
1577
|
-
const evalResult = await h.evaluateProviderScript('setMode', { MODE: JSON.stringify(mode) }, 15000);
|
|
1578
|
-
if (evalResult?.result) {
|
|
1579
|
-
let parsed = evalResult.result;
|
|
1580
|
-
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
1581
|
-
if (parsed?.success) return { success: true, mode, method: 'script' };
|
|
1582
|
-
}
|
|
1583
|
-
} catch (e: any) {
|
|
1584
|
-
LOG.info('Command', `[set_mode] script error: ${e.message}`);
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
return { success: false, error: `setMode '${mode}' not supported by this provider` };
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
export async function handleChangeModel(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1592
|
-
const provider = h.getProvider(args?.agentType);
|
|
1593
|
-
const transport = getTargetTransport(h, provider);
|
|
1594
|
-
const model = args?.model;
|
|
1595
|
-
|
|
1596
|
-
LOG.info('Command', `[change_model] model=${model} provider=${provider?.type} transport=${transport} manager=${getCurrentManagerKey(h)} providerType=${getCurrentProviderType(h)}`);
|
|
1597
|
-
|
|
1598
|
-
// ACP transport
|
|
1599
|
-
if (transport === 'acp') {
|
|
1600
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1601
|
-
LOG.info('Command', `[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
|
|
1602
|
-
const acpInstance = adapter?._acpInstance;
|
|
1603
|
-
if (acpInstance && typeof acpInstance.setConfigOption === 'function') {
|
|
1604
|
-
await acpInstance.setConfigOption('model', model);
|
|
1605
|
-
LOG.info('Command', `[change_model] Updated ACP model to ${model}`);
|
|
1606
|
-
return { success: true, model };
|
|
1607
|
-
}
|
|
1608
|
-
return { success: false, error: 'ACP adapter not found' };
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
// 1. webview setModel
|
|
1612
|
-
const webviewScript = h.getProviderScript('webviewSetModel', { MODEL: JSON.stringify(model) });
|
|
1613
|
-
if (webviewScript) {
|
|
1614
|
-
const cdp = h.getCdp();
|
|
1615
|
-
if (cdp?.isConnected) {
|
|
1616
|
-
try {
|
|
1617
|
-
const matchText = provider?.webviewMatchText;
|
|
1618
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1619
|
-
const raw = await cdp.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
1620
|
-
let result: any = raw;
|
|
1621
|
-
if (typeof raw === 'string') { try { result = JSON.parse(raw); } catch { } }
|
|
1622
|
-
if (result?.success) return { success: true, model, method: 'webview-script' };
|
|
1623
|
-
} catch (e: any) {
|
|
1624
|
-
LOG.info('Command', `[change_model] webview script error: ${e.message}`);
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
// 2. main frame setModel
|
|
1630
|
-
const mainScript = h.getProviderScript('setModel', { MODEL: JSON.stringify(model) });
|
|
1631
|
-
if (mainScript) {
|
|
1632
|
-
try {
|
|
1633
|
-
const evalResult = await h.evaluateProviderScript('setModel', { MODEL: JSON.stringify(model) }, 15000);
|
|
1634
|
-
if (evalResult?.result) {
|
|
1635
|
-
let parsed = evalResult.result;
|
|
1636
|
-
if (typeof parsed === 'string') { try { parsed = JSON.parse(parsed); } catch { } }
|
|
1637
|
-
if (parsed?.success) return { success: true, model, method: 'script' };
|
|
1638
|
-
}
|
|
1639
|
-
} catch (e: any) {
|
|
1640
|
-
LOG.info('Command', `[change_model] script error: ${e.message}`);
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
return { success: false, error: 'changeModel not supported by this IDE provider' };
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
export async function handleSetThoughtLevel(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1648
|
-
const configId = args?.configId;
|
|
1649
|
-
const value = args?.value;
|
|
1650
|
-
if (!configId || !value) return { success: false, error: 'configId and value required' };
|
|
1651
|
-
|
|
1652
|
-
const provider = h.getProvider(args?.agentType);
|
|
1653
|
-
const transport = getTargetTransport(h, provider);
|
|
1654
|
-
if (transport !== 'acp') {
|
|
1655
|
-
return { success: false, error: 'set_thought_level only for ACP providers' };
|
|
1656
|
-
}
|
|
1657
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1658
|
-
const acpInstance = adapter?._acpInstance;
|
|
1659
|
-
if (!acpInstance) return { success: false, error: 'ACP instance not found' };
|
|
1660
|
-
if (typeof acpInstance.setConfigOption !== 'function') {
|
|
1661
|
-
return { success: false, error: 'ACP setConfigOption not available' };
|
|
1662
|
-
}
|
|
1663
|
-
|
|
1664
|
-
try {
|
|
1665
|
-
await acpInstance.setConfigOption(configId, value);
|
|
1666
|
-
LOG.info('Command', `[set_thought_level] ${configId}=${value} for ${provider?.type || 'unknown_acp'}`);
|
|
1667
|
-
return { success: true, configId, value };
|
|
1668
|
-
} catch (e: any) {
|
|
1669
|
-
return { success: false, error: e?.message };
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
export async function handleResolveAction(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
1674
|
-
const provider = h.getProvider(args?.agentType);
|
|
1675
|
-
const transport = getTargetTransport(h, provider);
|
|
1676
|
-
const action = args?.action || 'approve';
|
|
1677
|
-
const button = args?.button || args?.buttonText
|
|
1678
|
-
|| (action === 'approve' ? 'Accept' : action === 'reject' ? 'Reject' : 'Accept');
|
|
1679
|
-
|
|
1680
|
-
LOG.info('Command', `[resolveAction] action=${action} button="${button}" provider=${provider?.type}`);
|
|
1681
|
-
|
|
1682
|
-
// 0. PTY transport: navigate approval dialog via PTY arrow keys + Enter
|
|
1683
|
-
if (transport === 'pty') {
|
|
1684
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1685
|
-
if (!adapter) return { success: false, error: 'CLI adapter not running' };
|
|
1686
|
-
|
|
1687
|
-
// Handle data-driven resolve actions (like from the dashboard 'Fix' button)
|
|
1688
|
-
if (args?.data && typeof adapter.resolveAction === 'function') {
|
|
1689
|
-
try {
|
|
1690
|
-
await adapter.resolveAction(args.data);
|
|
1691
|
-
LOG.info('Command', `[resolveAction] CLI PTY → resolveAction triggered with data payload`);
|
|
1692
|
-
return { success: true, method: 'cli-resolve-action' };
|
|
1693
|
-
} catch (e: any) {
|
|
1694
|
-
return { success: false, error: `CLI resolveAction failed: ${e.message}` };
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
const status = adapter.getStatus();
|
|
1699
|
-
const targetInstance = getTargetInstance(h, args);
|
|
1700
|
-
const targetState = targetInstance?.getState?.() as { activeChat?: { status?: string; activeModal?: { message?: string; buttons?: string[] } | null } } | undefined;
|
|
1701
|
-
const surfacedModal = targetState?.activeChat?.activeModal && Array.isArray(targetState.activeChat.activeModal.buttons)
|
|
1702
|
-
&& targetState.activeChat.activeModal.buttons.some((candidate) => typeof candidate === 'string' && candidate.trim())
|
|
1703
|
-
? targetState.activeChat.activeModal
|
|
1704
|
-
: null;
|
|
1705
|
-
const statusModal = status?.activeModal && Array.isArray(status.activeModal.buttons)
|
|
1706
|
-
&& status.activeModal.buttons.some((candidate) => typeof candidate === 'string' && candidate.trim())
|
|
1707
|
-
? status.activeModal
|
|
1708
|
-
: null;
|
|
1709
|
-
const parsedStatus = !statusModal && !surfacedModal && typeof adapter.getScriptParsedStatus === 'function'
|
|
1710
|
-
? (() => {
|
|
1711
|
-
try {
|
|
1712
|
-
return parseMaybeJson(adapter.getScriptParsedStatus());
|
|
1713
|
-
} catch {
|
|
1714
|
-
return null;
|
|
1715
|
-
}
|
|
1716
|
-
})()
|
|
1717
|
-
: null;
|
|
1718
|
-
const parsedModal = parsedStatus?.status === 'waiting_approval'
|
|
1719
|
-
&& parsedStatus?.activeModal
|
|
1720
|
-
&& Array.isArray(parsedStatus.activeModal.buttons)
|
|
1721
|
-
&& parsedStatus.activeModal.buttons.some((candidate: unknown) => typeof candidate === 'string' && candidate.trim())
|
|
1722
|
-
? parsedStatus.activeModal
|
|
1723
|
-
: null;
|
|
1724
|
-
const effectiveModal = statusModal || surfacedModal || parsedModal;
|
|
1725
|
-
const effectiveStatus = status?.status === 'waiting_approval' || targetState?.activeChat?.status === 'waiting_approval' || parsedStatus?.status === 'waiting_approval'
|
|
1726
|
-
? 'waiting_approval'
|
|
1727
|
-
: status?.status;
|
|
1728
|
-
LOG.info('Command', `[resolveAction] CLI PTY gate target=${String(args?.targetSessionId || '')} rawStatus=${String(status?.status || '')} effectiveStatus=${String(effectiveStatus || '')} statusModal=${statusModal ? 'yes' : 'no'} surfacedModal=${surfacedModal ? 'yes' : 'no'} parsedModal=${parsedModal ? 'yes' : 'no'} instance=${targetInstance ? 'yes' : 'no'}`);
|
|
1729
|
-
if (!effectiveModal) {
|
|
1730
|
-
return { success: false, error: 'Not in approval state' };
|
|
1731
|
-
}
|
|
1732
|
-
const buttons: string[] = Array.isArray(effectiveModal.buttons) ? effectiveModal.buttons : [];
|
|
1733
|
-
// Resolve button index: explicit buttonIndex arg → exact text match → explicit action mapping
|
|
1734
|
-
let buttonIndex = typeof args?.buttonIndex === 'number' ? args.buttonIndex : -1;
|
|
1735
|
-
if (buttonIndex < 0 && button) {
|
|
1736
|
-
const btnLower = button.toLowerCase();
|
|
1737
|
-
buttonIndex = buttons.findIndex(b => b.toLowerCase().includes(btnLower));
|
|
1738
|
-
}
|
|
1739
|
-
if (buttonIndex < 0 && (action === 'reject' || action === 'deny')) {
|
|
1740
|
-
buttonIndex = buttons.findIndex(b => /deny|reject|no/i.test(b));
|
|
1741
|
-
}
|
|
1742
|
-
if (buttonIndex < 0 && (action === 'always' || /always/i.test(button))) {
|
|
1743
|
-
buttonIndex = buttons.findIndex(b => /always/i.test(b));
|
|
1744
|
-
}
|
|
1745
|
-
if (buttonIndex < 0) {
|
|
1746
|
-
return { success: false, error: 'Approval action did not match any visible button' };
|
|
1747
|
-
}
|
|
1748
|
-
if (typeof adapter.resolveModal === 'function') {
|
|
1749
|
-
adapter.resolveModal(buttonIndex);
|
|
1750
|
-
} else {
|
|
1751
|
-
const keys = '\x1B[B'.repeat(Math.max(0, buttonIndex)) + '\r';
|
|
1752
|
-
adapter.writeRaw?.(keys);
|
|
1753
|
-
}
|
|
1754
|
-
LOG.info('Command', `[resolveAction] CLI PTY → buttonIndex=${buttonIndex} "${buttons[buttonIndex] ?? '?'}"`);
|
|
1755
|
-
getTargetInstance(h, args)?.recordApprovalSelection?.(buttons[buttonIndex] ?? button);
|
|
1756
|
-
return { success: true, buttonIndex, button: buttons[buttonIndex] ?? button };
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// 1. Extension transport: via AgentStreamManager
|
|
1760
|
-
if (isExtensionTransport(transport) && h.agentStream && h.getCdp() && h.currentSession?.sessionId) {
|
|
1761
|
-
const ok = await h.agentStream.resolveSessionAction(h.getCdp()!, h.currentSession.sessionId, action, button);
|
|
1762
|
-
return { success: ok };
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
// 1.5 ACP transport: resolve protocol permission request directly
|
|
1766
|
-
if (transport === 'acp') {
|
|
1767
|
-
const adapter = getTargetedCliAdapter(h, args, provider?.type);
|
|
1768
|
-
const acpInstance = adapter?._acpInstance;
|
|
1769
|
-
if (!acpInstance) return { success: false, error: 'ACP instance not found' };
|
|
1770
|
-
if (typeof acpInstance.resolvePermission !== 'function') {
|
|
1771
|
-
return { success: false, error: 'ACP resolvePermission not available' };
|
|
1772
|
-
}
|
|
1773
|
-
|
|
1774
|
-
try {
|
|
1775
|
-
await acpInstance.resolvePermission(action === 'approve' || action === 'accept' || action === 'always');
|
|
1776
|
-
LOG.info('Command', `[resolveAction] ACP → ${action}`);
|
|
1777
|
-
return { success: true, action };
|
|
1778
|
-
} catch (e: any) {
|
|
1779
|
-
return { success: false, error: e?.message || 'ACP resolve action failed' };
|
|
1780
|
-
}
|
|
1781
|
-
}
|
|
1782
|
-
|
|
1783
|
-
// 2. Webview Provider script
|
|
1784
|
-
if (provider?.scripts?.webviewResolveAction || provider?.scripts?.webview_resolve_action) {
|
|
1785
|
-
const script = h.getProviderScript('webviewResolveAction', { action, button, buttonText: button })
|
|
1786
|
-
|| h.getProviderScript('webview_resolve_action', { action, button, buttonText: button });
|
|
1787
|
-
if (script) {
|
|
1788
|
-
const cdp = h.getCdp();
|
|
1789
|
-
if (cdp?.isConnected) {
|
|
1790
|
-
try {
|
|
1791
|
-
const matchText = provider?.webviewMatchText;
|
|
1792
|
-
const matchFn = matchText ? (body: string) => body.includes(matchText) : undefined;
|
|
1793
|
-
const raw = await cdp.evaluateInWebviewFrame?.(script, matchFn);
|
|
1794
|
-
let result: any = raw;
|
|
1795
|
-
if (typeof raw === 'string') { try { result = JSON.parse(raw); } catch { } }
|
|
1796
|
-
LOG.info('Command', `[resolveAction] webview script result: ${JSON.stringify(result)}`);
|
|
1797
|
-
|
|
1798
|
-
if (result?.resolved) return { success: true, clicked: result.clicked };
|
|
1799
|
-
if (result?.found && result.x != null && result.y != null) {
|
|
1800
|
-
LOG.info('Command', `[resolveAction] Webview coordinate click not fully supported via CDP. Click directly in script.`);
|
|
1801
|
-
}
|
|
1802
|
-
if (result?.found || result?.resolved) return { success: true };
|
|
1803
|
-
} catch (e: any) {
|
|
1804
|
-
return { success: false, error: `webviewResolveAction failed: ${e.message}` };
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
// 3. Provider script (Main DOM) → returns coords → CDP mouse click
|
|
1811
|
-
if (provider?.scripts?.resolveAction) {
|
|
1812
|
-
const script = provider.scripts.resolveAction({ action, button, buttonText: button });
|
|
1813
|
-
if (script) {
|
|
1814
|
-
const cdp = h.getCdp();
|
|
1815
|
-
if (!cdp?.isConnected) return { success: false, error: 'CDP not connected' };
|
|
1816
|
-
try {
|
|
1817
|
-
const raw = await cdp.evaluate(script, 30000);
|
|
1818
|
-
let result: any = raw;
|
|
1819
|
-
if (typeof raw === 'string') { try { result = JSON.parse(raw); } catch {} }
|
|
1820
|
-
LOG.info('Command', `[resolveAction] script result: ${JSON.stringify(result)}`);
|
|
1821
|
-
|
|
1822
|
-
if (result?.resolved) {
|
|
1823
|
-
LOG.info('Command', `[resolveAction] script-click resolved — "${result.clicked}"`);
|
|
1824
|
-
return { success: true, clicked: result.clicked };
|
|
1825
|
-
}
|
|
1826
|
-
if (result?.found && result.x != null && result.y != null) {
|
|
1827
|
-
const x = result.x;
|
|
1828
|
-
const y = result.y;
|
|
1829
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1830
|
-
type: 'mousePressed', x, y, button: 'left', clickCount: 1
|
|
1831
|
-
});
|
|
1832
|
-
await cdp.send('Input.dispatchMouseEvent', {
|
|
1833
|
-
type: 'mouseReleased', x, y, button: 'left', clickCount: 1
|
|
1834
|
-
});
|
|
1835
|
-
LOG.info('Command', `[resolveAction] CDP click at (${x}, ${y}) — "${result.text}"`);
|
|
1836
|
-
return { success: true, clicked: result.text };
|
|
1837
|
-
}
|
|
1838
|
-
return { success: false, error: result?.found === false ? `Button not found: ${button}` : 'No coordinates' };
|
|
1839
|
-
} catch (e: any) {
|
|
1840
|
-
return { success: false, error: `resolveAction failed: ${e.message}` };
|
|
1841
|
-
}
|
|
1842
|
-
}
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
return { success: false, error: 'resolveAction script not available for this provider' };
|
|
1846
|
-
}
|
|
11
|
+
export { READ_CHAT_PROVIDER_EVAL_TIMEOUT_MS, buildSendInputSignature } from './chat-commands-shared.js';
|
|
12
|
+
export { evaluateReadChatNodeWorkspaceScope } from './chat-commands-scope.js';
|
|
13
|
+
export { sanitizeDebugBundleValue, handleGetChatDebugBundle } from './chat-commands-debug-bundle.js';
|
|
14
|
+
export { handleChatHistory, handleReadChat, __resetProviderSessionPinsForTest, __getProviderSessionPinForTest } from './chat-commands-read.js';
|
|
15
|
+
export {
|
|
16
|
+
handleSendChat,
|
|
17
|
+
handleListChats,
|
|
18
|
+
handleNewChat,
|
|
19
|
+
handleSwitchChat,
|
|
20
|
+
handleSetMode,
|
|
21
|
+
handleChangeModel,
|
|
22
|
+
handleSetThoughtLevel,
|
|
23
|
+
handleResolveAction,
|
|
24
|
+
} from './chat-commands-write.js';
|