@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.10
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/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +3 -0
- package/dist/agent/agent-definition-registry.js +3 -1
- package/dist/agent/agent-definition.d.ts +15 -0
- package/dist/agent/agent-definition.js +27 -3
- package/dist/arena/context/context-tools.js +47 -4
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -84
- package/dist/automation/cron-expr.d.ts +10 -0
- package/dist/automation/cron-expr.js +23 -5
- package/dist/automation/runner.d.ts +21 -4
- package/dist/automation/runner.js +48 -12
- package/dist/automation/scheduler.d.ts +38 -0
- package/dist/automation/scheduler.js +89 -12
- package/dist/automation/store.js +5 -2
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/overlay.d.ts +15 -0
- package/dist/capability-control/overlay.js +27 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +15 -1
- package/dist/cli/agent-server-stdio.js +115 -17
- package/dist/cli/agent-server-tcp.js +40 -26
- package/dist/context/compaction.d.ts +86 -0
- package/dist/context/compaction.js +279 -0
- package/dist/context/manager.d.ts +18 -0
- package/dist/context/manager.js +181 -48
- package/dist/context/token-counter.js +13 -0
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +280 -141
- package/dist/engine/engine.js +1193 -245
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +145 -0
- package/dist/engine/goal.js +144 -0
- package/dist/engine/image-policy.d.ts +13 -0
- package/dist/engine/image-policy.js +24 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +67 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +15 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/query.js +2 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +27 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-usage.d.ts +31 -0
- package/dist/engine/session-usage.js +81 -0
- package/dist/engine/steer-queue.d.ts +33 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -4
- package/dist/engine/turn-loop.js +432 -54
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/utils.d.ts +12 -0
- package/dist/git/utils.js +38 -6
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +86 -10
- package/dist/hooks/goal-stop-hook.d.ts +28 -0
- package/dist/hooks/goal-stop-hook.js +186 -9
- package/dist/hooks/registry.d.ts +8 -0
- package/dist/hooks/registry.js +19 -0
- package/dist/hooks/shell-runner.js +10 -21
- package/dist/index.d.ts +51 -7
- package/dist/index.js +62 -5
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/rules.js +5 -1
- package/dist/llm/capabilities/types.d.ts +10 -0
- package/dist/llm/client-base.d.ts +28 -1
- package/dist/llm/client-base.js +119 -13
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +21 -0
- package/dist/llm/model-pool.js +21 -1
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +4 -0
- package/dist/llm/providers/anthropic.js +64 -14
- package/dist/llm/providers/openai.d.ts +36 -0
- package/dist/llm/providers/openai.js +257 -51
- package/dist/llm/reasoning-setting.d.ts +3 -3
- package/dist/llm/reasoning-setting.js +9 -1
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +4 -0
- package/dist/logging/sanitize-messages.js +22 -0
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +40 -10
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +412 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +61 -326
- package/dist/plugins/gitOps.d.ts +19 -0
- package/dist/plugins/gitOps.js +73 -4
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
- package/dist/plugins/installer/codex/convertMcp.js +45 -5
- package/dist/plugins/installer/install.js +25 -1
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.js +8 -2
- package/dist/plugins/installer/loadPluginAgents.js +6 -2
- package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
- package/dist/plugins/installer/loadPluginMcp.js +35 -2
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +5 -2
- package/dist/plugins/installer/types.js +1 -0
- package/dist/plugins/installer/uninstall.js +4 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +14 -0
- package/dist/plugins/installer/update.js +53 -21
- package/dist/plugins/loadPluginHooks.d.ts +47 -1
- package/dist/plugins/loadPluginHooks.js +73 -1
- package/dist/plugins/marketplaceManager.d.ts +7 -0
- package/dist/plugins/marketplaceManager.js +20 -0
- package/dist/plugins/pluginCommandHook.js +4 -18
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +13 -0
- package/dist/plugins/pluginInstaller.js +42 -8
- package/dist/plugins/schemas.js +1 -0
- package/dist/plugins/types.d.ts +6 -0
- package/dist/preset/index.d.ts +11 -1
- package/dist/preset/index.js +115 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +32 -0
- package/dist/prompt/composer.js +75 -21
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +2 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -2
- package/dist/protocol/chat-session-manager.js +38 -0
- package/dist/protocol/chat-session.d.ts +69 -1
- package/dist/protocol/chat-session.js +102 -4
- package/dist/protocol/client.d.ts +31 -0
- package/dist/protocol/client.js +44 -0
- package/dist/protocol/server.d.ts +120 -7
- package/dist/protocol/server.js +753 -51
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +60 -0
- package/dist/protocol/types.js +16 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/FileRunStore.js +8 -3
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +35 -0
- package/dist/run/factory.d.ts +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +509 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +98 -39
- package/dist/runtime/spawn-common.d.ts +159 -0
- package/dist/runtime/spawn-common.js +404 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -50
- package/dist/services/session-memory.js +24 -12
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +222 -6
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +40 -0
- package/dist/session/session-manager.js +119 -3
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +29 -1
- package/dist/session/transcript.js +56 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +9 -2
- package/dist/settings/disk-defaults.js +11 -2
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +68 -1
- package/dist/settings/manager.js +266 -29
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1186 -771
- package/dist/settings/schema.js +268 -97
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
- package/dist/tool-system/builtin/agent-registry.js +8 -0
- package/dist/tool-system/builtin/agent.d.ts +21 -1
- package/dist/tool-system/builtin/agent.js +489 -42
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +0 -15
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +101 -64
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +64 -4
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
- package/dist/tool-system/builtin/edit.js +20 -16
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +35 -0
- package/dist/tool-system/builtin/generate-image.js +278 -50
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +364 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.d.ts +9 -0
- package/dist/tool-system/builtin/grep.js +106 -11
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +10 -2
- package/dist/tool-system/builtin/index.js +251 -27
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.js +0 -7
- package/dist/tool-system/builtin/powershell.js +8 -2
- package/dist/tool-system/builtin/read.js +10 -10
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +8 -2
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.js +21 -7
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.js +0 -6
- package/dist/tool-system/context.d.ts +164 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +197 -90
- package/dist/tool-system/investigation-guard.js +1 -1
- package/dist/tool-system/mcp-manager.d.ts +42 -4
- package/dist/tool-system/mcp-manager.js +203 -20
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/path-policy.d.ts +5 -0
- package/dist/tool-system/path-policy.js +307 -8
- package/dist/tool-system/permission.d.ts +39 -2
- package/dist/tool-system/permission.js +283 -70
- package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
- package/dist/tool-system/plan-mode-allowlist.js +24 -2
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +16 -2
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/sandbox/off.js +7 -1
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +252 -7
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +48 -0
- package/dist/utils/exec.js +154 -0
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +13 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Speech-to-text transcription service (voice input / dictation).
|
|
3
|
+
*
|
|
4
|
+
* A NON-tool, pure "creds + audio bytes → text" service: the desktop composer
|
|
5
|
+
* records the user's voice and calls this to fill the input box (Codex-style
|
|
6
|
+
* dictation). It is deliberately NOT an agent tool — the user is speaking their
|
|
7
|
+
* own prompt, not asking the model to transcribe a file.
|
|
8
|
+
*
|
|
9
|
+
* Targets the OpenAI-compatible `/audio/transcriptions` endpoint, which a single
|
|
10
|
+
* adapter covers across OpenAI (whisper-1 / gpt-4o-transcribe), Groq
|
|
11
|
+
* (whisper-large-v3-turbo), and self-hosted whisper.cpp servers — they share
|
|
12
|
+
* the same multipart wire shape; only baseUrl + model differ.
|
|
13
|
+
*
|
|
14
|
+
* `fetch` is injected so this is unit-testable without network (mirrors
|
|
15
|
+
* image-providers.ts). Production callers pass the global `fetch`.
|
|
16
|
+
*/
|
|
17
|
+
export interface TranscribeCreds {
|
|
18
|
+
baseUrl: string;
|
|
19
|
+
apiKey: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TranscribeRequest {
|
|
22
|
+
/** Raw audio bytes (NOT base64) — e.g. a webm/opus recording. */
|
|
23
|
+
audio: Uint8Array;
|
|
24
|
+
/** MIME type of the audio, e.g. "audio/webm". */
|
|
25
|
+
mimeType: string;
|
|
26
|
+
/** Multipart part filename hint (extension matters to some servers). */
|
|
27
|
+
filename: string;
|
|
28
|
+
/** Model id, e.g. "gpt-4o-transcribe" | "whisper-1". */
|
|
29
|
+
model: string;
|
|
30
|
+
creds: TranscribeCreds;
|
|
31
|
+
/** Optional ISO-639-1 language hint (improves accuracy / latency). */
|
|
32
|
+
language?: string;
|
|
33
|
+
/** Optional cancellation signal forwarded to fetch. */
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
/** Injected for tests; defaults to global fetch. */
|
|
36
|
+
fetchImpl?: typeof fetch;
|
|
37
|
+
}
|
|
38
|
+
export type TranscribeResult = {
|
|
39
|
+
ok: true;
|
|
40
|
+
text: string;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
error: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Transcribe audio via an OpenAI-compatible `/audio/transcriptions` endpoint.
|
|
47
|
+
* Posts multipart/form-data (file + model + response_format[+ language]) with a
|
|
48
|
+
* Bearer key; reads `{ text }` from the JSON response. Do NOT set Content-Type
|
|
49
|
+
* manually — fetch derives the multipart boundary from the FormData body.
|
|
50
|
+
*/
|
|
51
|
+
export declare function transcribe(req: TranscribeRequest): Promise<TranscribeResult>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Speech-to-text transcription service (voice input / dictation).
|
|
3
|
+
*
|
|
4
|
+
* A NON-tool, pure "creds + audio bytes → text" service: the desktop composer
|
|
5
|
+
* records the user's voice and calls this to fill the input box (Codex-style
|
|
6
|
+
* dictation). It is deliberately NOT an agent tool — the user is speaking their
|
|
7
|
+
* own prompt, not asking the model to transcribe a file.
|
|
8
|
+
*
|
|
9
|
+
* Targets the OpenAI-compatible `/audio/transcriptions` endpoint, which a single
|
|
10
|
+
* adapter covers across OpenAI (whisper-1 / gpt-4o-transcribe), Groq
|
|
11
|
+
* (whisper-large-v3-turbo), and self-hosted whisper.cpp servers — they share
|
|
12
|
+
* the same multipart wire shape; only baseUrl + model differ.
|
|
13
|
+
*
|
|
14
|
+
* `fetch` is injected so this is unit-testable without network (mirrors
|
|
15
|
+
* image-providers.ts). Production callers pass the global `fetch`.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Transcribe audio via an OpenAI-compatible `/audio/transcriptions` endpoint.
|
|
19
|
+
* Posts multipart/form-data (file + model + response_format[+ language]) with a
|
|
20
|
+
* Bearer key; reads `{ text }` from the JSON response. Do NOT set Content-Type
|
|
21
|
+
* manually — fetch derives the multipart boundary from the FormData body.
|
|
22
|
+
*/
|
|
23
|
+
export async function transcribe(req) {
|
|
24
|
+
const fetchImpl = req.fetchImpl ?? fetch;
|
|
25
|
+
// Trim trailing slashes so `${baseUrl}/audio/...` doesn't double up.
|
|
26
|
+
const baseUrl = req.creds.baseUrl.replace(/\/+$/, "");
|
|
27
|
+
const form = new FormData();
|
|
28
|
+
// Copy into a fresh Uint8Array so the Blob gets a clean ArrayBuffer (avoids
|
|
29
|
+
// SharedArrayBuffer / byteOffset typing issues across runtimes).
|
|
30
|
+
const buf = Uint8Array.from(req.audio);
|
|
31
|
+
form.append("file", new Blob([buf], { type: req.mimeType }), req.filename);
|
|
32
|
+
form.append("model", req.model);
|
|
33
|
+
form.append("response_format", "json");
|
|
34
|
+
if (req.language)
|
|
35
|
+
form.append("language", req.language);
|
|
36
|
+
let resp;
|
|
37
|
+
try {
|
|
38
|
+
resp = await fetchImpl(`${baseUrl}/audio/transcriptions`, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: { Authorization: `Bearer ${req.creds.apiKey}` },
|
|
41
|
+
body: form,
|
|
42
|
+
signal: req.signal,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
return { ok: false, error: `request failed: ${err.message}` };
|
|
47
|
+
}
|
|
48
|
+
if (!resp.ok) {
|
|
49
|
+
const body = (await resp.text().catch(() => "")).slice(0, 500);
|
|
50
|
+
return { ok: false, error: `transcription API returned ${resp.status}: ${body}` };
|
|
51
|
+
}
|
|
52
|
+
let json;
|
|
53
|
+
try {
|
|
54
|
+
json = await resp.json();
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return { ok: false, error: `could not parse transcription response: ${err.message}` };
|
|
58
|
+
}
|
|
59
|
+
const text = json?.text;
|
|
60
|
+
if (typeof text !== "string") {
|
|
61
|
+
const preview = JSON.stringify(json).slice(0, 500);
|
|
62
|
+
return { ok: false, error: `no text in transcription response: ${preview}` };
|
|
63
|
+
}
|
|
64
|
+
return { ok: true, text };
|
|
65
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrowserBridge — driver-agnostic contract between the browser tools (core)
|
|
3
|
+
* and whatever drives the actual browser (renderer's webview via CDP).
|
|
4
|
+
*
|
|
5
|
+
* Spec: docs/superpowers/specs/2026-06-16-browser-automation-mvp.md
|
|
6
|
+
*
|
|
7
|
+
* The core tools only know this interface. The renderer implements it on top
|
|
8
|
+
* of the webview's `webContents.debugger` (CDP): observe via
|
|
9
|
+
* Accessibility.getFullAXTree, act via DOM.getBoxModel → Input.dispatchMouseEvent.
|
|
10
|
+
* Keeping it driver-agnostic means a future implementation (a hidden
|
|
11
|
+
* BrowserWindow for unattended runs, or an external engine) can swap in without
|
|
12
|
+
* touching the tools. Undefined on ToolContext → headless / no panel → tools
|
|
13
|
+
* degrade with a clear error.
|
|
14
|
+
*/
|
|
15
|
+
/** One interactive element from the page's accessibility tree. */
|
|
16
|
+
export interface BrowserElement {
|
|
17
|
+
/** Per-snapshot reference id (e1, e2, …); the renderer maps it back to a
|
|
18
|
+
* backendDOMNodeId for the action tools. Reassigned each snapshot. */
|
|
19
|
+
ref: string;
|
|
20
|
+
/** ARIA role (button, link, textbox, checkbox, combobox, …). */
|
|
21
|
+
role: string;
|
|
22
|
+
/** Accessible name (visible text / label). May be empty. */
|
|
23
|
+
name: string;
|
|
24
|
+
/** True for password/sensitive inputs — value is never captured (security). */
|
|
25
|
+
sensitive?: boolean;
|
|
26
|
+
/** Optional value for non-sensitive inputs (e.g. current textbox text). */
|
|
27
|
+
value?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface BrowserSnapshot {
|
|
30
|
+
url: string;
|
|
31
|
+
title?: string;
|
|
32
|
+
elements: BrowserElement[];
|
|
33
|
+
/** Set when the page needs the user to act (login wall / 2FA) — agent should
|
|
34
|
+
* hand control back to the user rather than retry. */
|
|
35
|
+
needsHuman?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface BrowserResult {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
/** Human-readable detail (error reason, or short success note). */
|
|
40
|
+
detail?: string;
|
|
41
|
+
/** True when a ref no longer resolves (DOM changed) → agent should re-snapshot. */
|
|
42
|
+
staleRef?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Result of reading the page's main textual content (扒内容). */
|
|
45
|
+
export interface BrowserContent {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
url: string;
|
|
48
|
+
title?: string;
|
|
49
|
+
/** Extracted readable text (main content, scripts/styles/nav stripped). */
|
|
50
|
+
text: string;
|
|
51
|
+
/** True if the text was truncated to a cap. */
|
|
52
|
+
truncated?: boolean;
|
|
53
|
+
detail?: string;
|
|
54
|
+
}
|
|
55
|
+
/** One hyperlink extracted from the page (a[href]). */
|
|
56
|
+
export interface BrowserLink {
|
|
57
|
+
/** Visible link text (trimmed, may be empty for icon-only links). */
|
|
58
|
+
text: string;
|
|
59
|
+
/** Absolute URL (href resolved against the page). */
|
|
60
|
+
url: string;
|
|
61
|
+
}
|
|
62
|
+
/** One image extracted from the page (img[src]). */
|
|
63
|
+
export interface BrowserImage {
|
|
64
|
+
/** Absolute image URL (src resolved against the page). */
|
|
65
|
+
url: string;
|
|
66
|
+
/** alt text if present (helps the agent know what the image is). */
|
|
67
|
+
alt?: string;
|
|
68
|
+
/** Per-extract ref (imgN) — pass to browser_observe(image) to see its pixels. */
|
|
69
|
+
ref?: string;
|
|
70
|
+
}
|
|
71
|
+
/** One video extracted from the page (<video src> / <source src>). */
|
|
72
|
+
export interface BrowserVideo {
|
|
73
|
+
/** Absolute video URL (src resolved against the page). */
|
|
74
|
+
url: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Result of extracting the page's link/image URLs (扒链接/图片地址).
|
|
78
|
+
* The a11y snapshot deliberately omits href/src (token economy); this is the
|
|
79
|
+
* explicit opt-in for "I need the actual URLs" — e.g. collect article links,
|
|
80
|
+
* find a video/image source to hand to yt-dlp/curl.
|
|
81
|
+
*/
|
|
82
|
+
export interface BrowserExtract {
|
|
83
|
+
ok: boolean;
|
|
84
|
+
url: string;
|
|
85
|
+
title?: string;
|
|
86
|
+
links: BrowserLink[];
|
|
87
|
+
images: BrowserImage[];
|
|
88
|
+
/** Video/source URLs found on the page (a downloader like yt-dlp can fetch). */
|
|
89
|
+
videos: BrowserVideo[];
|
|
90
|
+
/** True if any list was capped (page had more) — narrow the page first. */
|
|
91
|
+
truncated?: boolean;
|
|
92
|
+
detail?: string;
|
|
93
|
+
}
|
|
94
|
+
/** A captured image's bytes, ready to become a vision ContentBlock. */
|
|
95
|
+
export interface BrowserImageData {
|
|
96
|
+
ok: boolean;
|
|
97
|
+
/** base64 (no data: prefix). */
|
|
98
|
+
base64?: string;
|
|
99
|
+
/** "image/jpeg" | "image/png" | … */
|
|
100
|
+
mediaType?: string;
|
|
101
|
+
/** ref the image came from (so the tool can label which image is which). */
|
|
102
|
+
ref?: string;
|
|
103
|
+
detail?: string;
|
|
104
|
+
}
|
|
105
|
+
export interface BrowserBridge {
|
|
106
|
+
snapshot(): Promise<BrowserSnapshot>;
|
|
107
|
+
click(ref: string): Promise<BrowserResult>;
|
|
108
|
+
type(ref: string, text: string): Promise<BrowserResult>;
|
|
109
|
+
navigate(url: string): Promise<BrowserResult>;
|
|
110
|
+
scroll(dir: "up" | "down", amount?: number): Promise<BrowserResult>;
|
|
111
|
+
/** Read the page's main readable text content (for summarizing / extraction). */
|
|
112
|
+
readContent(): Promise<BrowserContent>;
|
|
113
|
+
/** Extract the page's hyperlink + image URLs (href/src the a11y tree omits). */
|
|
114
|
+
extractLinks(): Promise<BrowserExtract>;
|
|
115
|
+
/** Wait until the page finishes loading (or a timeout). */
|
|
116
|
+
waitForLoad(timeoutMs?: number): Promise<BrowserResult>;
|
|
117
|
+
/** Hover over an element by ref (reveal hover-dependent menus/tooltips). */
|
|
118
|
+
hover(ref: string): Promise<BrowserResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Select an option in a native <select> by ref. `value` matches an option's
|
|
121
|
+
* value or visible text. Custom <div> dropdowns are NOT this — click to expand
|
|
122
|
+
* them and click the option from the next snapshot.
|
|
123
|
+
*/
|
|
124
|
+
selectOption(ref: string, value: string): Promise<BrowserResult>;
|
|
125
|
+
/**
|
|
126
|
+
* Press a key (or combination) on the focused element / a given ref. `key` is
|
|
127
|
+
* a key name ("Enter", "Tab", "Escape", "ArrowDown") or a combination
|
|
128
|
+
* ("Control+a"). Focuses `ref` first if given (so the key lands there).
|
|
129
|
+
*/
|
|
130
|
+
pressKey(key: string, ref?: string): Promise<BrowserResult>;
|
|
131
|
+
/**
|
|
132
|
+
* Fetch the real pixels of page images by ref (e.g. 小红书 笔记配图) so a
|
|
133
|
+
* vision model can SEE their content. Fetched in-page (carries the page's
|
|
134
|
+
* cookies → beats hotlink protection). One result per requested ref.
|
|
135
|
+
*/
|
|
136
|
+
fetchImages(refs: string[]): Promise<BrowserImageData[]>;
|
|
137
|
+
/**
|
|
138
|
+
* Screenshot the viewport, or a single element's box if `ref` given (a
|
|
139
|
+
* <video> ref → its current frame). For "see the rendered layout / canvas /
|
|
140
|
+
* chart" — when the a11y tree isn't enough.
|
|
141
|
+
*/
|
|
142
|
+
screenshot(ref?: string): Promise<BrowserImageData>;
|
|
143
|
+
/** List the open browser tabs (the agent picks one to drive via switchTab). */
|
|
144
|
+
listTabs(): Promise<BrowserTab[]>;
|
|
145
|
+
/** Make a tab the active automation target (subsequent actions drive it). */
|
|
146
|
+
switchTab(tabId: string): Promise<BrowserResult>;
|
|
147
|
+
}
|
|
148
|
+
/** One open browser tab, as the agent sees it. */
|
|
149
|
+
export interface BrowserTab {
|
|
150
|
+
/** Stable id (the desktop webContents id, as a string). */
|
|
151
|
+
tabId: string;
|
|
152
|
+
url: string;
|
|
153
|
+
title: string;
|
|
154
|
+
/** True for the tab automation currently drives. */
|
|
155
|
+
active: boolean;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Minimal shape of a CDP Accessibility.getFullAXTree node we depend on.
|
|
159
|
+
* (The real payload has more fields; we read only these.)
|
|
160
|
+
*/
|
|
161
|
+
export interface AXNode {
|
|
162
|
+
nodeId: string;
|
|
163
|
+
ignored?: boolean;
|
|
164
|
+
role?: {
|
|
165
|
+
value?: string;
|
|
166
|
+
};
|
|
167
|
+
name?: {
|
|
168
|
+
value?: string;
|
|
169
|
+
};
|
|
170
|
+
value?: {
|
|
171
|
+
value?: string;
|
|
172
|
+
};
|
|
173
|
+
backendDOMNodeId?: number;
|
|
174
|
+
properties?: Array<{
|
|
175
|
+
name: string;
|
|
176
|
+
value?: {
|
|
177
|
+
value?: unknown;
|
|
178
|
+
};
|
|
179
|
+
}>;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Pure: flatten a CDP accessibility tree into the compact, ref-tagged element
|
|
183
|
+
* list the LLM sees. Token-economical (role + name + key props only, no DOM
|
|
184
|
+
* markup). Returns elements in tree order with refs e1, e2, …, plus a
|
|
185
|
+
* `refToBackendId` map the caller (renderer) keeps so action tools can resolve
|
|
186
|
+
* a ref back to a backendDOMNodeId.
|
|
187
|
+
*
|
|
188
|
+
* Filtering: drop ignored nodes; keep nodes whose role is interactive (or that
|
|
189
|
+
* are explicitly focusable), and that have a name OR are a value input. Password
|
|
190
|
+
* / sensitive inputs are marked `sensitive` and their value is NEVER included.
|
|
191
|
+
*/
|
|
192
|
+
export declare function flattenAxTree(nodes: AXNode[]): {
|
|
193
|
+
elements: BrowserElement[];
|
|
194
|
+
refToBackendId: Record<string, number>;
|
|
195
|
+
};
|
|
196
|
+
/** Default cap for extracted page text (chars) — keeps content within a sane
|
|
197
|
+
* token budget; the agent can scroll + re-read for more. */
|
|
198
|
+
export declare const CONTENT_CHAR_CAP = 12000;
|
|
199
|
+
/** Cap for extracted links/images per call — keeps the result token-bounded;
|
|
200
|
+
* a busy page can have thousands of <a>/<img>. The agent narrows the page
|
|
201
|
+
* (navigate/scroll) and re-extracts for more. */
|
|
202
|
+
export declare const EXTRACT_LINK_CAP = 200;
|
|
203
|
+
/**
|
|
204
|
+
* In-page JS (string) that collects deduped, absolute link + image URLs and
|
|
205
|
+
* returns them as a JSON-serializable object. Run via CDP Runtime.evaluate with
|
|
206
|
+
* returnByValue. Kept here (not in the renderer) so the extraction contract is
|
|
207
|
+
* defined alongside its types and is unit-testable. `cap` bounds each list;
|
|
208
|
+
* `truncated` is set when either hit the cap.
|
|
209
|
+
*
|
|
210
|
+
* - Links: <a href> with a real navigable href (skips javascript:/empty/#-only).
|
|
211
|
+
* - Images: <img src> (skips empty/data: noise beyond a length sanity bound is
|
|
212
|
+
* left to the page; data URLs are dropped — they're inline, not fetchable URLs).
|
|
213
|
+
* - URLs are absolute (the DOM's .href/.src already resolve against the base).
|
|
214
|
+
*/
|
|
215
|
+
export declare function buildExtractLinksScript(cap?: number): string;
|
|
216
|
+
/**
|
|
217
|
+
* Pure: normalize raw extracted page text — collapse runs of whitespace/blank
|
|
218
|
+
* lines, trim, and cap to `cap` chars (marking truncation). The renderer pulls
|
|
219
|
+
* raw innerText via CDP; this keeps the cleanup logic testable and consistent.
|
|
220
|
+
*/
|
|
221
|
+
export declare function cleanPageText(raw: string, cap?: number): {
|
|
222
|
+
text: string;
|
|
223
|
+
truncated: boolean;
|
|
224
|
+
};
|
|
225
|
+
/** Render a snapshot's element list as the compact text block shown to the LLM. */
|
|
226
|
+
export declare function renderElementList(elements: BrowserElement[]): string;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrowserBridge — driver-agnostic contract between the browser tools (core)
|
|
3
|
+
* and whatever drives the actual browser (renderer's webview via CDP).
|
|
4
|
+
*
|
|
5
|
+
* Spec: docs/superpowers/specs/2026-06-16-browser-automation-mvp.md
|
|
6
|
+
*
|
|
7
|
+
* The core tools only know this interface. The renderer implements it on top
|
|
8
|
+
* of the webview's `webContents.debugger` (CDP): observe via
|
|
9
|
+
* Accessibility.getFullAXTree, act via DOM.getBoxModel → Input.dispatchMouseEvent.
|
|
10
|
+
* Keeping it driver-agnostic means a future implementation (a hidden
|
|
11
|
+
* BrowserWindow for unattended runs, or an external engine) can swap in without
|
|
12
|
+
* touching the tools. Undefined on ToolContext → headless / no panel → tools
|
|
13
|
+
* degrade with a clear error.
|
|
14
|
+
*/
|
|
15
|
+
/** Roles we surface as actionable/meaningful to the agent. */
|
|
16
|
+
const INTERACTIVE_ROLES = new Set([
|
|
17
|
+
"button",
|
|
18
|
+
"link",
|
|
19
|
+
"textbox",
|
|
20
|
+
"searchbox",
|
|
21
|
+
"checkbox",
|
|
22
|
+
"radio",
|
|
23
|
+
"combobox",
|
|
24
|
+
"listbox",
|
|
25
|
+
"menuitem",
|
|
26
|
+
"menuitemcheckbox",
|
|
27
|
+
"menuitemradio",
|
|
28
|
+
"tab",
|
|
29
|
+
"switch",
|
|
30
|
+
"slider",
|
|
31
|
+
"option",
|
|
32
|
+
]);
|
|
33
|
+
/** Input-ish roles whose value we may show (unless sensitive). */
|
|
34
|
+
const VALUE_ROLES = new Set(["textbox", "searchbox", "combobox", "slider"]);
|
|
35
|
+
function propValue(node, name) {
|
|
36
|
+
return node.properties?.find((p) => p.name === name)?.value?.value;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Pure: flatten a CDP accessibility tree into the compact, ref-tagged element
|
|
40
|
+
* list the LLM sees. Token-economical (role + name + key props only, no DOM
|
|
41
|
+
* markup). Returns elements in tree order with refs e1, e2, …, plus a
|
|
42
|
+
* `refToBackendId` map the caller (renderer) keeps so action tools can resolve
|
|
43
|
+
* a ref back to a backendDOMNodeId.
|
|
44
|
+
*
|
|
45
|
+
* Filtering: drop ignored nodes; keep nodes whose role is interactive (or that
|
|
46
|
+
* are explicitly focusable), and that have a name OR are a value input. Password
|
|
47
|
+
* / sensitive inputs are marked `sensitive` and their value is NEVER included.
|
|
48
|
+
*/
|
|
49
|
+
export function flattenAxTree(nodes) {
|
|
50
|
+
const elements = [];
|
|
51
|
+
const refToBackendId = {};
|
|
52
|
+
let counter = 0;
|
|
53
|
+
for (const node of nodes) {
|
|
54
|
+
if (node.ignored)
|
|
55
|
+
continue;
|
|
56
|
+
const role = node.role?.value;
|
|
57
|
+
if (!role)
|
|
58
|
+
continue;
|
|
59
|
+
const focusable = propValue(node, "focusable") === true;
|
|
60
|
+
if (!INTERACTIVE_ROLES.has(role) && !focusable)
|
|
61
|
+
continue;
|
|
62
|
+
const name = (node.name?.value ?? "").trim();
|
|
63
|
+
const isValueRole = VALUE_ROLES.has(role);
|
|
64
|
+
// Skip nameless non-input noise (e.g. a focusable wrapper with no label).
|
|
65
|
+
if (!name && !isValueRole)
|
|
66
|
+
continue;
|
|
67
|
+
// backendDOMNodeId is required to act on the element later; without it the
|
|
68
|
+
// element isn't actionable, so don't surface it.
|
|
69
|
+
if (node.backendDOMNodeId === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
const sensitive = propValue(node, "protected") === true || // ARIA "protected" → password-like
|
|
72
|
+
role === "textbox" && /password|密码/i.test(name);
|
|
73
|
+
counter += 1;
|
|
74
|
+
const ref = `e${counter}`;
|
|
75
|
+
refToBackendId[ref] = node.backendDOMNodeId;
|
|
76
|
+
const el = { ref, role, name };
|
|
77
|
+
if (sensitive) {
|
|
78
|
+
el.sensitive = true; // value intentionally omitted
|
|
79
|
+
}
|
|
80
|
+
else if (isValueRole) {
|
|
81
|
+
const v = node.value?.value?.toString();
|
|
82
|
+
if (v)
|
|
83
|
+
el.value = v;
|
|
84
|
+
}
|
|
85
|
+
elements.push(el);
|
|
86
|
+
}
|
|
87
|
+
return { elements, refToBackendId };
|
|
88
|
+
}
|
|
89
|
+
/** Default cap for extracted page text (chars) — keeps content within a sane
|
|
90
|
+
* token budget; the agent can scroll + re-read for more. */
|
|
91
|
+
export const CONTENT_CHAR_CAP = 12_000;
|
|
92
|
+
/** Cap for extracted links/images per call — keeps the result token-bounded;
|
|
93
|
+
* a busy page can have thousands of <a>/<img>. The agent narrows the page
|
|
94
|
+
* (navigate/scroll) and re-extracts for more. */
|
|
95
|
+
export const EXTRACT_LINK_CAP = 200;
|
|
96
|
+
/**
|
|
97
|
+
* In-page JS (string) that collects deduped, absolute link + image URLs and
|
|
98
|
+
* returns them as a JSON-serializable object. Run via CDP Runtime.evaluate with
|
|
99
|
+
* returnByValue. Kept here (not in the renderer) so the extraction contract is
|
|
100
|
+
* defined alongside its types and is unit-testable. `cap` bounds each list;
|
|
101
|
+
* `truncated` is set when either hit the cap.
|
|
102
|
+
*
|
|
103
|
+
* - Links: <a href> with a real navigable href (skips javascript:/empty/#-only).
|
|
104
|
+
* - Images: <img src> (skips empty/data: noise beyond a length sanity bound is
|
|
105
|
+
* left to the page; data URLs are dropped — they're inline, not fetchable URLs).
|
|
106
|
+
* - URLs are absolute (the DOM's .href/.src already resolve against the base).
|
|
107
|
+
*/
|
|
108
|
+
export function buildExtractLinksScript(cap = EXTRACT_LINK_CAP) {
|
|
109
|
+
return `(function(){
|
|
110
|
+
var cap=${cap};
|
|
111
|
+
var links=[],images=[],lt=false,it=false,seenL={},seenI={};
|
|
112
|
+
var as=document.querySelectorAll('a[href]');
|
|
113
|
+
for(var i=0;i<as.length;i++){
|
|
114
|
+
var a=as[i],u=a.href;
|
|
115
|
+
if(!u||u.indexOf('javascript:')===0||u==='#'||u.charAt(u.length-1)==='#'&&u.indexOf('#')===u.length-1)continue;
|
|
116
|
+
if(seenL[u])continue;seenL[u]=1;
|
|
117
|
+
if(links.length>=cap){lt=true;break;}
|
|
118
|
+
links.push({text:(a.textContent||'').trim().slice(0,200),url:u});
|
|
119
|
+
}
|
|
120
|
+
var ims=document.querySelectorAll('img[src]');
|
|
121
|
+
for(var j=0;j<ims.length;j++){
|
|
122
|
+
var im=ims[j],s=im.src;
|
|
123
|
+
if(!s||s.indexOf('data:')===0)continue;
|
|
124
|
+
if(seenI[s])continue;seenI[s]=1;
|
|
125
|
+
if(images.length>=cap){it=true;break;}
|
|
126
|
+
var o={url:s};var alt=(im.getAttribute('alt')||'').trim();if(alt)o.alt=alt.slice(0,200);
|
|
127
|
+
images.push(o);
|
|
128
|
+
}
|
|
129
|
+
return {links:links,images:images,truncated:lt||it};
|
|
130
|
+
})()`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Pure: normalize raw extracted page text — collapse runs of whitespace/blank
|
|
134
|
+
* lines, trim, and cap to `cap` chars (marking truncation). The renderer pulls
|
|
135
|
+
* raw innerText via CDP; this keeps the cleanup logic testable and consistent.
|
|
136
|
+
*/
|
|
137
|
+
export function cleanPageText(raw, cap = CONTENT_CHAR_CAP) {
|
|
138
|
+
const normalized = raw
|
|
139
|
+
.replace(/\r\n?/g, "\n")
|
|
140
|
+
.replace(/[ \t\f\v]+/g, " ")
|
|
141
|
+
.replace(/ *\n */g, "\n")
|
|
142
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
143
|
+
.trim();
|
|
144
|
+
if (normalized.length <= cap)
|
|
145
|
+
return { text: normalized, truncated: false };
|
|
146
|
+
return { text: normalized.slice(0, cap) + "\n…(truncated)", truncated: true };
|
|
147
|
+
}
|
|
148
|
+
/** Render a snapshot's element list as the compact text block shown to the LLM. */
|
|
149
|
+
export function renderElementList(elements) {
|
|
150
|
+
if (elements.length === 0)
|
|
151
|
+
return "(no interactive elements found)";
|
|
152
|
+
return elements
|
|
153
|
+
.map((e) => {
|
|
154
|
+
const namePart = e.name ? ` "${e.name}"` : "";
|
|
155
|
+
const valuePart = e.sensitive
|
|
156
|
+
? " [sensitive]"
|
|
157
|
+
: e.value
|
|
158
|
+
? ` =${JSON.stringify(e.value)}`
|
|
159
|
+
: "";
|
|
160
|
+
return `[ref=${e.ref}] ${e.role}${namePart}${valuePart}`;
|
|
161
|
+
})
|
|
162
|
+
.join("\n");
|
|
163
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHeartbeatPinger (B: background-agent visibility) — while background
|
|
3
|
+
* sub-agents are running, emit a periodic `agent_heartbeat` per parent session
|
|
4
|
+
* so the UI knows they're alive even during long LLM-request silence (no
|
|
5
|
+
* tool_use / no text for minutes). Decided over mtime/event-stream liveness
|
|
6
|
+
* because the worker KNOWS an agent is mid-request and can keep reporting it,
|
|
7
|
+
* whereas a quiet event stream looks dead.
|
|
8
|
+
*
|
|
9
|
+
* Liveness model deliberately NOT a separate heartbeat-file here — the file +
|
|
10
|
+
* pid (Heartbeat.ts / isProcessAlive) is for cross-restart interrupted
|
|
11
|
+
* detection (phase C). This pinger is the live "still working" UI signal only.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle (anti-leak): single timer, stored + cleared; `unref()` so it never
|
|
14
|
+
* pins the process open; `start()` idempotent; self-stops emitting when no
|
|
15
|
+
* agent runs but keeps the (unref'd) tick cheap. Mirrors the proven
|
|
16
|
+
* Heartbeat.ts / chat-session-manager sweeper pattern.
|
|
17
|
+
*/
|
|
18
|
+
import type { StreamEvent } from "../../types.js";
|
|
19
|
+
/** Default 30s — user-chosen. Stale threshold downstream is 3× (90s). */
|
|
20
|
+
export declare const DEFAULT_HEARTBEAT_INTERVAL_MS = 30000;
|
|
21
|
+
export interface AgentHeartbeatConfig {
|
|
22
|
+
intervalMs?: number;
|
|
23
|
+
/** Sink for each heartbeat event (server forwards to the client). */
|
|
24
|
+
publish: (sessionId: string, event: StreamEvent) => void;
|
|
25
|
+
/** Injectable clock for tests. */
|
|
26
|
+
now?: () => number;
|
|
27
|
+
}
|
|
28
|
+
export declare class AgentHeartbeatPinger {
|
|
29
|
+
private readonly intervalMs;
|
|
30
|
+
private readonly publish;
|
|
31
|
+
private readonly now;
|
|
32
|
+
private timer;
|
|
33
|
+
constructor(config: AgentHeartbeatConfig);
|
|
34
|
+
/** Begin periodic heartbeats. Idempotent — a second call is a no-op. */
|
|
35
|
+
start(): void;
|
|
36
|
+
stop(): void;
|
|
37
|
+
/** One heartbeat cycle: group running agents by session and publish. */
|
|
38
|
+
private tick;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Process-wide pinger that publishes to the agentNotificationBus (the same
|
|
42
|
+
* channel background-agent completions use → the protocol server forwards each
|
|
43
|
+
* event to the client). `ensureRunning()` is called when an agent backgrounds;
|
|
44
|
+
* the pinger self-stops once no agent is running, so a fresh handoff re-arms it.
|
|
45
|
+
*/
|
|
46
|
+
export declare const agentHeartbeatPinger: AgentHeartbeatPinger;
|
|
47
|
+
/** Start the shared pinger if not already ticking (idempotent). Call when an
|
|
48
|
+
* agent enters the background. */
|
|
49
|
+
export declare function ensureAgentHeartbeat(): void;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHeartbeatPinger (B: background-agent visibility) — while background
|
|
3
|
+
* sub-agents are running, emit a periodic `agent_heartbeat` per parent session
|
|
4
|
+
* so the UI knows they're alive even during long LLM-request silence (no
|
|
5
|
+
* tool_use / no text for minutes). Decided over mtime/event-stream liveness
|
|
6
|
+
* because the worker KNOWS an agent is mid-request and can keep reporting it,
|
|
7
|
+
* whereas a quiet event stream looks dead.
|
|
8
|
+
*
|
|
9
|
+
* Liveness model deliberately NOT a separate heartbeat-file here — the file +
|
|
10
|
+
* pid (Heartbeat.ts / isProcessAlive) is for cross-restart interrupted
|
|
11
|
+
* detection (phase C). This pinger is the live "still working" UI signal only.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle (anti-leak): single timer, stored + cleared; `unref()` so it never
|
|
14
|
+
* pins the process open; `start()` idempotent; self-stops emitting when no
|
|
15
|
+
* agent runs but keeps the (unref'd) tick cheap. Mirrors the proven
|
|
16
|
+
* Heartbeat.ts / chat-session-manager sweeper pattern.
|
|
17
|
+
*/
|
|
18
|
+
import { asyncAgentRegistry } from "./agent-registry.js";
|
|
19
|
+
import { agentNotificationBus } from "./agent-notifications.js";
|
|
20
|
+
/** Default 30s — user-chosen. Stale threshold downstream is 3× (90s). */
|
|
21
|
+
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000;
|
|
22
|
+
export class AgentHeartbeatPinger {
|
|
23
|
+
intervalMs;
|
|
24
|
+
publish;
|
|
25
|
+
now;
|
|
26
|
+
timer = null;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.intervalMs = config.intervalMs ?? DEFAULT_HEARTBEAT_INTERVAL_MS;
|
|
29
|
+
this.publish = config.publish;
|
|
30
|
+
this.now = config.now ?? Date.now;
|
|
31
|
+
}
|
|
32
|
+
/** Begin periodic heartbeats. Idempotent — a second call is a no-op. */
|
|
33
|
+
start() {
|
|
34
|
+
if (this.timer)
|
|
35
|
+
return;
|
|
36
|
+
this.timer = setInterval(() => this.tick(), this.intervalMs);
|
|
37
|
+
// Never keep the process alive just for heartbeats.
|
|
38
|
+
if (typeof this.timer === "object" && "unref" in this.timer) {
|
|
39
|
+
this.timer.unref();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
stop() {
|
|
43
|
+
if (this.timer) {
|
|
44
|
+
clearInterval(this.timer);
|
|
45
|
+
this.timer = null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** One heartbeat cycle: group running agents by session and publish. */
|
|
49
|
+
tick() {
|
|
50
|
+
const running = asyncAgentRegistry.getSnapshot().filter((e) => e.status === "running");
|
|
51
|
+
if (running.length === 0) {
|
|
52
|
+
// Nothing left to ping — self-stop so the timer doesn't tick forever
|
|
53
|
+
// after the last background agent finishes. start() re-arms on the next
|
|
54
|
+
// handoff. (Idempotent stop.)
|
|
55
|
+
this.stop();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const bySession = new Map();
|
|
59
|
+
for (const e of running) {
|
|
60
|
+
// Background agents always carry their spawning sessionId; skip any
|
|
61
|
+
// legacy/ad-hoc entry without one (can't address a heartbeat to nobody).
|
|
62
|
+
if (!e.sessionId)
|
|
63
|
+
continue;
|
|
64
|
+
const list = bySession.get(e.sessionId);
|
|
65
|
+
if (list)
|
|
66
|
+
list.push(e.agentId);
|
|
67
|
+
else
|
|
68
|
+
bySession.set(e.sessionId, [e.agentId]);
|
|
69
|
+
}
|
|
70
|
+
const ts = this.now();
|
|
71
|
+
for (const [sessionId, agentIds] of bySession) {
|
|
72
|
+
this.publish(sessionId, { type: "agent_heartbeat", agentIds, ts });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Process-wide pinger that publishes to the agentNotificationBus (the same
|
|
78
|
+
* channel background-agent completions use → the protocol server forwards each
|
|
79
|
+
* event to the client). `ensureRunning()` is called when an agent backgrounds;
|
|
80
|
+
* the pinger self-stops once no agent is running, so a fresh handoff re-arms it.
|
|
81
|
+
*/
|
|
82
|
+
export const agentHeartbeatPinger = new AgentHeartbeatPinger({
|
|
83
|
+
publish: (sessionId, event) => agentNotificationBus.publish(sessionId, event),
|
|
84
|
+
});
|
|
85
|
+
/** Start the shared pinger if not already ticking (idempotent). Call when an
|
|
86
|
+
* agent enters the background. */
|
|
87
|
+
export function ensureAgentHeartbeat() {
|
|
88
|
+
agentHeartbeatPinger.start();
|
|
89
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BackgroundAgentCompletedEvent } from "../../types.js";
|
|
1
|
+
import type { BackgroundAgentCompletedEvent, StreamEvent } from "../../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Background-agent completion notification queue.
|
|
4
4
|
*
|
|
@@ -30,8 +30,17 @@ export type NotificationItem = {
|
|
|
30
30
|
name?: string;
|
|
31
31
|
description: string;
|
|
32
32
|
status: "completed" | "failed";
|
|
33
|
+
/** What kind of background work this was (lets UIs localize the toast). */
|
|
34
|
+
workKind?: "agent" | "shell" | "video" | "cc";
|
|
35
|
+
/** For workKind === "shell": the command that ran. */
|
|
36
|
+
command?: string;
|
|
33
37
|
/** Final assistant text (completed only). */
|
|
34
38
|
finalText?: string;
|
|
39
|
+
/** For workKind === "cc": the external claude session id this drove, so the
|
|
40
|
+
* result is recoverable from ~/.claude/projects/.../<ccSessionId>.jsonl even
|
|
41
|
+
* if this notification is lost, and the user sees a real session id (not just
|
|
42
|
+
* an opaque background jobId). */
|
|
43
|
+
ccSessionId?: string;
|
|
35
44
|
/** Error message (failed only). */
|
|
36
45
|
error?: string;
|
|
37
46
|
enqueuedAt: number;
|
|
@@ -53,10 +62,10 @@ declare class NotificationQueue {
|
|
|
53
62
|
private notify;
|
|
54
63
|
}
|
|
55
64
|
export declare const notificationQueue: NotificationQueue;
|
|
56
|
-
type BusHandler = (sessionId: string, event:
|
|
65
|
+
type BusHandler = (sessionId: string, event: StreamEvent) => void;
|
|
57
66
|
declare class AgentNotificationBus {
|
|
58
67
|
private handlers;
|
|
59
|
-
publish(sessionId: string, event:
|
|
68
|
+
publish(sessionId: string, event: StreamEvent): void;
|
|
60
69
|
subscribe(handler: BusHandler): () => void;
|
|
61
70
|
}
|
|
62
71
|
export declare const agentNotificationBus: AgentNotificationBus;
|