@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +56 -0
- package/dist/context/compaction.js +186 -0
- package/dist/context/manager.js +25 -4
- 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 +267 -138
- package/dist/engine/engine.js +957 -191
- 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 +66 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +25 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.js +2 -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 +19 -0
- package/dist/engine/session-usage.js +25 -0
- package/dist/engine/steer-queue.d.ts +32 -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 +67 -3
- package/dist/engine/turn-loop.js +292 -41
- 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.js +11 -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 +49 -6
- package/dist/index.js +60 -4
- 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 +4 -0
- 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 +14 -0
- package/dist/llm/model-pool.js +13 -0
- 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 +407 -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 +7 -1
- package/dist/preset/index.js +96 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +27 -0
- package/dist/prompt/composer.js +67 -21
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +1 -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 +8 -2
- package/dist/protocol/chat-session-manager.js +36 -0
- package/dist/protocol/chat-session.d.ts +65 -0
- package/dist/protocol/chat-session.js +94 -2
- package/dist/protocol/client.d.ts +27 -0
- package/dist/protocol/client.js +38 -0
- package/dist/protocol/server.d.ts +112 -0
- package/dist/protocol/server.js +586 -16
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +46 -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 +495 -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 +15 -34
- package/dist/runtime/spawn-common.d.ts +148 -0
- package/dist/runtime/spawn-common.js +303 -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 +113 -3
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +25 -1
- package/dist/session/transcript.js +35 -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 +98 -62
- 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 +55 -3
- 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 +94 -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 +361 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.js +6 -8
- 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 +7 -1
- package/dist/tool-system/builtin/index.js +249 -25
- 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 +4 -1
- 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 +3 -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 +154 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +172 -88
- 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 +186 -20
- 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 +36 -1
- package/dist/tool-system/permission.js +281 -69
- 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/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +217 -6
- 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 +40 -0
- package/dist/utils/exec.js +144 -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
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFile, stat } from "node:fs/promises";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { fileCache } from "./file-cache.js";
|
|
7
|
-
import {
|
|
7
|
+
import { toLf } from "./eol.js";
|
|
8
8
|
export const readToolDef = {
|
|
9
9
|
name: "Read",
|
|
10
10
|
description: "Read a file from the local filesystem. Returns the file content with line numbers. " +
|
|
@@ -31,13 +31,6 @@ export async function readTool(args, ctx) {
|
|
|
31
31
|
const filePath = args.file_path;
|
|
32
32
|
if (!filePath)
|
|
33
33
|
return "Error: file_path is required";
|
|
34
|
-
// Path policy: in-workspace reads pass through; sensitive paths (~/.ssh,
|
|
35
|
-
// .env, etc.) and outside-workspace reads are refused with an explanatory
|
|
36
|
-
// error. existsSync check stays AFTER the policy gate — we don't want to
|
|
37
|
-
// probe sensitive paths' existence as a side-channel.
|
|
38
|
-
const blocked = enforcePathPolicy(filePath, "read", ctx?.cwd);
|
|
39
|
-
if (blocked)
|
|
40
|
-
return blocked;
|
|
41
34
|
if (!existsSync(filePath))
|
|
42
35
|
return `Error: File not found: ${filePath}`;
|
|
43
36
|
try {
|
|
@@ -54,10 +47,17 @@ export async function readTool(args, ctx) {
|
|
|
54
47
|
content = await readFile(filePath, "utf-8");
|
|
55
48
|
fileCache.set(filePath, content, fileInfo.mtimeMs);
|
|
56
49
|
}
|
|
57
|
-
|
|
50
|
+
// Split on LF after normalizing CRLF → LF so a Windows (CRLF) file doesn't
|
|
51
|
+
// render a trailing ^M on every line. Display-only; the file isn't written.
|
|
52
|
+
const lines = toLf(content).split("\n");
|
|
58
53
|
const totalLines = lines.length;
|
|
59
54
|
const offset = Math.max(1, args.offset || 1);
|
|
60
|
-
|
|
55
|
+
// A non-positive limit (a misbehaving caller passing 0 / -5 / NaN) falls back
|
|
56
|
+
// to the default page size — NOT through to `lines.slice(start, start+limit)`
|
|
57
|
+
// with a NEGATIVE end, which JS reads as "all but the last N" and would
|
|
58
|
+
// silently return the wrong window. `|| 2000` only caught 0/NaN, not negatives.
|
|
59
|
+
const rawLimit = args.limit;
|
|
60
|
+
const limit = typeof rawLimit === "number" && rawLimit > 0 ? rawLimit : 2000;
|
|
61
61
|
const end = Math.min(totalLines, offset - 1 + limit);
|
|
62
62
|
const selected = lines.slice(offset - 1, end);
|
|
63
63
|
let numbered = selected.map((line, i) => `${offset + i}\t${line}`).join("\n");
|
|
@@ -34,7 +34,10 @@ const REPL_MAX_BUFFER = 1024 * 1024;
|
|
|
34
34
|
export async function replTool(args, ctx) {
|
|
35
35
|
const language = args.language;
|
|
36
36
|
const code = args.code;
|
|
37
|
-
|
|
37
|
+
// `??` only catches null/undefined — a non-positive timeout (0/-5) would slip
|
|
38
|
+
// through to setTimeout (clamped to 0 → near-instant kill). Floor to default.
|
|
39
|
+
const rawTimeout = args.timeout;
|
|
40
|
+
const timeout = typeof rawTimeout === "number" && rawTimeout > 0 ? rawTimeout : 30_000;
|
|
38
41
|
// A4: child process runs in the Engine's cwd, not the host process cwd.
|
|
39
42
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
40
43
|
if (!code.trim()) {
|
|
@@ -42,6 +42,14 @@ export async function skillTool(args, ctx) {
|
|
|
42
42
|
// ordering is observable but not load-bearing.
|
|
43
43
|
const disabledSkills = ctx?.disabledSkills;
|
|
44
44
|
const disabledPlugins = ctx?.disabledPlugins;
|
|
45
|
+
const skillAllowlist = ctx?.skillAllowlist;
|
|
46
|
+
// Sub-agent skill isolation: when this agent has an allowlist, a skill
|
|
47
|
+
// outside it must be refused — even invoking it by exact name. The scan
|
|
48
|
+
// below also drops it, but an explicit message beats a misleading "not
|
|
49
|
+
// found" for a skill that exists in the project but isn't this agent's.
|
|
50
|
+
if (skillAllowlist !== undefined && !skillAllowlist.includes(skillName)) {
|
|
51
|
+
return `Skill "${skillName}" is not available to this sub-agent. This agent's role restricts it to: ${skillAllowlist.length > 0 ? skillAllowlist.join(", ") : "(no skills)"}.`;
|
|
52
|
+
}
|
|
45
53
|
if (disabledSkills && disabledSkills.includes(skillName)) {
|
|
46
54
|
return `Skill "${skillName}" is disabled. Enable it in Customize or remove it from settings.disabledSkills.`;
|
|
47
55
|
}
|
|
@@ -58,6 +66,7 @@ export async function skillTool(args, ctx) {
|
|
|
58
66
|
const skills = scanSkills(ctx?.cwd ?? process.cwd(), {
|
|
59
67
|
disabledSkills,
|
|
60
68
|
disabledPlugins,
|
|
69
|
+
skillAllowlist,
|
|
61
70
|
});
|
|
62
71
|
const found = skills.find((s) => s.name === skillName);
|
|
63
72
|
if (!found) {
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const sleepToolDef = {
|
|
5
5
|
name: "Sleep",
|
|
6
|
-
description: "Pause execution for a
|
|
7
|
-
"
|
|
6
|
+
description: "Pause execution for a brief, deterministic wait (e.g. letting a just-started service settle for a few seconds). " +
|
|
7
|
+
"Do NOT use Sleep to poll for or wait on background work (background shells, async sub-agents, video generation): " +
|
|
8
|
+
"the system wakes you automatically when that work completes — just end your turn instead of looping Sleep. " +
|
|
8
9
|
"Maximum duration is 300 seconds (5 minutes).",
|
|
9
10
|
inputSchema: {
|
|
10
11
|
type: "object",
|
|
@@ -31,20 +31,38 @@ export async function toolSearchTool(args, ctx) {
|
|
|
31
31
|
return "Error: query is required";
|
|
32
32
|
if (!ctx?.toolRegistry)
|
|
33
33
|
return "Error: ToolSearch is not configured (no registry in ctx)";
|
|
34
|
-
|
|
34
|
+
// Floor to default on non-positive: a negative max_results would reach
|
|
35
|
+
// `.slice(0, maxResults)` with a negative end (= all but the last N), silently
|
|
36
|
+
// returning the wrong tool set. `|| 5` only caught 0/NaN, not negatives.
|
|
37
|
+
const rawMax = args.max_results;
|
|
38
|
+
const maxResults = Math.min(typeof rawMax === "number" && rawMax > 0 ? rawMax : 5, 20);
|
|
39
|
+
// The tool registry is worker-SHARED (B1): it holds MCP tools registered by
|
|
40
|
+
// every session, including servers another project enabled. ToolSearch is a
|
|
41
|
+
// side door around the per-turn toolDefs filter — without this gate it would
|
|
42
|
+
// surface (and let the model `select:` into) MCP tools from a server THIS
|
|
43
|
+
// session never enabled, which is exactly the chrome-devtools-in-writeflow
|
|
44
|
+
// leak. Mirror the engine's toolDefs visibility: keep an MCP tool only when
|
|
45
|
+
// its server is in this session's allowedMcpServers. Undefined set = no
|
|
46
|
+
// gating (sub-agents / hosts that don't populate it).
|
|
47
|
+
const visible = (tool) => {
|
|
48
|
+
if (tool.source !== "mcp")
|
|
49
|
+
return true;
|
|
50
|
+
const allowed = ctx.allowedMcpServers;
|
|
51
|
+
return !allowed || allowed.has(tool.serverName ?? "");
|
|
52
|
+
};
|
|
35
53
|
// "select:Name1,Name2" → exact match
|
|
36
54
|
if (query.startsWith("select:")) {
|
|
37
55
|
const names = query.slice(7).split(",").map((n) => n.trim());
|
|
38
|
-
return matchExact(ctx.toolRegistry, names);
|
|
56
|
+
return matchExact(ctx.toolRegistry, names, visible);
|
|
39
57
|
}
|
|
40
58
|
// Keyword search
|
|
41
|
-
return searchByKeyword(ctx.toolRegistry, query, maxResults);
|
|
59
|
+
return searchByKeyword(ctx.toolRegistry, query, maxResults, visible);
|
|
42
60
|
}
|
|
43
|
-
function matchExact(registry, names) {
|
|
61
|
+
function matchExact(registry, names, visible) {
|
|
44
62
|
const results = [];
|
|
45
63
|
for (const name of names) {
|
|
46
64
|
const tool = registry.getTool(name);
|
|
47
|
-
if (tool) {
|
|
65
|
+
if (tool && visible(tool)) {
|
|
48
66
|
results.push(formatTool(tool));
|
|
49
67
|
}
|
|
50
68
|
else {
|
|
@@ -53,8 +71,8 @@ function matchExact(registry, names) {
|
|
|
53
71
|
}
|
|
54
72
|
return results.join("\n\n---\n\n");
|
|
55
73
|
}
|
|
56
|
-
function searchByKeyword(registry, query, maxResults) {
|
|
57
|
-
const allTools = registry.listToolsDetailed();
|
|
74
|
+
function searchByKeyword(registry, query, maxResults, visible) {
|
|
75
|
+
const allTools = registry.listToolsDetailed().filter(visible);
|
|
58
76
|
const queryLower = query.toLowerCase();
|
|
59
77
|
const keywords = queryLower.split(/\s+/);
|
|
60
78
|
// Score each tool
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VideoProvider — adapter interface for text-to-video generation (TODO 7.1).
|
|
3
|
+
*
|
|
4
|
+
* Unlike images (one request → bytes), video generation is asynchronous and
|
|
5
|
+
* long: SUBMIT a job → POLL its status until it finishes → DOWNLOAD the bytes.
|
|
6
|
+
* The interface captures exactly those three steps so the GenerateVideo tool's
|
|
7
|
+
* background polling loop is provider-agnostic, and concrete adapters
|
|
8
|
+
* (Seedance/Kling — filled in once their private API docs are available) only
|
|
9
|
+
* implement submit/poll/download.
|
|
10
|
+
*
|
|
11
|
+
* Credentials are resolved by the tool layer from settings; adapters are pure
|
|
12
|
+
* "creds + request → job lifecycle". `fetch` is injected for testability.
|
|
13
|
+
*/
|
|
14
|
+
export interface VideoProviderCreds {
|
|
15
|
+
baseUrl: string;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
}
|
|
18
|
+
export interface VideoSubmitRequest {
|
|
19
|
+
prompt: string;
|
|
20
|
+
model: string;
|
|
21
|
+
creds: VideoProviderCreds;
|
|
22
|
+
/** Single image URL (http/https) — image-to-video. Back-compat. */
|
|
23
|
+
image?: string;
|
|
24
|
+
/** Multiple image URLs — ≥2 triggers reference-to-video. Takes precedence over `image`. */
|
|
25
|
+
images?: string[];
|
|
26
|
+
/** Reference/continuation video URLs (http/https) — passed through to fal's `video_urls`. Reference them in the prompt as @Video1/@Video2. Forces reference-to-video. */
|
|
27
|
+
videos?: string[];
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
export type VideoSubmitResult = {
|
|
31
|
+
ok: true;
|
|
32
|
+
jobId: string;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
|
37
|
+
export type VideoPollResult = {
|
|
38
|
+
ok: true;
|
|
39
|
+
status: "running";
|
|
40
|
+
} | {
|
|
41
|
+
ok: true;
|
|
42
|
+
status: "succeeded";
|
|
43
|
+
} | {
|
|
44
|
+
ok: true;
|
|
45
|
+
status: "failed";
|
|
46
|
+
error: string;
|
|
47
|
+
} | {
|
|
48
|
+
ok: false;
|
|
49
|
+
error: string;
|
|
50
|
+
};
|
|
51
|
+
export type VideoDownloadResult = {
|
|
52
|
+
ok: true;
|
|
53
|
+
bytes: Uint8Array;
|
|
54
|
+
ext: string;
|
|
55
|
+
url?: string;
|
|
56
|
+
} | {
|
|
57
|
+
ok: false;
|
|
58
|
+
error: string;
|
|
59
|
+
};
|
|
60
|
+
export interface VideoProvider {
|
|
61
|
+
readonly kind: string;
|
|
62
|
+
submit(req: VideoSubmitRequest): Promise<VideoSubmitResult>;
|
|
63
|
+
poll(req: {
|
|
64
|
+
jobId: string;
|
|
65
|
+
creds: VideoProviderCreds;
|
|
66
|
+
signal?: AbortSignal;
|
|
67
|
+
}): Promise<VideoPollResult>;
|
|
68
|
+
download(req: {
|
|
69
|
+
jobId: string;
|
|
70
|
+
creds: VideoProviderCreds;
|
|
71
|
+
signal?: AbortSignal;
|
|
72
|
+
}): Promise<VideoDownloadResult>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Deterministic in-memory adapter for tests and local dev — no network. Models
|
|
76
|
+
* the three-step lifecycle: submit mints a job; poll returns `running` for the
|
|
77
|
+
* first N calls then `succeeded` (or `failed` after M); download returns the
|
|
78
|
+
* configured bytes. Lets the GenerateVideo background loop be tested end-to-end
|
|
79
|
+
* before any real (Seedance/Kling) adapter exists.
|
|
80
|
+
*/
|
|
81
|
+
export declare class FakeVideoProvider implements VideoProvider {
|
|
82
|
+
private readonly opts;
|
|
83
|
+
readonly kind = "fake";
|
|
84
|
+
private polls;
|
|
85
|
+
private counter;
|
|
86
|
+
constructor(opts?: {
|
|
87
|
+
succeedAfterPolls?: number;
|
|
88
|
+
failAfterPolls?: number;
|
|
89
|
+
failMessage?: string;
|
|
90
|
+
bytes?: string;
|
|
91
|
+
});
|
|
92
|
+
submit(_req: VideoSubmitRequest): Promise<VideoSubmitResult>;
|
|
93
|
+
poll(req: {
|
|
94
|
+
jobId: string;
|
|
95
|
+
creds?: VideoProviderCreds;
|
|
96
|
+
signal?: AbortSignal;
|
|
97
|
+
}): Promise<VideoPollResult>;
|
|
98
|
+
download(_req: {
|
|
99
|
+
jobId: string;
|
|
100
|
+
creds?: VideoProviderCreds;
|
|
101
|
+
signal?: AbortSignal;
|
|
102
|
+
}): Promise<VideoDownloadResult>;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* fal.ai video adapter — submit/poll/download against the fal queue API
|
|
106
|
+
* (https://queue.fal.run). model id selects the underlying model
|
|
107
|
+
* (e.g. fal-ai/kling-video/v3/pro/text-to-video). When `image` is present we
|
|
108
|
+
* switch a `...text-to-video` model to `...image-to-video` and send image_url,
|
|
109
|
+
* so callers only need to pass an image to get i2v.
|
|
110
|
+
*
|
|
111
|
+
* IMPORTANT: fal's status/result URLs use a model-FAMILY prefix
|
|
112
|
+
* (`fal-ai/kling-video/requests/<id>`), NOT the full submit path
|
|
113
|
+
* (`.../v3/pro/text-to-video/requests/<id>` → HTTP 405). So we never
|
|
114
|
+
* reconstruct those URLs; we encode the `status_url` + `response_url` that
|
|
115
|
+
* submit returns into the jobId (`<status_url>|<response_url>`) and use them
|
|
116
|
+
* verbatim in poll/download. Verified against the live API 2026-06-10.
|
|
117
|
+
*/
|
|
118
|
+
export declare class FalVideoProvider implements VideoProvider {
|
|
119
|
+
private readonly fetchImpl;
|
|
120
|
+
readonly kind = "fal";
|
|
121
|
+
constructor(fetchImpl?: typeof fetch);
|
|
122
|
+
/** Effective image list: images[] wins; else single image; else []. */
|
|
123
|
+
private imageList;
|
|
124
|
+
/**
|
|
125
|
+
* Does this model family support reference-to-video (multi-image)? Verified
|
|
126
|
+
* 2026-06-10 against the live API: Seedance has it; Kling does NOT (routing a
|
|
127
|
+
* Kling model to `.../reference-to-video` 404s at result fetch). Keep this a
|
|
128
|
+
* conservative allowlist — only families confirmed to expose the endpoint.
|
|
129
|
+
*/
|
|
130
|
+
private supportsReference;
|
|
131
|
+
/** Route model suffix by image count: 0→text, 1→image, ≥2→reference. */
|
|
132
|
+
private resolveModel;
|
|
133
|
+
/** jobId = `${statusUrl}|${responseUrl}` (fal returns both from submit). */
|
|
134
|
+
private split;
|
|
135
|
+
submit(req: VideoSubmitRequest): Promise<VideoSubmitResult>;
|
|
136
|
+
poll(req: {
|
|
137
|
+
jobId: string;
|
|
138
|
+
creds: VideoProviderCreds;
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
}): Promise<VideoPollResult>;
|
|
141
|
+
download(req: {
|
|
142
|
+
jobId: string;
|
|
143
|
+
creds: VideoProviderCreds;
|
|
144
|
+
signal?: AbortSignal;
|
|
145
|
+
}): Promise<VideoDownloadResult>;
|
|
146
|
+
}
|
|
147
|
+
/** Default model per video provider kind (filled in with real adapters). */
|
|
148
|
+
export declare const DEFAULT_VIDEO_MODEL: Record<string, string>;
|
|
149
|
+
/**
|
|
150
|
+
* Registry of video-provider adapters, keyed by `kind`. Only `fake` exists
|
|
151
|
+
* today (test/dev). Real adapters (seedance, kling) are intentionally absent
|
|
152
|
+
* until their private API contracts are confirmed — see TODO 7.1 块3.
|
|
153
|
+
*/
|
|
154
|
+
export declare function getVideoProvider(kind: string, fetchImpl?: typeof fetch): VideoProvider | null;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VideoProvider — adapter interface for text-to-video generation (TODO 7.1).
|
|
3
|
+
*
|
|
4
|
+
* Unlike images (one request → bytes), video generation is asynchronous and
|
|
5
|
+
* long: SUBMIT a job → POLL its status until it finishes → DOWNLOAD the bytes.
|
|
6
|
+
* The interface captures exactly those three steps so the GenerateVideo tool's
|
|
7
|
+
* background polling loop is provider-agnostic, and concrete adapters
|
|
8
|
+
* (Seedance/Kling — filled in once their private API docs are available) only
|
|
9
|
+
* implement submit/poll/download.
|
|
10
|
+
*
|
|
11
|
+
* Credentials are resolved by the tool layer from settings; adapters are pure
|
|
12
|
+
* "creds + request → job lifecycle". `fetch` is injected for testability.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Deterministic in-memory adapter for tests and local dev — no network. Models
|
|
16
|
+
* the three-step lifecycle: submit mints a job; poll returns `running` for the
|
|
17
|
+
* first N calls then `succeeded` (or `failed` after M); download returns the
|
|
18
|
+
* configured bytes. Lets the GenerateVideo background loop be tested end-to-end
|
|
19
|
+
* before any real (Seedance/Kling) adapter exists.
|
|
20
|
+
*/
|
|
21
|
+
export class FakeVideoProvider {
|
|
22
|
+
opts;
|
|
23
|
+
kind = "fake";
|
|
24
|
+
polls = new Map();
|
|
25
|
+
counter = 0;
|
|
26
|
+
constructor(opts = {}) {
|
|
27
|
+
this.opts = opts;
|
|
28
|
+
}
|
|
29
|
+
async submit(_req) {
|
|
30
|
+
this.counter += 1;
|
|
31
|
+
const jobId = `fakejob-${this.counter}`;
|
|
32
|
+
this.polls.set(jobId, 0);
|
|
33
|
+
return { ok: true, jobId };
|
|
34
|
+
}
|
|
35
|
+
async poll(req) {
|
|
36
|
+
const n = this.polls.get(req.jobId) ?? 0;
|
|
37
|
+
this.polls.set(req.jobId, n + 1);
|
|
38
|
+
if (this.opts.failAfterPolls !== undefined && n >= this.opts.failAfterPolls) {
|
|
39
|
+
return { ok: true, status: "failed", error: this.opts.failMessage ?? "job failed" };
|
|
40
|
+
}
|
|
41
|
+
if (this.opts.succeedAfterPolls !== undefined && n >= this.opts.succeedAfterPolls) {
|
|
42
|
+
return { ok: true, status: "succeeded" };
|
|
43
|
+
}
|
|
44
|
+
return { ok: true, status: "running" };
|
|
45
|
+
}
|
|
46
|
+
async download(_req) {
|
|
47
|
+
return { ok: true, bytes: Buffer.from(this.opts.bytes ?? "FAKE_VIDEO"), ext: "mp4" };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* fal.ai video adapter — submit/poll/download against the fal queue API
|
|
52
|
+
* (https://queue.fal.run). model id selects the underlying model
|
|
53
|
+
* (e.g. fal-ai/kling-video/v3/pro/text-to-video). When `image` is present we
|
|
54
|
+
* switch a `...text-to-video` model to `...image-to-video` and send image_url,
|
|
55
|
+
* so callers only need to pass an image to get i2v.
|
|
56
|
+
*
|
|
57
|
+
* IMPORTANT: fal's status/result URLs use a model-FAMILY prefix
|
|
58
|
+
* (`fal-ai/kling-video/requests/<id>`), NOT the full submit path
|
|
59
|
+
* (`.../v3/pro/text-to-video/requests/<id>` → HTTP 405). So we never
|
|
60
|
+
* reconstruct those URLs; we encode the `status_url` + `response_url` that
|
|
61
|
+
* submit returns into the jobId (`<status_url>|<response_url>`) and use them
|
|
62
|
+
* verbatim in poll/download. Verified against the live API 2026-06-10.
|
|
63
|
+
*/
|
|
64
|
+
export class FalVideoProvider {
|
|
65
|
+
fetchImpl;
|
|
66
|
+
kind = "fal";
|
|
67
|
+
constructor(fetchImpl = fetch) {
|
|
68
|
+
this.fetchImpl = fetchImpl;
|
|
69
|
+
}
|
|
70
|
+
/** Effective image list: images[] wins; else single image; else []. */
|
|
71
|
+
imageList(req) {
|
|
72
|
+
if (req.images && req.images.length)
|
|
73
|
+
return req.images;
|
|
74
|
+
if (req.image)
|
|
75
|
+
return [req.image];
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Does this model family support reference-to-video (multi-image)? Verified
|
|
80
|
+
* 2026-06-10 against the live API: Seedance has it; Kling does NOT (routing a
|
|
81
|
+
* Kling model to `.../reference-to-video` 404s at result fetch). Keep this a
|
|
82
|
+
* conservative allowlist — only families confirmed to expose the endpoint.
|
|
83
|
+
*/
|
|
84
|
+
supportsReference(model) {
|
|
85
|
+
return /seedance/i.test(model);
|
|
86
|
+
}
|
|
87
|
+
/** Route model suffix by image count: 0→text, 1→image, ≥2→reference. */
|
|
88
|
+
resolveModel(model, imageCount) {
|
|
89
|
+
const target = imageCount >= 2 ? "reference-to-video" : imageCount === 1 ? "image-to-video" : null;
|
|
90
|
+
if (target && /(text|image|reference)-to-video$/.test(model)) {
|
|
91
|
+
return model.replace(/(text|image|reference)-to-video$/, target);
|
|
92
|
+
}
|
|
93
|
+
return model;
|
|
94
|
+
}
|
|
95
|
+
/** jobId = `${statusUrl}|${responseUrl}` (fal returns both from submit). */
|
|
96
|
+
split(jobId) {
|
|
97
|
+
const idx = jobId.indexOf("|");
|
|
98
|
+
if (idx < 0)
|
|
99
|
+
return { statusUrl: jobId, responseUrl: jobId };
|
|
100
|
+
return { statusUrl: jobId.slice(0, idx), responseUrl: jobId.slice(idx + 1) };
|
|
101
|
+
}
|
|
102
|
+
async submit(req) {
|
|
103
|
+
const base = req.creds.baseUrl.replace(/\/+$/, "");
|
|
104
|
+
const imgs = this.imageList(req);
|
|
105
|
+
const vids = req.videos ?? [];
|
|
106
|
+
// Multi-image OR any reference video needs the reference-to-video endpoint,
|
|
107
|
+
// which only some families (Seedance) expose. Routing e.g. Kling there
|
|
108
|
+
// 404s at result fetch — fail fast with a clear message instead.
|
|
109
|
+
const needsReference = imgs.length >= 2 || vids.length > 0;
|
|
110
|
+
if (needsReference && !this.supportsReference(req.model)) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
error: `model "${req.model}" does not support reference-to-video (multiple images or reference videos). Use a Seedance model, or pass a single image / no video.`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// imageCount alone can't express "reference needed"; bump to 2 when a video
|
|
117
|
+
// forces the reference endpoint so resolveModel picks reference-to-video.
|
|
118
|
+
const refImageCount = needsReference ? Math.max(imgs.length, 2) : imgs.length;
|
|
119
|
+
const model = this.resolveModel(req.model, refImageCount);
|
|
120
|
+
const body = { prompt: req.prompt };
|
|
121
|
+
if (imgs.length >= 2)
|
|
122
|
+
body.image_urls = imgs;
|
|
123
|
+
else if (imgs.length === 1)
|
|
124
|
+
body.image_url = imgs[0];
|
|
125
|
+
if (vids.length > 0)
|
|
126
|
+
body.video_urls = vids;
|
|
127
|
+
try {
|
|
128
|
+
const r = await this.fetchImpl(`${base}/${model}`, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { Authorization: `Key ${req.creds.apiKey}`, "Content-Type": "application/json" },
|
|
131
|
+
body: JSON.stringify(body),
|
|
132
|
+
signal: req.signal,
|
|
133
|
+
});
|
|
134
|
+
if (!r.ok) {
|
|
135
|
+
const t = await r.text().catch(() => "");
|
|
136
|
+
return { ok: false, error: `fal submit failed: HTTP ${r.status} ${t}`.trim() };
|
|
137
|
+
}
|
|
138
|
+
const j = (await r.json());
|
|
139
|
+
if (!j.request_id)
|
|
140
|
+
return { ok: false, error: "fal submit: no request_id in response" };
|
|
141
|
+
// Prefer the URLs fal hands back; fall back to family-prefix reconstruction
|
|
142
|
+
// only if they're absent (defensive — live API always returns them).
|
|
143
|
+
const statusUrl = j.status_url ?? "";
|
|
144
|
+
const responseUrl = j.response_url ?? "";
|
|
145
|
+
if (!statusUrl || !responseUrl) {
|
|
146
|
+
return { ok: false, error: "fal submit: missing status_url/response_url" };
|
|
147
|
+
}
|
|
148
|
+
return { ok: true, jobId: `${statusUrl}|${responseUrl}` };
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
return { ok: false, error: `fal submit error: ${err.message}` };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async poll(req) {
|
|
155
|
+
const { statusUrl } = this.split(req.jobId);
|
|
156
|
+
try {
|
|
157
|
+
const r = await this.fetchImpl(statusUrl, {
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers: { Authorization: `Key ${req.creds.apiKey}` },
|
|
160
|
+
signal: req.signal,
|
|
161
|
+
});
|
|
162
|
+
// fal returns 200/202 for valid in-progress/queued states; only treat a
|
|
163
|
+
// hard client/server error (4xx except 202, 5xx) as a transport failure.
|
|
164
|
+
if (!r.ok && r.status !== 202) {
|
|
165
|
+
const t = await r.text().catch(() => "");
|
|
166
|
+
return { ok: false, error: `fal status failed: HTTP ${r.status} ${t}`.trim() };
|
|
167
|
+
}
|
|
168
|
+
const j = (await r.json());
|
|
169
|
+
switch (j.status) {
|
|
170
|
+
case "IN_QUEUE":
|
|
171
|
+
case "IN_PROGRESS":
|
|
172
|
+
return { ok: true, status: "running" };
|
|
173
|
+
case "COMPLETED":
|
|
174
|
+
return { ok: true, status: "succeeded" };
|
|
175
|
+
default:
|
|
176
|
+
return { ok: true, status: "failed", error: `fal status: ${j.status ?? "unknown"}` };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
return { ok: false, error: `fal poll error: ${err.message}` };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async download(req) {
|
|
184
|
+
const { responseUrl } = this.split(req.jobId);
|
|
185
|
+
try {
|
|
186
|
+
// hop 1: result JSON
|
|
187
|
+
const r = await this.fetchImpl(responseUrl, {
|
|
188
|
+
method: "GET",
|
|
189
|
+
headers: { Authorization: `Key ${req.creds.apiKey}` },
|
|
190
|
+
signal: req.signal,
|
|
191
|
+
});
|
|
192
|
+
if (!r.ok) {
|
|
193
|
+
const t = await r.text().catch(() => "");
|
|
194
|
+
return { ok: false, error: `fal result failed: HTTP ${r.status} ${t}`.trim() };
|
|
195
|
+
}
|
|
196
|
+
const j = (await r.json());
|
|
197
|
+
const videoUrl = j.video?.url;
|
|
198
|
+
if (!videoUrl)
|
|
199
|
+
return { ok: false, error: "fal result: no video.url" };
|
|
200
|
+
// hop 2: video bytes
|
|
201
|
+
const vr = await this.fetchImpl(videoUrl, { method: "GET", signal: req.signal });
|
|
202
|
+
if (!vr.ok) {
|
|
203
|
+
return { ok: false, error: `fal video download failed: HTTP ${vr.status}` };
|
|
204
|
+
}
|
|
205
|
+
const buf = new Uint8Array(await vr.arrayBuffer());
|
|
206
|
+
const m = /\.([a-z0-9]{2,4})(?:\?|$)/i.exec(videoUrl);
|
|
207
|
+
const ext = m ? m[1].toLowerCase() : "mp4";
|
|
208
|
+
// Surface the fal-hosted URL so the model can reuse it for video extension
|
|
209
|
+
// (videos[] → video_urls) without re-uploading. Note: signed/expiring.
|
|
210
|
+
return { ok: true, bytes: buf, ext, url: videoUrl };
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
return { ok: false, error: `fal download error: ${err.message}` };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/** Default model per video provider kind (filled in with real adapters). */
|
|
218
|
+
export const DEFAULT_VIDEO_MODEL = {
|
|
219
|
+
fal: "fal-ai/kling-video/v3/pro/text-to-video",
|
|
220
|
+
};
|
|
221
|
+
/**
|
|
222
|
+
* Registry of video-provider adapters, keyed by `kind`. Only `fake` exists
|
|
223
|
+
* today (test/dev). Real adapters (seedance, kling) are intentionally absent
|
|
224
|
+
* until their private API contracts are confirmed — see TODO 7.1 块3.
|
|
225
|
+
*/
|
|
226
|
+
export function getVideoProvider(kind, fetchImpl = fetch) {
|
|
227
|
+
switch (kind) {
|
|
228
|
+
case "fake":
|
|
229
|
+
return new FakeVideoProvider({ succeedAfterPolls: 0 });
|
|
230
|
+
case "fal":
|
|
231
|
+
return new FalVideoProvider(fetchImpl);
|
|
232
|
+
default:
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -176,7 +176,12 @@ export async function webFetchTool(args) {
|
|
|
176
176
|
catch {
|
|
177
177
|
return `Error: invalid URL "${url}"`;
|
|
178
178
|
}
|
|
179
|
-
|
|
179
|
+
// A non-positive max_length must NOT reach `text.slice(0, maxLength)` below
|
|
180
|
+
// with a negative end — JS reads that as "all but the last N", silently
|
|
181
|
+
// returning the WRONG bytes (+ a misleading "truncated" suffix). `|| DEFAULT`
|
|
182
|
+
// only caught 0/NaN, not negatives. Floor to the default page size first.
|
|
183
|
+
const rawMaxLength = args.max_length;
|
|
184
|
+
const maxLength = Math.min(typeof rawMaxLength === "number" && rawMaxLength > 0 ? rawMaxLength : DEFAULT_MAX, MAX_OUTPUT);
|
|
180
185
|
const rawHeaders = args.headers ?? {};
|
|
181
186
|
const baseHeaders = {
|
|
182
187
|
"User-Agent": "Mozilla/5.0 (compatible; code-shell/0.1; +https://github.com/nicepkg/code-shell)",
|
|
@@ -192,7 +197,12 @@ export async function webFetchTool(args) {
|
|
|
192
197
|
// host, and DNS-resolved IPs against the block lists. fetch() runs
|
|
193
198
|
// with redirect: "manual" so the runtime cannot silently follow a
|
|
194
199
|
// redirect into private/loopback/metadata space.
|
|
200
|
+
// Combine the 30s fetch timeout with the run's abort signal (registry injects
|
|
201
|
+
// __signal) so a user Stop cancels an in-flight fetch promptly, not only on
|
|
202
|
+
// the 30s timeout. Timeout alone was the previous behavior.
|
|
195
203
|
const timeoutSignal = AbortSignal.timeout(30_000);
|
|
204
|
+
const userSignal = args.__signal;
|
|
205
|
+
const fetchSignal = userSignal ? AbortSignal.any([userSignal, timeoutSignal]) : timeoutSignal;
|
|
196
206
|
let currentUrl = parsed;
|
|
197
207
|
let currentHeaders = { ...baseHeaders };
|
|
198
208
|
const originalOrigin = parsed;
|
|
@@ -206,7 +216,7 @@ export async function webFetchTool(args) {
|
|
|
206
216
|
const res = await fetch(currentUrl, {
|
|
207
217
|
headers: currentHeaders,
|
|
208
218
|
redirect: "manual",
|
|
209
|
-
signal:
|
|
219
|
+
signal: fetchSignal,
|
|
210
220
|
});
|
|
211
221
|
// Not a redirect: return body (or HTTP error).
|
|
212
222
|
if (res.status < 300 || res.status >= 400 || !res.headers.get("location")) {
|
|
@@ -119,23 +119,34 @@ export async function webSearchTool(args, ctx) {
|
|
|
119
119
|
const query = args.query;
|
|
120
120
|
if (!query)
|
|
121
121
|
return "Error: query is required";
|
|
122
|
-
|
|
122
|
+
// Floor to default on non-positive: a negative num_results would reach
|
|
123
|
+
// `results.slice(0, num)` with a negative end (= all but the last N), silently
|
|
124
|
+
// returning the wrong result set. `|| 10` only caught 0/NaN, not negatives.
|
|
125
|
+
const rawNum = args.num_results;
|
|
126
|
+
const numResults = Math.min(typeof rawNum === "number" && rawNum > 0 ? rawNum : 10, 20);
|
|
123
127
|
// Use the run's cwd (e.g. an automation job's project dir) so per-project
|
|
124
128
|
// settings resolve correctly; falls back to process.cwd() when absent.
|
|
125
129
|
const config = resolveSearchConfig(ctx?.cwd);
|
|
126
130
|
if (config.source === "none") {
|
|
127
131
|
return "Error: No search provider configured. Open desktop 设置 → 连接 to add credentials, or set SERPER_API_KEY / TAVILY_API_KEY / SEARXNG_URL.";
|
|
128
132
|
}
|
|
133
|
+
// A search provider's fetch had NO timeout and NO abort wiring — a hung/slow
|
|
134
|
+
// provider blocked the tool indefinitely and a user Stop couldn't cancel it.
|
|
135
|
+
// Give every provider call a 20s timeout, combined with the run's abort
|
|
136
|
+
// signal (registry injects __signal) so Stop is prompt.
|
|
137
|
+
const userSignal = args.__signal;
|
|
138
|
+
const timeoutSignal = AbortSignal.timeout(20_000);
|
|
139
|
+
const signal = userSignal ? AbortSignal.any([userSignal, timeoutSignal]) : timeoutSignal;
|
|
129
140
|
try {
|
|
130
141
|
let results;
|
|
131
142
|
if (config.provider === "serper" && config.apiKey) {
|
|
132
|
-
results = await searchSerper(query, numResults, config.apiKey);
|
|
143
|
+
results = await searchSerper(query, numResults, config.apiKey, signal);
|
|
133
144
|
}
|
|
134
145
|
else if (config.provider === "tavily" && config.apiKey) {
|
|
135
|
-
results = await searchTavily(query, numResults, config.apiKey);
|
|
146
|
+
results = await searchTavily(query, numResults, config.apiKey, signal);
|
|
136
147
|
}
|
|
137
148
|
else if (config.provider === "searxng" && config.baseUrl) {
|
|
138
|
-
results = await searchSearXNG(query, numResults, config.baseUrl);
|
|
149
|
+
results = await searchSearXNG(query, numResults, config.baseUrl, signal);
|
|
139
150
|
}
|
|
140
151
|
else {
|
|
141
152
|
return `Error: provider "${config.provider}" missing ${config.provider === "searxng" ? "base URL" : "API key"}`;
|
|
@@ -150,7 +161,7 @@ export async function webSearchTool(args, ctx) {
|
|
|
150
161
|
return `Search error: ${err.message}`;
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
|
-
async function searchSerper(query, num, apiKey) {
|
|
164
|
+
async function searchSerper(query, num, apiKey, signal) {
|
|
154
165
|
const res = await fetch("https://google.serper.dev/search", {
|
|
155
166
|
method: "POST",
|
|
156
167
|
headers: {
|
|
@@ -158,6 +169,7 @@ async function searchSerper(query, num, apiKey) {
|
|
|
158
169
|
"Content-Type": "application/json",
|
|
159
170
|
},
|
|
160
171
|
body: JSON.stringify({ q: query, num }),
|
|
172
|
+
signal,
|
|
161
173
|
});
|
|
162
174
|
if (!res.ok)
|
|
163
175
|
throw new Error(`Serper API error: ${res.status} ${res.statusText}`);
|
|
@@ -168,7 +180,7 @@ async function searchSerper(query, num, apiKey) {
|
|
|
168
180
|
snippet: item.snippet ?? "",
|
|
169
181
|
}));
|
|
170
182
|
}
|
|
171
|
-
async function searchTavily(query, num, apiKey) {
|
|
183
|
+
async function searchTavily(query, num, apiKey, signal) {
|
|
172
184
|
const res = await fetch("https://api.tavily.com/search", {
|
|
173
185
|
method: "POST",
|
|
174
186
|
headers: { "Content-Type": "application/json" },
|
|
@@ -178,6 +190,7 @@ async function searchTavily(query, num, apiKey) {
|
|
|
178
190
|
max_results: num,
|
|
179
191
|
include_answer: false,
|
|
180
192
|
}),
|
|
193
|
+
signal,
|
|
181
194
|
});
|
|
182
195
|
if (!res.ok)
|
|
183
196
|
throw new Error(`Tavily API error: ${res.status} ${res.statusText}`);
|
|
@@ -188,13 +201,14 @@ async function searchTavily(query, num, apiKey) {
|
|
|
188
201
|
snippet: item.content ?? "",
|
|
189
202
|
}));
|
|
190
203
|
}
|
|
191
|
-
async function searchSearXNG(query, num, baseUrl) {
|
|
204
|
+
async function searchSearXNG(query, num, baseUrl, signal) {
|
|
192
205
|
const url = new URL("/search", baseUrl);
|
|
193
206
|
url.searchParams.set("q", query);
|
|
194
207
|
url.searchParams.set("format", "json");
|
|
195
208
|
url.searchParams.set("pageno", "1");
|
|
196
209
|
const res = await fetch(url.toString(), {
|
|
197
210
|
headers: { Accept: "application/json" },
|
|
211
|
+
signal,
|
|
198
212
|
});
|
|
199
213
|
if (!res.ok)
|
|
200
214
|
throw new Error(`SearXNG error: ${res.status} ${res.statusText}`);
|