@sayknow-cli/coding-agent 0.4.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +112 -0
- package/dist/types/capability/index.d.ts +10 -20
- package/dist/types/capability/types.d.ts +3 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/cli.d.ts +9 -1
- package/dist/types/commands/daemon.d.ts +2 -2
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/commands/sdk.d.ts +20 -2
- package/dist/types/config/keybindings.d.ts +34 -4
- package/dist/types/config/model-profile-contract.d.ts +40 -0
- package/dist/types/config/model-profiles.d.ts +2 -3
- package/dist/types/config/model-registry.d.ts +5 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +156 -10
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/runner-artifact.d.ts +8 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/extensibility/extensions/types.d.ts +43 -2
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +33 -2
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +22 -9
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/queue-pane.d.ts +8 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/tool-status-header.d.ts +4 -2
- package/dist/types/modes/components/welcome.d.ts +5 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +13 -2
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +6 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/runtime/memory-domain.d.ts +9 -0
- package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
- package/dist/types/runtime/memory-guard.d.ts +39 -0
- package/dist/types/runtime/memory-limit.d.ts +11 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
- package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
- package/dist/types/runtime-mcp/manager.d.ts +9 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/runtime-mcp/redaction.d.ts +2 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/final-text.d.ts +19 -0
- package/dist/types/sdk/acp/mcp.d.ts +23 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +52 -4
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
- package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/notification-service.d.ts +34 -0
- package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
- package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
- package/dist/types/sdk/client/discovery.d.ts +2 -0
- package/dist/types/sdk/client/liveness.d.ts +7 -0
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/host.d.ts +3 -1
- package/dist/types/sdk/host/query/handlers.d.ts +14 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/lifecycle-session.d.ts +10 -1
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +7 -0
- package/dist/types/sdk/startup-capability.d.ts +9 -0
- package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
- package/dist/types/sdk/transport/index.d.ts +12 -0
- package/dist/types/sdk/transport/relay.d.ts +43 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
- package/dist/types/sdk/transport/socket.d.ts +7 -0
- package/dist/types/sdk/transport/stdio.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +83 -24
- package/dist/types/session/btw-contract.d.ts +16 -0
- package/dist/types/session/fallback-chain-controller.d.ts +18 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
- package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +89 -18
- package/dist/types/session/session-storage.d.ts +31 -0
- package/dist/types/session/streaming-output.d.ts +60 -5
- package/dist/types/setup/credential-import.d.ts +5 -1
- package/dist/types/setup/provider-onboarding.d.ts +2 -0
- package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
- package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
- package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
- package/dist/types/skc-runtime/state-writer.d.ts +5 -1
- package/dist/types/skc-runtime/team-launch.d.ts +1 -1
- package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
- package/dist/types/skc-runtime/team-store.d.ts +12 -0
- package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
- package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
- package/dist/types/skill-state/workflow-hud.d.ts +3 -0
- package/dist/types/ssh/utils.d.ts +1 -0
- package/dist/types/task/discovery.d.ts +3 -1
- package/dist/types/task/executor.d.ts +6 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +13 -0
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +133 -0
- package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +22 -9
- package/dist/types/tools/image-gen.d.ts +63 -1
- package/dist/types/tools/index.d.ts +7 -1
- package/dist/types/tools/output-meta.d.ts +21 -1
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -1
- package/dist/types/tools/resource-gc.d.ts +23 -0
- package/dist/types/tools/skill-discovery.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +7 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/dist/types/utils/shell-snapshot.d.ts +12 -0
- package/dist/types/web/insane/bridge.d.ts +1 -0
- package/dist/types/web/insane/url-guard.d.ts +17 -0
- package/package.json +8 -12
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/compile-args.ts +0 -1
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-docs-index.ts +4 -1
- package/scripts/generate-sdk-operation-inventory.ts +9 -0
- package/scripts/run-test-manifest.ts +6 -4
- package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/capability/index.ts +58 -42
- package/src/capability/types.ts +3 -0
- package/src/cli/args.ts +16 -100
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/mcp-cli.ts +4 -21
- package/src/cli/notify-cli.ts +6 -38
- package/src/cli/read-cli.ts +2 -1
- package/src/cli/setup-cli.ts +10 -4
- package/src/cli.ts +133 -11
- package/src/commands/daemon.ts +22 -5
- package/src/commands/deep-interview.ts +26 -1
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/sdk.ts +134 -17
- package/src/commands/team.ts +1 -1
- package/src/commit/agentic/index.ts +3 -3
- package/src/commit/map-reduce/index.ts +2 -2
- package/src/config/keybindings.ts +158 -25
- package/src/config/model-profile-contract.ts +174 -0
- package/src/config/model-profiles.ts +51 -10
- package/src/config/model-registry.ts +24 -4
- package/src/config/model-resolver.ts +2 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +157 -10
- package/src/config/settings.ts +254 -108
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
- package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
- package/src/defaults/skc/skills/team/SKILL.md +1 -1
- package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
- package/src/discovery/ssh.ts +12 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/js/tool-bridge.ts +1 -1
- package/src/eval/py/executor.ts +37 -13
- package/src/eval/py/kernel.ts +1 -21
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/runner-artifact.ts +84 -0
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +42 -4
- package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
- package/src/extensibility/shared-events.ts +5 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/internal-urls/docs-index.generated.ts +17 -16
- package/src/internal-urls/local-protocol.ts +87 -25
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +120 -42
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +52 -0
- package/src/modes/acp/acp-agent.ts +647 -83
- package/src/modes/acp/acp-event-mapper.ts +106 -12
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/action-registry.ts +1 -1
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/btw-panel.ts +82 -16
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +1 -1
- package/src/modes/components/oauth-selector.ts +42 -12
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/queue-pane.ts +55 -9
- package/src/modes/components/queued-message-selector.ts +53 -10
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/sayknow-pet-widget.ts +27 -8
- package/src/modes/components/session-observer-overlay.ts +187 -32
- package/src/modes/components/settings-selector.ts +60 -18
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +26 -8
- package/src/modes/components/welcome.ts +50 -23
- package/src/modes/controllers/btw-controller.ts +119 -36
- package/src/modes/controllers/command-controller.ts +101 -54
- package/src/modes/controllers/event-controller.ts +119 -27
- package/src/modes/controllers/extension-ui-controller.ts +23 -12
- package/src/modes/controllers/input-controller.ts +100 -8
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +4 -1
- package/src/modes/interactive-mode.ts +201 -60
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/runtime-init.ts +20 -2
- package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +6 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +99 -4
- package/src/prompts/agent-fragments/restricted-bash.md +1 -1
- package/src/prompts/agents/architect.md +17 -3
- package/src/prompts/agents/critic.md +17 -2
- package/src/prompts/agents/executor.md +1 -1
- package/src/prompts/agents/planner.md +8 -1
- package/src/prompts/system/btw-user.md +3 -8
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -3
- package/src/prompts/tools/cron.md +3 -1
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/skill-discovery.md +1 -0
- package/src/runtime/memory-domain.ts +66 -0
- package/src/runtime/memory-guard-contract.ts +73 -0
- package/src/runtime/memory-guard.ts +292 -0
- package/src/runtime/memory-limit.ts +50 -0
- package/src/runtime-mcp/client.ts +41 -55
- package/src/runtime-mcp/content-limits.ts +71 -0
- package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
- package/src/runtime-mcp/json-rpc.ts +28 -11
- package/src/runtime-mcp/manager.ts +77 -59
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/redaction.ts +30 -0
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/runtime-mcp/transports/http.ts +63 -22
- package/src/sdk/acp/adapter.ts +8 -1
- package/src/sdk/acp/final-text.ts +40 -0
- package/src/sdk/acp/mcp.ts +26 -0
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/identity.ts +22 -12
- package/src/sdk/broker/lifecycle.ts +293 -43
- package/src/sdk/broker/session-index.ts +11 -1
- package/src/sdk/bus/chat-daemon-cli.ts +2 -2
- package/src/sdk/bus/chat-daemon-control.ts +22 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +10 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/daemon-paths.ts +2 -0
- package/src/sdk/bus/index.ts +793 -121
- package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
- package/src/sdk/bus/lifecycle-commands.ts +63 -10
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +87 -25
- package/src/sdk/bus/operator-runtime.ts +31 -5
- package/src/sdk/bus/prompt-reconciliation.ts +240 -0
- package/src/sdk/bus/rate-limit-pool.ts +8 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/reconciliation-store.ts +224 -0
- package/src/sdk/bus/telegram-cli.ts +6 -13
- package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
- package/src/sdk/bus/telegram-daemon-control.ts +161 -40
- package/src/sdk/bus/telegram-daemon.ts +3649 -593
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/bus/topic-registry.ts +451 -12
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/client/discovery.ts +19 -3
- package/src/sdk/client/liveness.ts +49 -0
- package/src/sdk/host/control/dispatch.ts +8 -2
- package/src/sdk/host/control/operations.ts +3 -2
- package/src/sdk/host/host.ts +27 -9
- package/src/sdk/host/query/handlers.ts +125 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/lifecycle-session.ts +19 -3
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +145 -0
- package/src/sdk/protocol/operation-registry.ts +24 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +69 -8
- package/src/sdk/startup-capability.ts +15 -1
- package/src/sdk/transport/auth-preface.ts +82 -0
- package/src/sdk/transport/index.ts +18 -0
- package/src/sdk/transport/relay.ts +252 -0
- package/src/sdk/transport/serve-cli.ts +105 -0
- package/src/sdk/transport/socket.ts +197 -0
- package/src/sdk/transport/stdio.ts +17 -0
- package/src/session/agent-session.ts +1112 -314
- package/src/session/blob-store.ts +25 -7
- package/src/session/btw-contract.ts +59 -0
- package/src/session/fallback-chain-controller.ts +47 -8
- package/src/session/internal/managed-session-scope.ts +1426 -259
- package/src/session/internal/managed-session-storage.ts +414 -238
- package/src/session/internal/native-publish-outcome.ts +271 -0
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2079 -469
- package/src/session/session-storage.ts +151 -11
- package/src/session/streaming-output.ts +262 -43
- package/src/setup/credential-import.ts +28 -3
- package/src/setup/provider-onboarding.ts +49 -5
- package/src/setup/provider-presets.json +2 -1
- package/src/skc-runtime/deep-interview-runtime.ts +303 -36
- package/src/skc-runtime/deep-interview-stage.ts +1016 -0
- package/src/skc-runtime/deep-interview-state.ts +8 -5
- package/src/skc-runtime/gc-render.ts +1 -0
- package/src/skc-runtime/gc-runtime.ts +11 -1
- package/src/skc-runtime/linux-proc.ts +70 -36
- package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/skc-runtime/ralplan-runtime.ts +1057 -75
- package/src/skc-runtime/repository-binding.ts +267 -0
- package/src/skc-runtime/session-state-sidecar.ts +25 -9
- package/src/skc-runtime/state-runtime.ts +50 -12
- package/src/skc-runtime/state-writer.ts +68 -56
- package/src/skc-runtime/team-launch.ts +34 -5
- package/src/skc-runtime/team-runtime.ts +41 -1
- package/src/skc-runtime/team-store.ts +32 -0
- package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/skc-runtime/team-workers.ts +1 -0
- package/src/skc-runtime/tmux-common.ts +13 -2
- package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
- package/src/skc-runtime/tmux-sessions.ts +45 -6
- package/src/skc-runtime/ultragoal-guard.ts +93 -15
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
- package/src/skc-runtime/ultragoal-runtime.ts +469 -38
- package/src/skc-runtime/workflow-manifest.generated.json +195 -5
- package/src/skc-runtime/workflow-manifest.ts +67 -3
- package/src/skill-state/workflow-hud.ts +25 -2
- package/src/skill-state/workflow-mutation-guard.ts +104 -12
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/ssh/utils.ts +14 -0
- package/src/task/discovery.ts +8 -2
- package/src/task/executor.ts +165 -47
- package/src/task/index.ts +123 -15
- package/src/task/provider-retry-status.ts +88 -0
- package/src/task/receipt.ts +3 -0
- package/src/task/render.ts +45 -26
- package/src/task/skc-command.ts +2 -2
- package/src/task/types.ts +55 -10
- package/src/task/ultragoal-redteam-activation.ts +75 -0
- package/src/tools/ask.ts +1 -1
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-allowed-prefixes.ts +45 -1
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/fetch.ts +127 -154
- package/src/tools/image-gen.ts +476 -23
- package/src/tools/index.ts +7 -1
- package/src/tools/output-meta.ts +176 -9
- package/src/tools/path-utils.ts +5 -1
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1544 -310
- package/src/tools/resource-gc.ts +665 -125
- package/src/tools/skill-discovery.ts +39 -3
- package/src/tools/sqlite-reader.ts +63 -5
- package/src/tools/subagent-render.ts +74 -16
- package/src/tools/subagent.ts +2 -0
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/pasted-image-path.ts +15 -6
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/shell-snapshot.ts +173 -63
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/bridge.ts +5 -1
- package/src/web/insane/url-guard.ts +140 -27
- package/src/web/scrapers/docs-rs.ts +30 -5
- package/src/web/scrapers/types.ts +19 -49
- package/src/web/scrapers/utils.ts +12 -26
- package/src/web/search/providers/codex.ts +1 -1
- package/src/workflow/workflow-intent-diff.ts +4 -1
package/src/tools/image-gen.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import * as http from "node:http";
|
|
2
|
+
import * as https from "node:https";
|
|
3
|
+
import * as net from "node:net";
|
|
1
4
|
import * as os from "node:os";
|
|
2
5
|
import * as path from "node:path";
|
|
3
6
|
import { getAntigravityUserAgent, getEnvApiKey, type Model } from "@sayknow-cli/ai";
|
|
@@ -23,22 +26,27 @@ import packageJson from "../../package.json" with { type: "json" };
|
|
|
23
26
|
import { isAuthenticated, type ModelRegistry } from "../config/model-registry";
|
|
24
27
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
25
28
|
import imageGenDescription from "../prompts/tools/image-gen.md" with { type: "text" };
|
|
29
|
+
import { isPrivateOrSpecialAddress, validatePublicHttpUrl } from "../web/insane/url-guard";
|
|
26
30
|
import { resolveReadPath } from "./path-utils";
|
|
27
31
|
|
|
28
32
|
const DEFAULT_MODEL = "gemini-3-pro-image-preview";
|
|
29
|
-
const DEFAULT_OPENROUTER_MODEL = "google/gemini-3-pro-image-preview";
|
|
30
|
-
const DEFAULT_ANTIGRAVITY_MODEL = "gemini-3-pro-image";
|
|
31
33
|
const IMAGE_TIMEOUT = 3 * 60 * 1000; // 3 minutes
|
|
32
34
|
const MAX_IMAGE_SIZE = 35 * 1024 * 1024;
|
|
35
|
+
const MAX_IMAGE_REDIRECTS = 5;
|
|
36
|
+
const MAX_IMAGE_HEADER_SIZE = 16 * 1024;
|
|
37
|
+
const MAX_IMAGE_ERROR_PREVIEW_SIZE = 8 * 1024;
|
|
38
|
+
const IMAGE_REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
|
|
33
39
|
const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
34
40
|
const OPENAI_IMAGE_OUTPUT_FORMAT = "webp";
|
|
35
41
|
const OPENAI_IMAGE_MIME_TYPE = "image/webp";
|
|
36
42
|
|
|
37
43
|
const ANTIGRAVITY_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
|
|
44
|
+
const ALIBABA_TOKEN_PLAN_HOST = "https://token-plan.ap-southeast-1.maas.aliyuncs.com";
|
|
45
|
+
const ALIBABA_IMAGE_GENERATION_URL = `${ALIBABA_TOKEN_PLAN_HOST}/api/v1/services/aigc/multimodal-generation/generation`;
|
|
38
46
|
const IMAGE_SYSTEM_INSTRUCTION =
|
|
39
47
|
"You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.";
|
|
40
48
|
|
|
41
|
-
type ImageProvider = "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter";
|
|
49
|
+
type ImageProvider = "alibaba" | "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter";
|
|
42
50
|
interface ImageApiKey {
|
|
43
51
|
provider: ImageProvider;
|
|
44
52
|
apiKey: string;
|
|
@@ -290,6 +298,81 @@ interface AntigravityResponseChunk {
|
|
|
290
298
|
};
|
|
291
299
|
}
|
|
292
300
|
|
|
301
|
+
interface AlibabaImageContentPart {
|
|
302
|
+
type?: string;
|
|
303
|
+
text?: string;
|
|
304
|
+
image?: string;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
interface AlibabaImageResponse {
|
|
308
|
+
code?: string;
|
|
309
|
+
message?: string;
|
|
310
|
+
output?: {
|
|
311
|
+
choices?: Array<{ message?: { content?: AlibabaImageContentPart[] } }>;
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Map the tool's image_size values onto wan2.7's 1K/2K size classes. */
|
|
316
|
+
export function resolveAlibabaImageSize(imageSize: string | undefined): string | undefined {
|
|
317
|
+
switch (imageSize) {
|
|
318
|
+
case "1024x1024":
|
|
319
|
+
return "1K";
|
|
320
|
+
case "1536x1024":
|
|
321
|
+
case "1024x1536":
|
|
322
|
+
return "2K";
|
|
323
|
+
default:
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Build the Bailian multimodal-generation request body for wan2.7 image
|
|
330
|
+
* generation/editing. Input images ride along as data URLs; generation-only
|
|
331
|
+
* calls send just the prompt text. The sync endpoint returns OSS URLs inline.
|
|
332
|
+
*/
|
|
333
|
+
export function buildAlibabaImageRequest(
|
|
334
|
+
model: string,
|
|
335
|
+
promptText: string,
|
|
336
|
+
inputImages: InlineImageData[],
|
|
337
|
+
imageSize: string | undefined,
|
|
338
|
+
): {
|
|
339
|
+
model: string;
|
|
340
|
+
input: { messages: Array<{ role: "user"; content: Array<{ text: string } | { image: string }> }> };
|
|
341
|
+
parameters: { n: number; watermark: boolean; size?: string };
|
|
342
|
+
} {
|
|
343
|
+
const content: Array<{ text: string } | { image: string }> = [];
|
|
344
|
+
for (const image of inputImages) {
|
|
345
|
+
content.push({ image: toDataUrl(image) });
|
|
346
|
+
}
|
|
347
|
+
content.push({ text: promptText });
|
|
348
|
+
const size = resolveAlibabaImageSize(imageSize);
|
|
349
|
+
return {
|
|
350
|
+
model,
|
|
351
|
+
input: { messages: [{ role: "user", content }] },
|
|
352
|
+
parameters: { n: 1, watermark: false, ...(size ? { size } : {}) },
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Collect OSS image URLs and any text parts from a Bailian image response. */
|
|
357
|
+
export function collectAlibabaImageResult(response: AlibabaImageResponse): {
|
|
358
|
+
imageUrls: string[];
|
|
359
|
+
responseText?: string;
|
|
360
|
+
} {
|
|
361
|
+
const imageUrls: string[] = [];
|
|
362
|
+
const textParts: string[] = [];
|
|
363
|
+
for (const choice of response.output?.choices ?? []) {
|
|
364
|
+
for (const part of choice.message?.content ?? []) {
|
|
365
|
+
if (part.image) {
|
|
366
|
+
imageUrls.push(part.image);
|
|
367
|
+
} else if (part.text) {
|
|
368
|
+
textParts.push(part.text);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const responseText = textParts.join("\n").trim();
|
|
373
|
+
return { imageUrls, responseText: responseText.length > 0 ? responseText : undefined };
|
|
374
|
+
}
|
|
375
|
+
|
|
293
376
|
interface ImageGenToolDetails {
|
|
294
377
|
provider: ImageProvider;
|
|
295
378
|
model: string;
|
|
@@ -327,6 +410,160 @@ function toDataUrl(image: InlineImageData): string {
|
|
|
327
410
|
return `data:${image.mimeType};base64,${image.data}`;
|
|
328
411
|
}
|
|
329
412
|
|
|
413
|
+
function withAbortSignal<T>(operation: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
|
|
414
|
+
if (!signal) return operation;
|
|
415
|
+
if (signal.aborted) return Promise.reject(signal.reason);
|
|
416
|
+
const deferred = Promise.withResolvers<T>();
|
|
417
|
+
const abort = () => deferred.reject(signal.reason);
|
|
418
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
419
|
+
operation.then(deferred.resolve, deferred.reject).finally(() => signal.removeEventListener("abort", abort));
|
|
420
|
+
return deferred.promise;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function normalizePeerAddress(address: string): string {
|
|
424
|
+
const normalized = address.toLowerCase();
|
|
425
|
+
return normalized.startsWith("::ffff:") ? normalized.slice(7) : normalized;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function normalizeUrlHostname(hostname: string): string {
|
|
429
|
+
return hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function responseHeaderByteLength(response: http.IncomingMessage): number {
|
|
433
|
+
let bytes = Buffer.byteLength(
|
|
434
|
+
`HTTP/${response.httpVersion} ${response.statusCode ?? ""}${response.statusMessage ? ` ${response.statusMessage}` : ""}\r\n`,
|
|
435
|
+
);
|
|
436
|
+
for (let index = 0; index < response.rawHeaders.length; index += 2) {
|
|
437
|
+
bytes += Buffer.byteLength(`${response.rawHeaders[index] ?? ""}: ${response.rawHeaders[index + 1] ?? ""}\r\n`);
|
|
438
|
+
}
|
|
439
|
+
return bytes + Buffer.byteLength("\r\n");
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function validateImageUrl(rawUrl: string, signal: AbortSignal | undefined) {
|
|
443
|
+
return withAbortSignal(validatePublicHttpUrl(rawUrl), signal);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function openImageResponse(
|
|
447
|
+
url: URL,
|
|
448
|
+
addresses: string[],
|
|
449
|
+
signal: AbortSignal | undefined,
|
|
450
|
+
): Promise<http.IncomingMessage> {
|
|
451
|
+
const hostname = normalizeUrlHostname(url.hostname);
|
|
452
|
+
const approved = addresses.map(address => ({ address, family: net.isIP(address) }));
|
|
453
|
+
const approvedPeers = new Set(approved.map(record => normalizePeerAddress(record.address)));
|
|
454
|
+
const lookup: net.LookupFunction = (requestedHostname, options, callback) => {
|
|
455
|
+
const requestedFamily = options.family === "IPv4" ? 4 : options.family === "IPv6" ? 6 : (options.family ?? 0);
|
|
456
|
+
const matching = approved.filter(record => requestedFamily === 0 || record.family === requestedFamily);
|
|
457
|
+
if (normalizeUrlHostname(requestedHostname) !== hostname || matching.length === 0) {
|
|
458
|
+
const error = Object.assign(new Error("No approved address for image host"), { code: "ENOTFOUND" });
|
|
459
|
+
callback(error, options.all ? [] : "", 0);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (options.all) callback(null, matching);
|
|
463
|
+
else callback(null, matching[0].address, matching[0].family);
|
|
464
|
+
};
|
|
465
|
+
const deferred = Promise.withResolvers<http.IncomingMessage>();
|
|
466
|
+
const options: https.RequestOptions = {
|
|
467
|
+
protocol: url.protocol,
|
|
468
|
+
hostname,
|
|
469
|
+
port: url.port || undefined,
|
|
470
|
+
path: `${url.pathname}${url.search}`,
|
|
471
|
+
method: "GET",
|
|
472
|
+
headers: { Accept: "image/*", "Accept-Encoding": "identity", Connection: "close", Host: url.host },
|
|
473
|
+
agent: false,
|
|
474
|
+
insecureHTTPParser: false,
|
|
475
|
+
lookup,
|
|
476
|
+
maxHeaderSize: MAX_IMAGE_HEADER_SIZE,
|
|
477
|
+
signal,
|
|
478
|
+
...(url.protocol === "https:"
|
|
479
|
+
? { rejectUnauthorized: true, servername: net.isIP(hostname) === 0 ? hostname : undefined }
|
|
480
|
+
: {}),
|
|
481
|
+
};
|
|
482
|
+
const requestFn = url.protocol === "https:" ? https.request : http.request;
|
|
483
|
+
const request = requestFn(options, response => {
|
|
484
|
+
if (responseHeaderByteLength(response) > MAX_IMAGE_HEADER_SIZE) {
|
|
485
|
+
response.destroy();
|
|
486
|
+
deferred.reject(new Error("Image response headers exceed the maximum size of 16 KiB"));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
const peer = response.socket.remoteAddress;
|
|
490
|
+
if (peer && (isPrivateOrSpecialAddress(peer) || !approvedPeers.has(normalizePeerAddress(peer)))) {
|
|
491
|
+
response.destroy();
|
|
492
|
+
deferred.reject(new Error("Refusing image response from an unapproved connected peer"));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
deferred.resolve(response);
|
|
496
|
+
});
|
|
497
|
+
request.once("error", deferred.reject);
|
|
498
|
+
const abort = () => {
|
|
499
|
+
request.destroy(signal?.reason);
|
|
500
|
+
deferred.reject(signal?.reason);
|
|
501
|
+
};
|
|
502
|
+
if (signal?.aborted) abort();
|
|
503
|
+
else signal?.addEventListener("abort", abort, { once: true });
|
|
504
|
+
request.once("close", () => signal?.removeEventListener("abort", abort));
|
|
505
|
+
request.end();
|
|
506
|
+
return deferred.promise;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
function validateImageResponseFraming(response: http.IncomingMessage): void {
|
|
510
|
+
const rawContentLengths: string[] = [];
|
|
511
|
+
let hasTransferEncoding = false;
|
|
512
|
+
for (let index = 0; index < response.rawHeaders.length; index += 2) {
|
|
513
|
+
const name = response.rawHeaders[index]?.toLowerCase();
|
|
514
|
+
if (name === "content-length") rawContentLengths.push(response.rawHeaders[index + 1] ?? "");
|
|
515
|
+
if (name === "transfer-encoding") hasTransferEncoding = true;
|
|
516
|
+
}
|
|
517
|
+
if (rawContentLengths.length > 1 || (rawContentLengths.length > 0 && hasTransferEncoding)) {
|
|
518
|
+
response.destroy();
|
|
519
|
+
throw new Error("Image response has ambiguous framing");
|
|
520
|
+
}
|
|
521
|
+
const contentLength = response.headers["content-length"];
|
|
522
|
+
if (contentLength === undefined) return;
|
|
523
|
+
const declaredBytes = typeof contentLength === "string" && /^\d+$/.test(contentLength) ? Number(contentLength) : NaN;
|
|
524
|
+
if (!Number.isSafeInteger(declaredBytes)) {
|
|
525
|
+
response.destroy();
|
|
526
|
+
throw new Error("Image response has an invalid Content-Length");
|
|
527
|
+
}
|
|
528
|
+
if (declaredBytes > MAX_IMAGE_SIZE) {
|
|
529
|
+
response.destroy();
|
|
530
|
+
throw new Error("Image response exceeds the maximum size of 35 MiB");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async function readImageResponse(
|
|
535
|
+
response: http.IncomingMessage,
|
|
536
|
+
maxBytes: number,
|
|
537
|
+
signal: AbortSignal | undefined,
|
|
538
|
+
): Promise<Buffer> {
|
|
539
|
+
const abort = () => response.destroy(signal?.reason);
|
|
540
|
+
if (signal?.aborted) {
|
|
541
|
+
abort();
|
|
542
|
+
throw signal.reason;
|
|
543
|
+
}
|
|
544
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
545
|
+
const chunks: Buffer[] = [];
|
|
546
|
+
let receivedBytes = 0;
|
|
547
|
+
try {
|
|
548
|
+
for await (const chunk of response) {
|
|
549
|
+
const bytes = Buffer.from(chunk);
|
|
550
|
+
receivedBytes += bytes.byteLength;
|
|
551
|
+
if (receivedBytes > maxBytes) {
|
|
552
|
+
response.destroy();
|
|
553
|
+
throw new Error(
|
|
554
|
+
maxBytes === MAX_IMAGE_SIZE
|
|
555
|
+
? "Image response exceeds the maximum size of 35 MiB"
|
|
556
|
+
: "Image download error response exceeded the preview limit",
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
chunks.push(bytes);
|
|
560
|
+
}
|
|
561
|
+
return Buffer.concat(chunks, receivedBytes);
|
|
562
|
+
} finally {
|
|
563
|
+
signal?.removeEventListener("abort", abort);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
330
567
|
async function loadImageFromUrl(imageUrl: string, signal?: AbortSignal): Promise<InlineImageData> {
|
|
331
568
|
if (imageUrl.startsWith("data:")) {
|
|
332
569
|
const normalized = normalizeDataUrl(imageUrl.trim());
|
|
@@ -339,17 +576,40 @@ async function loadImageFromUrl(imageUrl: string, signal?: AbortSignal): Promise
|
|
|
339
576
|
return { data: normalized.data, mimeType: normalized.mimeType };
|
|
340
577
|
}
|
|
341
578
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
579
|
+
let currentUrl = imageUrl;
|
|
580
|
+
for (let redirectCount = 0; ; redirectCount++) {
|
|
581
|
+
const guard = await validateImageUrl(currentUrl, signal);
|
|
582
|
+
if (!guard.ok) {
|
|
583
|
+
throw new Error(`Refusing image URL: target URL is not public HTTP(S): ${guard.reason}`);
|
|
584
|
+
}
|
|
585
|
+
const response = await openImageResponse(guard.url, guard.addresses, signal);
|
|
586
|
+
const status = response.statusCode ?? 0;
|
|
587
|
+
if (IMAGE_REDIRECT_STATUSES.has(status)) {
|
|
588
|
+
if (redirectCount >= MAX_IMAGE_REDIRECTS) {
|
|
589
|
+
response.destroy();
|
|
590
|
+
throw new Error("Too many redirects downloading image");
|
|
591
|
+
}
|
|
592
|
+
const location = response.headers.location;
|
|
593
|
+
response.destroy();
|
|
594
|
+
if (!location) throw new Error("Image redirect is missing a Location header");
|
|
595
|
+
currentUrl = new URL(location, guard.url).toString();
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
validateImageResponseFraming(response);
|
|
599
|
+
if (status < 200 || status >= 300) {
|
|
600
|
+
const preview = (await readImageResponse(response, MAX_IMAGE_ERROR_PREVIEW_SIZE, signal)).toString("utf8");
|
|
601
|
+
throw new Error(`Image download failed (${status}): ${preview}`);
|
|
602
|
+
}
|
|
603
|
+
const rawContentType = response.headers["content-type"];
|
|
604
|
+
const contentType =
|
|
605
|
+
typeof rawContentType === "string" ? rawContentType.split(";", 1)[0]?.trim().toLowerCase() : undefined;
|
|
606
|
+
if (!contentType?.startsWith("image/")) {
|
|
607
|
+
response.destroy();
|
|
608
|
+
throw new Error("Image response is missing a supported image Content-Type");
|
|
609
|
+
}
|
|
610
|
+
const buffer = await readImageResponse(response, MAX_IMAGE_SIZE, signal);
|
|
611
|
+
return { data: buffer.toBase64(), mimeType: contentType };
|
|
350
612
|
}
|
|
351
|
-
const buffer = await response.bytes();
|
|
352
|
-
return { data: buffer.toBase64(), mimeType: contentType };
|
|
353
613
|
}
|
|
354
614
|
|
|
355
615
|
function collectOpenRouterResponseText(message: OpenRouterMessage | undefined): string | undefined {
|
|
@@ -399,6 +659,50 @@ export function setPreferredImageProvider(provider: ImageProvider | "auto"): voi
|
|
|
399
659
|
preferredImageProvider = provider;
|
|
400
660
|
}
|
|
401
661
|
|
|
662
|
+
/** Provider → default image model mapping for auto-binding */
|
|
663
|
+
export const IMAGE_PROVIDER_DEFAULTS: Record<string, string> = {
|
|
664
|
+
openai: "gpt-image-2",
|
|
665
|
+
alibaba: "wan2.7-image",
|
|
666
|
+
"openai-codex": "gpt-image-2",
|
|
667
|
+
antigravity: "gemini-3-pro-image",
|
|
668
|
+
gemini: "gemini-3-pro-image-preview",
|
|
669
|
+
openrouter: "google/gemini-3-pro-image-preview",
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
/** Resolved image generation configuration from settings */
|
|
673
|
+
export interface ImageProviderConfig {
|
|
674
|
+
provider: ImageProvider | "auto" | "custom";
|
|
675
|
+
model: string | null;
|
|
676
|
+
customUrl?: string;
|
|
677
|
+
customKey?: string;
|
|
678
|
+
customKeyEnv?: string;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/** Module-level configured image model state (set from settings at session init) */
|
|
682
|
+
let configuredImageConfig: ImageProviderConfig | null = null;
|
|
683
|
+
|
|
684
|
+
/** Set the configured image provider + model from settings */
|
|
685
|
+
export function setConfiguredImageModel(config: ImageProviderConfig | null): void {
|
|
686
|
+
configuredImageConfig = config;
|
|
687
|
+
// Keep preferredImageProvider in sync for backward compat
|
|
688
|
+
if (config && config.provider !== "auto" && config.provider !== "custom") {
|
|
689
|
+
preferredImageProvider = config.provider;
|
|
690
|
+
} else if (!config || config.provider === "auto") {
|
|
691
|
+
preferredImageProvider = "auto";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** Get the current configured image model (for UI display) */
|
|
696
|
+
export function getConfiguredImageModel(): ImageProviderConfig | null {
|
|
697
|
+
return configuredImageConfig;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/** Resolve the effective image model for a configured provider */
|
|
701
|
+
export function resolveImageModel(provider: string, modelOverride: string | null): string {
|
|
702
|
+
if (modelOverride) return modelOverride;
|
|
703
|
+
return IMAGE_PROVIDER_DEFAULTS[provider] ?? DEFAULT_MODEL;
|
|
704
|
+
}
|
|
705
|
+
|
|
402
706
|
interface ParsedAntigravityCredentials {
|
|
403
707
|
accessToken: string;
|
|
404
708
|
projectId?: string;
|
|
@@ -420,6 +724,22 @@ function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials
|
|
|
420
724
|
return rawToken.length > 0 ? { accessToken: rawToken } : null;
|
|
421
725
|
}
|
|
422
726
|
|
|
727
|
+
function createCustomImageModel(baseUrl: string, id: string): Model<"openai-responses"> {
|
|
728
|
+
return {
|
|
729
|
+
id,
|
|
730
|
+
name: id,
|
|
731
|
+
api: "openai-responses",
|
|
732
|
+
provider: "openai",
|
|
733
|
+
baseUrl: baseUrl.replace(/\/+$/, ""),
|
|
734
|
+
reasoning: false,
|
|
735
|
+
input: ["text"],
|
|
736
|
+
output: ["text", "image"],
|
|
737
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
738
|
+
contextWindow: 128_000,
|
|
739
|
+
maxTokens: 16_384,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
423
743
|
async function findAntigravityCredentials(
|
|
424
744
|
modelRegistry: ModelRegistry,
|
|
425
745
|
sessionId?: string,
|
|
@@ -462,12 +782,64 @@ async function findOpenAIHostedImageCredentials(
|
|
|
462
782
|
};
|
|
463
783
|
}
|
|
464
784
|
|
|
785
|
+
async function findAlibabaImageCredentials(
|
|
786
|
+
modelRegistry: ModelRegistry | undefined,
|
|
787
|
+
sessionId?: string,
|
|
788
|
+
): Promise<ImageApiKey | null> {
|
|
789
|
+
const envKey = getEnvApiKey("alibaba-token-plan");
|
|
790
|
+
if (envKey) return { provider: "alibaba", apiKey: envKey };
|
|
791
|
+
if (!modelRegistry) return null;
|
|
792
|
+
const apiKey = await modelRegistry.getApiKeyForProvider("alibaba-token-plan", sessionId);
|
|
793
|
+
if (!isAuthenticated(apiKey)) return null;
|
|
794
|
+
return { provider: "alibaba", apiKey };
|
|
795
|
+
}
|
|
796
|
+
|
|
465
797
|
async function findImageApiKey(
|
|
466
798
|
modelRegistry?: ModelRegistry,
|
|
467
799
|
activeModel?: Model,
|
|
468
800
|
sessionId?: string,
|
|
469
801
|
): Promise<ImageApiKey | null> {
|
|
470
|
-
//
|
|
802
|
+
// Config-driven routing: if an explicit provider+model is configured, use it.
|
|
803
|
+
if (configuredImageConfig && configuredImageConfig.provider !== "auto") {
|
|
804
|
+
const config = configuredImageConfig;
|
|
805
|
+
if (config.provider === "custom") {
|
|
806
|
+
const baseUrl = config.customUrl?.trim();
|
|
807
|
+
const apiKey = config.customKey ?? (config.customKeyEnv ? Bun.env[config.customKeyEnv] : undefined);
|
|
808
|
+
if (baseUrl && apiKey) {
|
|
809
|
+
return {
|
|
810
|
+
provider: "openai",
|
|
811
|
+
apiKey,
|
|
812
|
+
model: createCustomImageModel(baseUrl, config.model ?? IMAGE_PROVIDER_DEFAULTS.openai),
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
return null;
|
|
816
|
+
}
|
|
817
|
+
// For configured providers, resolve credentials through the existing paths.
|
|
818
|
+
if (config.provider === "openai" || config.provider === "openai-codex") {
|
|
819
|
+
const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
|
|
820
|
+
if (openAI) return openAI;
|
|
821
|
+
return null;
|
|
822
|
+
}
|
|
823
|
+
if (config.provider === "antigravity") {
|
|
824
|
+
if (!modelRegistry) return null;
|
|
825
|
+
return await findAntigravityCredentials(modelRegistry, sessionId);
|
|
826
|
+
}
|
|
827
|
+
if (config.provider === "gemini") {
|
|
828
|
+
const geminiKey = getEnvApiKey("google");
|
|
829
|
+
if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
|
|
830
|
+
const googleKey = $env.GOOGLE_API_KEY;
|
|
831
|
+
return googleKey ? { provider: "gemini", apiKey: googleKey } : null;
|
|
832
|
+
}
|
|
833
|
+
if (config.provider === "openrouter") {
|
|
834
|
+
const openRouterKey = getEnvApiKey("openrouter");
|
|
835
|
+
return openRouterKey ? { provider: "openrouter", apiKey: openRouterKey } : null;
|
|
836
|
+
}
|
|
837
|
+
if (config.provider === "alibaba") {
|
|
838
|
+
return await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// If a specific provider is preferred (legacy path), try it first.
|
|
471
843
|
if (preferredImageProvider === "openai") {
|
|
472
844
|
const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
|
|
473
845
|
if (openAI) return openAI;
|
|
@@ -483,9 +855,11 @@ async function findImageApiKey(
|
|
|
483
855
|
} else if (preferredImageProvider === "openrouter") {
|
|
484
856
|
const openRouterKey = getEnvApiKey("openrouter");
|
|
485
857
|
return openRouterKey ? { provider: "openrouter", apiKey: openRouterKey } : null;
|
|
858
|
+
} else if (preferredImageProvider === "alibaba") {
|
|
859
|
+
return await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
486
860
|
}
|
|
487
861
|
|
|
488
|
-
// Auto-detect: GPT hosted image generation, then Antigravity, OpenRouter, Gemini.
|
|
862
|
+
// Auto-detect: GPT hosted image generation, then Antigravity, OpenRouter, Gemini, Alibaba.
|
|
489
863
|
const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
|
|
490
864
|
if (openAI) return openAI;
|
|
491
865
|
|
|
@@ -503,6 +877,9 @@ async function findImageApiKey(
|
|
|
503
877
|
const googleKey = $env.GOOGLE_API_KEY;
|
|
504
878
|
if (googleKey) return { provider: "gemini", apiKey: googleKey };
|
|
505
879
|
|
|
880
|
+
const alibaba = await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
881
|
+
if (alibaba) return alibaba;
|
|
882
|
+
|
|
506
883
|
return null;
|
|
507
884
|
}
|
|
508
885
|
|
|
@@ -939,19 +1316,23 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
939
1316
|
const apiKey = await findImageApiKey(ctx.modelRegistry, ctx.model, sessionId);
|
|
940
1317
|
if (!apiKey) {
|
|
941
1318
|
throw new Error(
|
|
942
|
-
"No image API credentials found. Use a GPT Responses/Codex model with OpenAI credentials, login with google-antigravity, or set OPENROUTER_API_KEY, GEMINI_API_KEY, or
|
|
1319
|
+
"No image API credentials found. Use a GPT Responses/Codex model with OpenAI credentials, login with google-antigravity, or set OPENROUTER_API_KEY, GEMINI_API_KEY, GOOGLE_API_KEY, or ALIBABA_TOKEN_PLAN_API_KEY.",
|
|
943
1320
|
);
|
|
944
1321
|
}
|
|
945
1322
|
|
|
946
1323
|
const provider = apiKey.provider;
|
|
947
1324
|
const model =
|
|
948
|
-
provider
|
|
949
|
-
?
|
|
950
|
-
: provider === "
|
|
951
|
-
?
|
|
952
|
-
: provider === "
|
|
953
|
-
?
|
|
954
|
-
:
|
|
1325
|
+
configuredImageConfig && configuredImageConfig.provider !== "auto" && configuredImageConfig.model
|
|
1326
|
+
? configuredImageConfig.model
|
|
1327
|
+
: provider === "openai" || provider === "openai-codex"
|
|
1328
|
+
? (apiKey.model?.id ?? resolveImageModel(provider, null))
|
|
1329
|
+
: provider === "antigravity"
|
|
1330
|
+
? resolveImageModel("antigravity", null)
|
|
1331
|
+
: provider === "alibaba"
|
|
1332
|
+
? resolveImageModel("alibaba", null)
|
|
1333
|
+
: provider === "openrouter"
|
|
1334
|
+
? resolveImageModel("openrouter", null)
|
|
1335
|
+
: resolveImageModel("gemini", null);
|
|
955
1336
|
const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
|
|
956
1337
|
const cwd = ctx.sessionManager.getCwd();
|
|
957
1338
|
|
|
@@ -1169,6 +1550,78 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1169
1550
|
};
|
|
1170
1551
|
}
|
|
1171
1552
|
|
|
1553
|
+
if (provider === "alibaba") {
|
|
1554
|
+
const requestBody = buildAlibabaImageRequest(
|
|
1555
|
+
model,
|
|
1556
|
+
assemblePrompt(params),
|
|
1557
|
+
resolvedImages,
|
|
1558
|
+
params.image_size,
|
|
1559
|
+
);
|
|
1560
|
+
|
|
1561
|
+
const response = await fetch(ALIBABA_IMAGE_GENERATION_URL, {
|
|
1562
|
+
method: "POST",
|
|
1563
|
+
headers: {
|
|
1564
|
+
"Content-Type": "application/json",
|
|
1565
|
+
Authorization: `Bearer ${apiKey.apiKey}`,
|
|
1566
|
+
},
|
|
1567
|
+
body: JSON.stringify(requestBody),
|
|
1568
|
+
signal: requestSignal,
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
const rawText = await response.text();
|
|
1572
|
+
if (!response.ok) {
|
|
1573
|
+
let message = rawText;
|
|
1574
|
+
try {
|
|
1575
|
+
const parsed = JSON.parse(rawText) as { message?: string; error?: { message?: string } };
|
|
1576
|
+
message = parsed.error?.message ?? parsed.message ?? message;
|
|
1577
|
+
} catch {
|
|
1578
|
+
// Keep raw text.
|
|
1579
|
+
}
|
|
1580
|
+
throw new Error(`Alibaba image request failed (${response.status}): ${message}`);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
const data = JSON.parse(rawText) as AlibabaImageResponse;
|
|
1584
|
+
if (data.code) {
|
|
1585
|
+
throw new Error(`Alibaba image request failed: ${data.code}: ${data.message ?? ""}`.trim());
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
const { imageUrls, responseText } = collectAlibabaImageResult(data);
|
|
1589
|
+
// Result URLs are short-lived OSS-signed URLs (24h); download immediately.
|
|
1590
|
+
const inlineImages: InlineImageData[] = [];
|
|
1591
|
+
for (const imageUrl of imageUrls) {
|
|
1592
|
+
inlineImages.push(await loadImageFromUrl(imageUrl, requestSignal));
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
if (inlineImages.length === 0) {
|
|
1596
|
+
const messageText = responseText ? `\n\n${responseText}` : "";
|
|
1597
|
+
return {
|
|
1598
|
+
content: [{ type: "text", text: `No image data returned.${messageText}` }],
|
|
1599
|
+
details: {
|
|
1600
|
+
provider,
|
|
1601
|
+
model,
|
|
1602
|
+
imageCount: 0,
|
|
1603
|
+
imagePaths: [],
|
|
1604
|
+
images: [],
|
|
1605
|
+
responseText,
|
|
1606
|
+
},
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
const imagePaths = await saveImagesToTemp(inlineImages);
|
|
1611
|
+
|
|
1612
|
+
return {
|
|
1613
|
+
content: [{ type: "text", text: buildResponseSummary(provider, model, imagePaths, responseText) }],
|
|
1614
|
+
details: {
|
|
1615
|
+
provider,
|
|
1616
|
+
model,
|
|
1617
|
+
imageCount: inlineImages.length,
|
|
1618
|
+
imagePaths,
|
|
1619
|
+
images: inlineImages,
|
|
1620
|
+
responseText,
|
|
1621
|
+
},
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1172
1625
|
const parts = [] as Array<{ text?: string; inlineData?: InlineImageData }>;
|
|
1173
1626
|
for (const image of resolvedImages) {
|
|
1174
1627
|
parts.push({ inlineData: image });
|
package/src/tools/index.ts
CHANGED
|
@@ -137,6 +137,8 @@ export interface AskAnswerRequest {
|
|
|
137
137
|
options: string[];
|
|
138
138
|
interaction: "selector" | "custom_editor" | "clarification_editor";
|
|
139
139
|
controls: readonly AskRemoteControl[];
|
|
140
|
+
/** Optional zero-based recommendation into the authoritative raw options. */
|
|
141
|
+
recommendedIndex?: number;
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
export type AskRemoteInteraction =
|
|
@@ -232,6 +234,8 @@ export interface ToolSession {
|
|
|
232
234
|
* its terminal-phase chain guard. Returns the raw phase string or undefined
|
|
233
235
|
* when no active skill (or accessor) is available. */
|
|
234
236
|
getActiveSkillPhase?: () => string | undefined;
|
|
237
|
+
/** Restrict provider-facing deep-interview ask metadata to the active workflow stage. */
|
|
238
|
+
getDeepInterviewAskStage?: () => "topology" | "post-topology" | undefined;
|
|
235
239
|
/** Pre-loaded prompt templates */
|
|
236
240
|
promptTemplates?: PromptTemplate[];
|
|
237
241
|
/** Whether LSP integrations are enabled */
|
|
@@ -271,9 +275,11 @@ export interface ToolSession {
|
|
|
271
275
|
/** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
|
|
272
276
|
getAgentId?: () => string | null;
|
|
273
277
|
/** Look up a registered tool by name (used by the eval js backend's tool bridge). */
|
|
278
|
+
getToolByName?: (name: string) => AgentTool | undefined;
|
|
279
|
+
/** Look up a registered tool with the session's execution guards applied. */
|
|
280
|
+
getToolForExecution?: (name: string) => AgentTool | undefined;
|
|
274
281
|
/** Purge undelivered queued custom messages matching the predicate. Returns counts. */
|
|
275
282
|
purgeQueuedCustomMessages?: (predicate: (message: CustomMessage) => boolean) => PurgeQueuedCustomMessagesResult;
|
|
276
|
-
getToolByName?: (name: string) => AgentTool | undefined;
|
|
277
283
|
/** Agent registry for IRC routing across live sessions. */
|
|
278
284
|
agentRegistry?: AgentRegistry;
|
|
279
285
|
/** Optional restricted bash command prefixes for read-only role agents and constrained modes. */
|