@gajae-code/coding-agent 0.12.21 → 0.13.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 +67 -2
- package/dist/types/cli/args.d.ts +5 -2
- package/dist/types/cli/root-flags.d.ts +246 -0
- package/dist/types/cli.d.ts +11 -19
- package/dist/types/commands/daemon.d.ts +0 -30
- package/dist/types/commands/launch.d.ts +11 -19
- package/dist/types/commands/quick-lane.d.ts +32 -0
- package/dist/types/commands/sdk.d.ts +21 -15
- package/dist/types/config/file-lock-gc.d.ts +2 -0
- package/dist/types/config/model-equivalence.d.ts +20 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -17
- package/dist/types/config/model-profiles.d.ts +10 -0
- package/dist/types/config/model-registry.d.ts +68 -1
- package/dist/types/config/model-resolver.d.ts +15 -4
- package/dist/types/config/models-config-schema.d.ts +7 -2
- package/dist/types/config/provider-selection-policy.d.ts +62 -0
- package/dist/types/config/settings-schema.d.ts +36 -78
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/edit/renderer.d.ts +5 -2
- package/dist/types/edit/streaming.d.ts +13 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/compact-handler.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +19 -5
- package/dist/types/internal-urls/local-protocol.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +0 -2
- package/dist/types/modes/components/provider-order-context.d.ts +45 -0
- package/dist/types/modes/components/provider-order-editor.d.ts +24 -0
- package/dist/types/modes/components/settings-defs.d.ts +5 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/quick-lane/classify.d.ts +40 -0
- package/dist/types/runtime/stt-settings-setup.d.ts +18 -0
- package/dist/types/sdk/broker/broker.d.ts +56 -2
- package/dist/types/sdk/broker/operation-receipt-ledger.d.ts +25 -0
- package/dist/types/sdk/broker/session-index.d.ts +83 -2
- package/dist/types/sdk/broker/state-version.d.ts +1 -1
- package/dist/types/sdk/bus/notification-service.d.ts +6 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +4 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +26 -0
- package/dist/types/sdk/cli/rows.d.ts +98 -0
- package/dist/types/sdk/cli/session-cli.d.ts +35 -3
- package/dist/types/sdk/elevation/allowlist.d.ts +20 -0
- package/dist/types/sdk/elevation/audit.d.ts +93 -0
- package/dist/types/sdk/elevation/capability.d.ts +3 -0
- package/dist/types/sdk/elevation/digest.d.ts +72 -0
- package/dist/types/sdk/elevation/dispatch-receipt.d.ts +85 -0
- package/dist/types/sdk/elevation/grant-ledger.d.ts +163 -0
- package/dist/types/sdk/elevation/owner.d.ts +48 -0
- package/dist/types/sdk/guides/cache.d.ts +98 -0
- package/dist/types/sdk/guides/catalog.d.ts +156 -0
- package/dist/types/sdk/guides/cli.d.ts +39 -0
- package/dist/types/sdk/guides/manifest.d.ts +66 -0
- package/dist/types/sdk/guides/verify.d.ts +66 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +8 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +3 -2
- package/dist/types/sdk/host/query/cursor.d.ts +10 -0
- package/dist/types/sdk/host/query/handlers.d.ts +21 -0
- package/dist/types/sdk/host/session-runtime.d.ts +9 -0
- package/dist/types/sdk/mcp/server.d.ts +2 -2
- package/dist/types/sdk/prompt-status.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +4 -0
- package/dist/types/sdk/transport/index.d.ts +1 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +20 -1
- package/dist/types/session/agent-session.d.ts +32 -18
- package/dist/types/session/blob-store.d.ts +13 -7
- package/dist/types/session/fallback-chain-controller.d.ts +15 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +25 -1
- package/dist/types/session/internal/session-memory-sidecar.d.ts +5 -5
- package/dist/types/session/session-manager.d.ts +139 -10
- package/dist/types/session/session-storage.d.ts +48 -0
- package/dist/types/setup/provider-onboarding.d.ts +7 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +14 -0
- package/dist/types/stt/setup.d.ts +2 -0
- package/dist/types/task/executor.d.ts +10 -2
- package/dist/types/tools/browser/runtime-diagnostics.d.ts +65 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/browser/tab-worker.d.ts +2 -0
- package/dist/types/tools/browser.d.ts +2 -0
- package/dist/types/tools/image-gen.d.ts +14 -18
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/resource-gc.d.ts +2 -0
- package/dist/types/utils/pasted-image-path.d.ts +6 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +8 -0
- package/scripts/run-test-manifest.ts +61 -21
- package/scripts/verify-gjc-sdk-canonicalization.ts +9 -4
- package/src/cli/args.ts +62 -9
- package/src/cli/completion-cli.ts +1 -0
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/root-flags.ts +100 -0
- package/src/cli/setup-cli.ts +6 -2
- package/src/cli.ts +42 -65
- package/src/commands/acp.ts +1 -1
- package/src/commands/daemon.ts +2 -31
- package/src/commands/gc.ts +1 -1
- package/src/commands/launch.ts +8 -129
- package/src/commands/quick-lane.ts +51 -0
- package/src/commands/sdk.ts +200 -26
- package/src/commands/setup.ts +4 -2
- package/src/config/file-lock-gc.ts +4 -1
- package/src/config/model-equivalence.ts +76 -7
- package/src/config/model-profile-activation.ts +1115 -265
- package/src/config/model-profiles.ts +134 -2
- package/src/config/model-registry.ts +352 -50
- package/src/config/model-resolver.ts +289 -38
- package/src/config/models-config-schema.ts +11 -4
- package/src/config/provider-selection-policy.ts +146 -0
- package/src/config/settings-schema.ts +50 -77
- package/src/config/settings.ts +40 -0
- package/src/coordinator-mcp/server.ts +32 -2
- package/src/edit/renderer.ts +175 -223
- package/src/edit/streaming.ts +108 -1
- package/src/export/html/index.ts +57 -7
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/extensions/compact-handler.ts +3 -2
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +3 -0
- package/src/gjc-runtime/gc-runtime.ts +379 -20
- package/src/gjc-runtime/launch-worktree.ts +5 -1
- package/src/gjc-runtime/team-runtime.ts +59 -2
- package/src/gjc-runtime/tmux-owner-isolation.ts +19 -0
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +17 -13
- package/src/internal-urls/local-protocol.ts +1 -0
- package/src/main.ts +40 -3
- package/src/memories/index.ts +4 -2
- package/src/modes/acp/acp-agent.ts +235 -91
- package/src/modes/components/model-selector.ts +206 -42
- package/src/modes/components/provider-order-context.ts +197 -0
- package/src/modes/components/provider-order-editor.ts +441 -0
- package/src/modes/components/settings-defs.ts +21 -1
- package/src/modes/components/settings-selector.ts +45 -2
- package/src/modes/components/tool-execution.ts +47 -15
- package/src/modes/controllers/command-controller.ts +6 -3
- package/src/modes/controllers/extension-ui-controller.ts +2 -0
- package/src/modes/controllers/input-controller.ts +73 -2
- package/src/modes/controllers/selector-controller.ts +260 -153
- package/src/modes/prompt-suggestion-controller.ts +1 -1
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +18 -0
- package/src/prompts/tools/browser.md +1 -0
- package/src/quick-lane/classify.ts +257 -0
- package/src/rlm/index.ts +1 -1
- package/src/runtime/stt-settings-setup.ts +57 -0
- package/src/sdk/acp/adapter.ts +2 -1
- package/src/sdk/broker/broker.ts +780 -12
- package/src/sdk/broker/lifecycle.ts +70 -29
- package/src/sdk/broker/operation-receipt-ledger.ts +144 -0
- package/src/sdk/broker/session-index.ts +463 -48
- package/src/sdk/broker/state-version.ts +13 -9
- package/src/sdk/broker/transport.ts +14 -1
- package/src/sdk/bus/index.ts +34 -13
- package/src/sdk/bus/kind-aware-reconciliation.ts +21 -2
- package/src/sdk/bus/notification-service.ts +47 -1
- package/src/sdk/bus/prompt-reconciliation.ts +22 -3
- package/src/sdk/bus/telegram-daemon-contract.ts +4 -1
- package/src/sdk/bus/telegram-daemon.ts +54 -3
- package/src/sdk/cli/rows.ts +247 -0
- package/src/sdk/cli/session-cli.ts +930 -79
- package/src/sdk/elevation/allowlist.ts +54 -0
- package/src/sdk/elevation/audit.ts +208 -0
- package/src/sdk/elevation/capability.ts +41 -0
- package/src/sdk/elevation/digest.ts +129 -0
- package/src/sdk/elevation/dispatch-receipt.ts +184 -0
- package/src/sdk/elevation/grant-ledger.ts +1142 -0
- package/src/sdk/elevation/owner.ts +109 -0
- package/src/sdk/guides/cache.ts +363 -0
- package/src/sdk/guides/catalog.ts +554 -0
- package/src/sdk/guides/cli.ts +235 -0
- package/src/sdk/guides/manifest.ts +152 -0
- package/src/sdk/guides/verify.ts +178 -0
- package/src/sdk/host/control/dispatch.ts +49 -1
- package/src/sdk/host/control/index.ts +1 -0
- package/src/sdk/host/control/operations.ts +8 -1
- package/src/sdk/host/query/cursor.ts +47 -3
- package/src/sdk/host/query/handlers.ts +155 -2
- package/src/sdk/host/sdk-surface-parity.test.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +391 -1
- package/src/sdk/host/session-runtime.ts +308 -73
- package/src/sdk/host/surface-policy.ts +3 -0
- package/src/sdk/host/websocket-transport.ts +7 -1
- package/src/sdk/mcp/server.ts +88 -15
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/protocol/operation-inventory.generated.json +62 -0
- package/src/sdk/protocol/operation-registry.ts +3 -2
- package/src/sdk/session.ts +87 -39
- package/src/sdk/transport/index.ts +1 -0
- package/src/sdk/transport/relay.ts +3 -0
- package/src/sdk/transport/serve-cli.ts +111 -24
- package/src/secrets/obfuscator.ts +92 -12
- package/src/session/agent-session.ts +322 -93
- package/src/session/blob-store.ts +60 -8
- package/src/session/fallback-chain-controller.ts +35 -0
- package/src/session/internal/managed-session-scope.ts +25 -3
- package/src/session/internal/managed-session-storage.ts +574 -73
- package/src/session/internal/session-memory-sidecar.ts +19 -29
- package/src/session/session-manager.ts +2166 -743
- package/src/session/session-storage.ts +715 -48
- package/src/setup/provider-onboarding.ts +19 -3
- package/src/setup/provider-presets.json +3 -1
- package/src/slash-commands/builtin-registry.ts +85 -3
- package/src/stt/downloader.ts +2 -3
- package/src/stt/setup.ts +29 -4
- package/src/task/executor.ts +22 -18
- package/src/task/index.ts +24 -1
- package/src/tools/archive-reader.ts +11 -2
- package/src/tools/browser/runtime-diagnostics.ts +249 -0
- package/src/tools/browser/tab-protocol.ts +4 -0
- package/src/tools/browser/tab-supervisor.ts +5 -0
- package/src/tools/browser/tab-worker.ts +36 -1
- package/src/tools/browser.ts +7 -0
- package/src/tools/image-gen.ts +110 -166
- package/src/tools/index.ts +2 -0
- package/src/tools/read.ts +1 -1
- package/src/tools/resource-gc.ts +6 -1
- package/src/tools/tool-catalog.generated.ts +5 -1
- package/src/utils/pasted-image-path.ts +93 -0
- package/src/web/search/index.ts +3 -3
package/src/tools/image-gen.ts
CHANGED
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
import * as z from "zod/v4";
|
|
25
25
|
import packageJson from "../../package.json" with { type: "json" };
|
|
26
26
|
import { isAuthenticated, type ModelRegistry } from "../config/model-registry";
|
|
27
|
+
import { resolveModelRoleValue } from "../config/model-resolver";
|
|
28
|
+
import type { Settings } from "../config/settings";
|
|
27
29
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
28
30
|
import imageGenDescription from "../prompts/tools/image-gen.md" with { type: "text" };
|
|
29
31
|
import { isPrivateOrSpecialAddress, validatePublicHttpUrl } from "../web/insane/url-guard";
|
|
@@ -46,7 +48,7 @@ const ALIBABA_IMAGE_GENERATION_URL = `${ALIBABA_TOKEN_PLAN_HOST}/api/v1/services
|
|
|
46
48
|
const IMAGE_SYSTEM_INSTRUCTION =
|
|
47
49
|
"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.";
|
|
48
50
|
|
|
49
|
-
type ImageProvider = "alibaba" | "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter";
|
|
51
|
+
type ImageProvider = "alibaba" | "antigravity" | "gemini" | "openai" | "openai-codex" | "openrouter" | (string & {});
|
|
50
52
|
interface ImageApiKey {
|
|
51
53
|
provider: ImageProvider;
|
|
52
54
|
apiKey: string;
|
|
@@ -651,15 +653,7 @@ function extractOpenRouterImageUrls(message: OpenRouterMessage | undefined): str
|
|
|
651
653
|
return urls;
|
|
652
654
|
}
|
|
653
655
|
|
|
654
|
-
/**
|
|
655
|
-
let preferredImageProvider: ImageProvider | "auto" = "auto";
|
|
656
|
-
|
|
657
|
-
/** Set the preferred image provider from settings */
|
|
658
|
-
export function setPreferredImageProvider(provider: ImageProvider | "auto"): void {
|
|
659
|
-
preferredImageProvider = provider;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** Provider → default image model mapping for auto-binding */
|
|
656
|
+
/** Provider → default image model mapping for fallbacks */
|
|
663
657
|
export const IMAGE_PROVIDER_DEFAULTS: Record<string, string> = {
|
|
664
658
|
openai: "gpt-image-2",
|
|
665
659
|
alibaba: "wan2.7-image",
|
|
@@ -669,32 +663,26 @@ export const IMAGE_PROVIDER_DEFAULTS: Record<string, string> = {
|
|
|
669
663
|
openrouter: "google/gemini-3-pro-image-preview",
|
|
670
664
|
};
|
|
671
665
|
|
|
672
|
-
/**
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
/** Get the current configured image model (for UI display) */
|
|
696
|
-
export function getConfiguredImageModel(): ImageProviderConfig | null {
|
|
697
|
-
return configuredImageConfig;
|
|
666
|
+
/**
|
|
667
|
+
* Resolve the image-generation model from the `modelRoles.image` settings entry.
|
|
668
|
+
* Returns the resolved Model (with provider identity) or undefined when no
|
|
669
|
+
* image role is configured / no matching model is available.
|
|
670
|
+
*/
|
|
671
|
+
export function resolveImageRoleModel(
|
|
672
|
+
settings: Settings,
|
|
673
|
+
modelRegistry: ModelRegistry,
|
|
674
|
+
options?: { sessionId?: string; credentialSessionId?: string },
|
|
675
|
+
): Model | undefined {
|
|
676
|
+
const roleValue = settings.getModelRole("image");
|
|
677
|
+
if (!roleValue) return undefined;
|
|
678
|
+
const availableModels = modelRegistry.getAvailable();
|
|
679
|
+
const resolved = resolveModelRoleValue(roleValue, availableModels, {
|
|
680
|
+
settings,
|
|
681
|
+
modelRegistry,
|
|
682
|
+
sessionId: options?.sessionId,
|
|
683
|
+
credentialSessionId: options?.credentialSessionId,
|
|
684
|
+
});
|
|
685
|
+
return resolved.model;
|
|
698
686
|
}
|
|
699
687
|
|
|
700
688
|
/** Resolve the effective image model for a configured provider */
|
|
@@ -724,22 +712,6 @@ function parseAntigravityCredentials(raw: string): ParsedAntigravityCredentials
|
|
|
724
712
|
return rawToken.length > 0 ? { accessToken: rawToken } : null;
|
|
725
713
|
}
|
|
726
714
|
|
|
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
|
-
|
|
743
715
|
async function findAntigravityCredentials(
|
|
744
716
|
modelRegistry: ModelRegistry,
|
|
745
717
|
sessionId?: string,
|
|
@@ -766,22 +738,6 @@ async function findAntigravityCredentials(
|
|
|
766
738
|
};
|
|
767
739
|
}
|
|
768
740
|
|
|
769
|
-
async function findOpenAIHostedImageCredentials(
|
|
770
|
-
modelRegistry: ModelRegistry | undefined,
|
|
771
|
-
activeModel: Model | undefined,
|
|
772
|
-
sessionId?: string,
|
|
773
|
-
): Promise<ImageApiKey | null> {
|
|
774
|
-
if (!modelRegistry || !isOpenAIHostedImageModel(activeModel)) return null;
|
|
775
|
-
const apiKey = await modelRegistry.getApiKey(activeModel, sessionId);
|
|
776
|
-
if (!isAuthenticated(apiKey)) return null;
|
|
777
|
-
return {
|
|
778
|
-
provider: getOpenAIHostedImageProvider(activeModel),
|
|
779
|
-
apiKey,
|
|
780
|
-
model: activeModel,
|
|
781
|
-
authCredentialType: modelRegistry.getSessionCredentialType?.(activeModel.provider, sessionId),
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
|
|
785
741
|
async function findAlibabaImageCredentials(
|
|
786
742
|
modelRegistry: ModelRegistry | undefined,
|
|
787
743
|
sessionId?: string,
|
|
@@ -811,93 +767,77 @@ export function googleImageApiKeyFromEnvForTest(): string | undefined {
|
|
|
811
767
|
return googleImageApiKeyFromEnv();
|
|
812
768
|
}
|
|
813
769
|
|
|
770
|
+
/**
|
|
771
|
+
* Resolve image API credentials from the `modelRoles.image` model.
|
|
772
|
+
*
|
|
773
|
+
* The resolved model's provider identity determines which credential path
|
|
774
|
+
* is used. Returns null when no image role model is configured or the
|
|
775
|
+
* corresponding credentials are unavailable.
|
|
776
|
+
*/
|
|
814
777
|
async function findImageApiKey(
|
|
815
|
-
modelRegistry
|
|
816
|
-
|
|
778
|
+
modelRegistry: ModelRegistry,
|
|
779
|
+
settings: Settings,
|
|
817
780
|
sessionId?: string,
|
|
781
|
+
credentialSessionId?: string,
|
|
818
782
|
): Promise<ImageApiKey | null> {
|
|
819
|
-
|
|
820
|
-
if (
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
|
|
847
|
-
const googleKey = googleImageApiKeyFromEnv();
|
|
848
|
-
return googleKey ? { provider: "gemini", apiKey: googleKey } : null;
|
|
849
|
-
}
|
|
850
|
-
if (config.provider === "openrouter") {
|
|
851
|
-
const openRouterKey = getEnvApiKey("openrouter");
|
|
852
|
-
return openRouterKey ? { provider: "openrouter", apiKey: openRouterKey } : null;
|
|
853
|
-
}
|
|
854
|
-
if (config.provider === "alibaba") {
|
|
855
|
-
return await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
856
|
-
}
|
|
783
|
+
const imageModel = resolveImageRoleModel(settings, modelRegistry, { sessionId, credentialSessionId });
|
|
784
|
+
if (!imageModel) return null;
|
|
785
|
+
return resolveCredentialsForImageModel(imageModel, modelRegistry, sessionId);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Map a resolved image model to its provider credentials. Each provider
|
|
790
|
+
* branch mirrors the original credential-resolution path.
|
|
791
|
+
*/
|
|
792
|
+
async function resolveCredentialsForImageModel(
|
|
793
|
+
model: Model,
|
|
794
|
+
modelRegistry: ModelRegistry,
|
|
795
|
+
sessionId?: string,
|
|
796
|
+
): Promise<ImageApiKey | null> {
|
|
797
|
+
const provider: string = model.provider;
|
|
798
|
+
|
|
799
|
+
// OpenAI-hosted image generation (gpt-image via Responses API, including
|
|
800
|
+
// OpenAI-compatible proxies whose models declare output:image).
|
|
801
|
+
if (isOpenAIHostedImageModel(model)) {
|
|
802
|
+
const apiKey = await modelRegistry.getApiKey(model, sessionId);
|
|
803
|
+
if (!isAuthenticated(apiKey)) return null;
|
|
804
|
+
return {
|
|
805
|
+
provider: getOpenAIHostedImageProvider(model),
|
|
806
|
+
apiKey,
|
|
807
|
+
model,
|
|
808
|
+
authCredentialType: modelRegistry.getSessionCredentialType?.(model.provider, sessionId),
|
|
809
|
+
};
|
|
857
810
|
}
|
|
858
811
|
|
|
859
|
-
|
|
860
|
-
if (preferredImageProvider === "openai") {
|
|
861
|
-
const openAI = await findOpenAIHostedImageCredentials(modelRegistry, activeModel, sessionId);
|
|
862
|
-
if (openAI) return openAI;
|
|
863
|
-
// Fall through to auto-detect if preferred provider key not found.
|
|
864
|
-
} else if (preferredImageProvider === "antigravity") {
|
|
865
|
-
if (!modelRegistry) return null;
|
|
812
|
+
if (provider === "antigravity" || provider === "google-antigravity") {
|
|
866
813
|
return await findAntigravityCredentials(modelRegistry, sessionId);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
if (geminiKey) return { provider: "gemini", apiKey: geminiKey };
|
|
870
|
-
const googleKey = googleImageApiKeyFromEnv();
|
|
871
|
-
return googleKey ? { provider: "gemini", apiKey: googleKey } : null;
|
|
872
|
-
} else if (preferredImageProvider === "openrouter") {
|
|
873
|
-
const openRouterKey = getEnvApiKey("openrouter");
|
|
874
|
-
return openRouterKey ? { provider: "openrouter", apiKey: openRouterKey } : null;
|
|
875
|
-
} else if (preferredImageProvider === "alibaba") {
|
|
814
|
+
}
|
|
815
|
+
if (provider === "alibaba" || provider === "alibaba-token-plan") {
|
|
876
816
|
return await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
877
817
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
818
|
+
if (provider === "openrouter") {
|
|
819
|
+
const openRouterKey = await modelRegistry.getApiKey(model, sessionId);
|
|
820
|
+
if (isAuthenticated(openRouterKey)) return { provider: "openrouter", apiKey: openRouterKey };
|
|
821
|
+
const envKey = getEnvApiKey("openrouter");
|
|
822
|
+
return envKey ? { provider: "openrouter", apiKey: envKey } : null;
|
|
823
|
+
}
|
|
824
|
+
if (provider === "gemini" || provider === "google") {
|
|
825
|
+
const googleKey = await modelRegistry.getApiKey(model, sessionId);
|
|
826
|
+
if (isAuthenticated(googleKey)) return { provider: "gemini", apiKey: googleKey };
|
|
827
|
+
const envKey = getEnvApiKey("google");
|
|
828
|
+
if (envKey) return { provider: "gemini", apiKey: envKey };
|
|
829
|
+
const fallbackKey = googleImageApiKeyFromEnv();
|
|
830
|
+
return fallbackKey ? { provider: "gemini", apiKey: fallbackKey } : null;
|
|
886
831
|
}
|
|
887
832
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
const alibaba = await findAlibabaImageCredentials(modelRegistry, sessionId);
|
|
898
|
-
if (alibaba) return alibaba;
|
|
899
|
-
|
|
900
|
-
return null;
|
|
833
|
+
// Fallback: try the model's own API key via the registry.
|
|
834
|
+
const apiKey = await modelRegistry.getApiKey(model, sessionId);
|
|
835
|
+
if (!isAuthenticated(apiKey)) return null;
|
|
836
|
+
return {
|
|
837
|
+
provider: provider as ImageProvider,
|
|
838
|
+
apiKey,
|
|
839
|
+
model,
|
|
840
|
+
};
|
|
901
841
|
}
|
|
902
842
|
|
|
903
843
|
async function loadImageFromPath(imagePath: string, cwd: string): Promise<InlineImageData> {
|
|
@@ -1336,27 +1276,29 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1336
1276
|
parameters: imageGenSchema,
|
|
1337
1277
|
async execute(_toolCallId, params, _onUpdate, ctx, signal) {
|
|
1338
1278
|
return untilAborted(signal, async () => {
|
|
1339
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
1340
|
-
const
|
|
1279
|
+
const sessionId = ctx.credentialSessionId ?? ctx.sessionManager.getSessionId();
|
|
1280
|
+
const settings = ctx.settings;
|
|
1281
|
+
if (!settings) {
|
|
1282
|
+
throw new Error("Image generation requires session settings to resolve the image model role.");
|
|
1283
|
+
}
|
|
1284
|
+
const apiKey = await findImageApiKey(ctx.modelRegistry, settings, sessionId, ctx.credentialSessionId);
|
|
1341
1285
|
if (!apiKey) {
|
|
1342
1286
|
throw new Error(
|
|
1343
|
-
"No image
|
|
1287
|
+
"No image model configured. Set an image-capable model via /model (image role) to enable image generation.",
|
|
1344
1288
|
);
|
|
1345
1289
|
}
|
|
1346
1290
|
|
|
1347
1291
|
const provider = apiKey.provider;
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
? resolveImageModel("openrouter", null)
|
|
1359
|
-
: resolveImageModel("gemini", null);
|
|
1292
|
+
const imageModel = apiKey.model;
|
|
1293
|
+
const model = imageModel
|
|
1294
|
+
? imageModel.id
|
|
1295
|
+
: provider === "antigravity"
|
|
1296
|
+
? resolveImageModel("antigravity", null)
|
|
1297
|
+
: provider === "alibaba"
|
|
1298
|
+
? resolveImageModel("alibaba", null)
|
|
1299
|
+
: provider === "openrouter"
|
|
1300
|
+
? resolveImageModel("openrouter", null)
|
|
1301
|
+
: resolveImageModel("gemini", null);
|
|
1360
1302
|
const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
|
|
1361
1303
|
const cwd = ctx.sessionManager.getCwd();
|
|
1362
1304
|
|
|
@@ -1743,18 +1685,20 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1743
1685
|
|
|
1744
1686
|
export async function getImageGenTools(
|
|
1745
1687
|
modelRegistry?: ModelRegistry,
|
|
1746
|
-
|
|
1688
|
+
settings?: Settings,
|
|
1747
1689
|
): Promise<Array<CustomTool<typeof imageGenSchema, ImageGenToolDetails>>> {
|
|
1748
|
-
|
|
1749
|
-
if (!
|
|
1690
|
+
// The tool is available when an image role model is configured and resolvable.
|
|
1691
|
+
if (!modelRegistry || !settings) return [];
|
|
1692
|
+
const imageModel = resolveImageRoleModel(settings, modelRegistry);
|
|
1693
|
+
if (!imageModel) return [];
|
|
1750
1694
|
return [imageGenTool];
|
|
1751
1695
|
}
|
|
1752
1696
|
|
|
1753
1697
|
export async function getImageGenToolsWithRegistry(
|
|
1754
1698
|
modelRegistry: ModelRegistry,
|
|
1755
|
-
|
|
1699
|
+
settings: Settings,
|
|
1756
1700
|
): Promise<Array<CustomTool<typeof imageGenSchema, ImageGenToolDetails>>> {
|
|
1757
|
-
const
|
|
1758
|
-
if (!
|
|
1701
|
+
const imageModel = resolveImageRoleModel(settings, modelRegistry);
|
|
1702
|
+
if (!imageModel) return [];
|
|
1759
1703
|
return [imageGenTool];
|
|
1760
1704
|
}
|
package/src/tools/index.ts
CHANGED
|
@@ -219,6 +219,8 @@ export interface ToolSession {
|
|
|
219
219
|
requestForegroundBashBackground?: () => boolean;
|
|
220
220
|
/** Get session ID */
|
|
221
221
|
getSessionId?: () => string | null;
|
|
222
|
+
/** Get credential-selection session identity. */
|
|
223
|
+
getCredentialSessionId?: () => string | null;
|
|
222
224
|
/** Scope-held MCP facade for mcp:// resolution. */
|
|
223
225
|
getMcpManager?: () => import("../runtime-mcp/manager").MCPManager | undefined;
|
|
224
226
|
/** Whether local:// must use external managed scratch instead of artifacts/local. */
|
package/src/tools/read.ts
CHANGED
|
@@ -2409,7 +2409,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2409
2409
|
if (countTextLines(code) > MAX_SUMMARY_LINES) return null;
|
|
2410
2410
|
|
|
2411
2411
|
const summarizeCode = (await nativeRead()).summarizeCode;
|
|
2412
|
-
return summarizeCode({
|
|
2412
|
+
return await summarizeCode({
|
|
2413
2413
|
code,
|
|
2414
2414
|
path: absolutePath,
|
|
2415
2415
|
minBodyLines: this.session.settings.get("read.summarize.minBodyLines"),
|
package/src/tools/resource-gc.ts
CHANGED
|
@@ -101,12 +101,17 @@ export interface ResourceGcDeps {
|
|
|
101
101
|
) => Promise<void>;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/** Request collection without synchronously stopping the main event loop. */
|
|
105
|
+
export function requestMemoryPressureGc(): void {
|
|
106
|
+
Bun.gc(false);
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
const defaultDeps: ResourceGcDeps = {
|
|
105
110
|
now: () => Date.now(),
|
|
106
111
|
monotonicNow: () => performance.now(),
|
|
107
112
|
rssBytes: () => process.memoryUsage().rss,
|
|
108
113
|
memorySnapshot: () => sampleMemoryPressure(),
|
|
109
|
-
runGc:
|
|
114
|
+
runGc: requestMemoryPressureGc,
|
|
110
115
|
logWarn: (msg, meta) => logger.warn(msg, meta),
|
|
111
116
|
listTabs: () => listTabsForGc(),
|
|
112
117
|
releaseTab: (name, policy) => releaseTabIfGcEligible(name, policy),
|
|
@@ -1073,7 +1073,7 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
1073
1073
|
"browser": {
|
|
1074
1074
|
"name": "browser",
|
|
1075
1075
|
"label": "Browser",
|
|
1076
|
-
"description": "Drives a real Chromium tab with full puppeteer access via JS execution.\n\n<instruction>\n- For static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer the `read` tool with a URL. Use this tool only when you need JS execution, authentication, or interactive actions.\n- Four actions:\n - `open` — acquire (or reuse) a named tab. `name` defaults to `\"main\"`. Optional `url`, `viewport`, and `dialogs: \"accept\" | \"dismiss\"` (auto-handles `alert`/`confirm`/`beforeunload`). The `app` field selects the browser kind (spawned binary, saved Chrome profile, or existing CDP endpoint); omitted means headless Chromium with stealth patches.\n - `close` — release a tab by `name`, or every tab with `all: true`. `kill: true` also terminates a spawned-app process tree.\n - `act` — run a list of structured `actions` against an existing tab without writing JS (preferred for routine navigation/interaction). Each step is `{ verb, … }`; verbs: `navigate {url, wait_until?}`, `click {id|selector}`, `type {id|selector, text}`, `fill {selector, value}`, `select {selector, values}`, `press {key, selector?}`, `scroll {dx?, dy?}`, `back`, `wait {selector?|ms?}`, `observe {viewport_only?, include_all?}`, `extract {format?}`, `screenshot`. Address elements by the numeric `id` from a prior `observe` (preferred) or a selector. Steps run in order; the tool returns per-step results.\n - `run` — execute JS against an existing tab. `code` is the body of an async function with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. The return value is JSON-stringified into the tool result; `display(value)` calls accumulate text/images. Use `run` only when an `act` verb does not cover what you need.\n- Tabs survive across `run` calls and across in-process subagents. Open once, reuse many times.\n- Browser kinds: no `app` launches headless Chromium; `app.path` reuses CDP or kills stale same-path processes before spawning — NEVER use it for a daily Chrome profile; use explicit `app.browser: \"chrome\"` profile mode instead. Saved-profile/CDP automation has access to that profile's cookies and authenticated accounts. Profile mode refuses a matching non-CDP Chrome instead of killing/relaunching it, and `kill: true` can terminate only a Chrome process GJC launched; `app.cdp_url` is externally owned and disconnect-only. CDP must stay on `127.0.0.1`: it grants full browser-account access.\n- Inside `run`, `tab` exposes high-level helpers (`goto`, `observe`, `id`, `click`, `type`, `fill`, `press`, `waitFor`, `screenshot`, `extract`, …); reach for `page` (raw puppeteer Page) when they don't cover it.\n- Selectors accept CSS as well as puppeteer query handlers: `aria/Sign in`, `text/Continue`, `xpath/…`, `pierce/…`.\n- Full reference — helpers, browser kinds, CDP/security details, and more examples — read `gjc://tools/browser.md`.\n</instruction>\n\n<critical>\n- You MUST call `open` before `run` or `act`. Neither implicitly creates a tab.\n- You MUST observe before taking a screenshot to understand page state; screenshot only when visual appearance matters.\n- After a `tab.goto()` or any navigation, prior element ids from `tab.observe()` are invalidated. Re-observe before referencing them.\n- `code` runs with full Node access. Treat it as your code, not sandboxed code.\n</critical>\n\n<examples>\n# Open a tab and read structured page data\n`{\"action\":\"open\",\"name\":\"docs\",\"url\":\"https://example.com\"}`\n`{\"action\":\"act\",\"name\":\"docs\",\"actions\":[{\"verb\":\"observe\"}]}`\n\n# Click an observed element, then fill and submit a form\n`{\"action\":\"act\",\"name\":\"docs\",\"actions\":[{\"verb\":\"click\",\"id\":12},{\"verb\":\"fill\",\"selector\":\"input[name=email]\",\"value\":\"me@example.com\"},{\"verb\":\"click\",\"selector\":\"text/Continue\"}]}`\n\n# Use `run` only when `act` has no suitable verb\n`{\"action\":\"run\",\"name\":\"docs\",\"code\":\"const count = await page.locator('canvas').count(); return { count };\"}`\n</examples>\n\n<output>\n- Per call: any `display(value)` outputs (text/images) followed by the JSON-stringified return value of the `code` function. `run` always produces at least a status line.\n</output>",
|
|
1076
|
+
"description": "Drives a real Chromium tab with full puppeteer access via JS execution.\n\n<instruction>\n- For static web content (articles, docs, issues/PRs, JSON, PDFs, feeds), prefer the `read` tool with a URL. Use this tool only when you need JS execution, authentication, or interactive actions.\n- Four actions:\n - `open` — acquire (or reuse) a named tab. `name` defaults to `\"main\"`. Optional `url`, `viewport`, and `dialogs: \"accept\" | \"dismiss\"` (auto-handles `alert`/`confirm`/`beforeunload`). The `app` field selects the browser kind (spawned binary, saved Chrome profile, or existing CDP endpoint); omitted means headless Chromium with stealth patches.\n - `close` — release a tab by `name`, or every tab with `all: true`. `kill: true` also terminates a spawned-app process tree.\n - `act` — run a list of structured `actions` against an existing tab without writing JS (preferred for routine navigation/interaction). Each step is `{ verb, … }`; verbs: `navigate {url, wait_until?}`, `click {id|selector}`, `type {id|selector, text}`, `fill {selector, value}`, `select {selector, values}`, `press {key, selector?}`, `scroll {dx?, dy?}`, `back`, `wait {selector?|ms?}`, `observe {viewport_only?, include_all?}`, `extract {format?}`, `screenshot`. Address elements by the numeric `id` from a prior `observe` (preferred) or a selector. Steps run in order; the tool returns per-step results.\n - `run` — execute JS against an existing tab. `code` is the body of an async function with `page`, `browser`, `tab`, `display`, `assert`, `wait` in scope. The return value is JSON-stringified into the tool result; `display(value)` calls accumulate text/images. Use `run` only when an `act` verb does not cover what you need.\n- Tabs survive across `run` calls and across in-process subagents. Open once, reuse many times.\n- Browser kinds: no `app` launches headless Chromium; `app.path` reuses CDP or kills stale same-path processes before spawning — NEVER use it for a daily Chrome profile; use explicit `app.browser: \"chrome\"` profile mode instead. Saved-profile/CDP automation has access to that profile's cookies and authenticated accounts. Profile mode refuses a matching non-CDP Chrome instead of killing/relaunching it, and `kill: true` can terminate only a Chrome process GJC launched; `app.cdp_url` is externally owned and disconnect-only. CDP must stay on `127.0.0.1`: it grants full browser-account access.\n- Inside `run`, `tab` exposes high-level helpers (`goto`, `observe`, `id`, `click`, `type`, `fill`, `press`, `waitFor`, `screenshot`, `extract`, …); reach for `page` (raw puppeteer Page) when they don't cover it.\n- Selectors accept CSS as well as puppeteer query handlers: `aria/Sign in`, `text/Continue`, `xpath/…`, `pierce/…`.\n- Runtime diagnostics are opt-in: pass `diagnostics: true` to `open` to subscribe the tab to page `Runtime.exceptionThrown` and `console.error` events. The next successful `act`/`run` response then includes at most 20 `runtimeDiagnostics` entries plus `runtimeDiagnosticsDropped`, then drains them. Entries contain only kind, time, origin-only URL, line/column, and a built-in error class from a fixed allowlist — never path segments, query strings, messages, console arguments, values, or stacks. Output is byte-bounded and marks truncation explicitly.\n- Full reference — helpers, browser kinds, CDP/security details, and more examples — read `gjc://tools/browser.md`.\n</instruction>\n\n<critical>\n- You MUST call `open` before `run` or `act`. Neither implicitly creates a tab.\n- You MUST observe before taking a screenshot to understand page state; screenshot only when visual appearance matters.\n- After a `tab.goto()` or any navigation, prior element ids from `tab.observe()` are invalidated. Re-observe before referencing them.\n- `code` runs with full Node access. Treat it as your code, not sandboxed code.\n</critical>\n\n<examples>\n# Open a tab and read structured page data\n`{\"action\":\"open\",\"name\":\"docs\",\"url\":\"https://example.com\"}`\n`{\"action\":\"act\",\"name\":\"docs\",\"actions\":[{\"verb\":\"observe\"}]}`\n\n# Click an observed element, then fill and submit a form\n`{\"action\":\"act\",\"name\":\"docs\",\"actions\":[{\"verb\":\"click\",\"id\":12},{\"verb\":\"fill\",\"selector\":\"input[name=email]\",\"value\":\"me@example.com\"},{\"verb\":\"click\",\"selector\":\"text/Continue\"}]}`\n\n# Use `run` only when `act` has no suitable verb\n`{\"action\":\"run\",\"name\":\"docs\",\"code\":\"const count = await page.locator('canvas').count(); return { count };\"}`\n</examples>\n\n<output>\n- Per call: any `display(value)` outputs (text/images) followed by the JSON-stringified return value of the `code` function. `run` always produces at least a status line.\n</output>",
|
|
1077
1077
|
"parameters": {
|
|
1078
1078
|
"type": "object",
|
|
1079
1079
|
"properties": {
|
|
@@ -1185,6 +1185,10 @@ export const TOOL_CATALOG: Readonly<Record<string, ToolCatalogEntry>> = {
|
|
|
1185
1185
|
],
|
|
1186
1186
|
"description": "auto-handle dialogs"
|
|
1187
1187
|
},
|
|
1188
|
+
"diagnostics": {
|
|
1189
|
+
"type": "boolean",
|
|
1190
|
+
"description": "opt-in: capture bounded page runtime diagnostics (page exceptions and console.error metadata) in the next successful run/act response"
|
|
1191
|
+
},
|
|
1188
1192
|
"code": {
|
|
1189
1193
|
"type": "string",
|
|
1190
1194
|
"description": "js body to run in tab"
|
|
@@ -316,3 +316,96 @@ export function parsePastedImagePaths(
|
|
|
316
316
|
export function formatPastedImageReference(placeholder: string, imagePath: string): string {
|
|
317
317
|
return `${placeholder} source=${JSON.stringify(imagePath)}`;
|
|
318
318
|
}
|
|
319
|
+
export interface PastedImageReferenceRange {
|
|
320
|
+
imageIndex: number;
|
|
321
|
+
startCol: number;
|
|
322
|
+
endCol: number;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function findJsonStringEnd(text: string, startCol: number): number | null {
|
|
326
|
+
if (text[startCol] !== '"') return null;
|
|
327
|
+
|
|
328
|
+
let escaped = false;
|
|
329
|
+
for (let index = startCol + 1; index < text.length; index += 1) {
|
|
330
|
+
const character = text[index];
|
|
331
|
+
if (escaped) {
|
|
332
|
+
escaped = false;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (character === "\\") {
|
|
336
|
+
escaped = true;
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
if (character !== '"') continue;
|
|
340
|
+
|
|
341
|
+
const endCol = index + 1;
|
|
342
|
+
try {
|
|
343
|
+
if (typeof JSON.parse(text.slice(startCol, endCol)) !== "string") return null;
|
|
344
|
+
} catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
return endCol;
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Collects every validated `source=<JSON string>` value span on the line.
|
|
354
|
+
*
|
|
355
|
+
* A placeholder-looking token inside one of these spans is part of a file path,
|
|
356
|
+
* not a composer placeholder, so deleting it would corrupt the path. Spans are
|
|
357
|
+
* collected independently of any preceding placeholder because an orphaned
|
|
358
|
+
* `source="…"` can outlive the placeholder that produced it.
|
|
359
|
+
*/
|
|
360
|
+
function findSourceValueSpans(line: string): Array<{ start: number; end: number }> {
|
|
361
|
+
const spans: Array<{ start: number; end: number }> = [];
|
|
362
|
+
const sourcePattern = /source=/g;
|
|
363
|
+
while (true) {
|
|
364
|
+
const match = sourcePattern.exec(line);
|
|
365
|
+
if (!match) return spans;
|
|
366
|
+
const valueStart = match.index + match[0].length;
|
|
367
|
+
const valueEnd = findJsonStringEnd(line, valueStart);
|
|
368
|
+
if (valueEnd === null) continue;
|
|
369
|
+
spans.push({ start: valueStart, end: valueEnd });
|
|
370
|
+
sourcePattern.lastIndex = valueEnd;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function locatePastedImageReferenceAroundCursor(
|
|
375
|
+
line: string,
|
|
376
|
+
cursorCol: number,
|
|
377
|
+
): PastedImageReferenceRange | null {
|
|
378
|
+
if (!Number.isInteger(cursorCol) || cursorCol < 0 || cursorCol > line.length) return null;
|
|
379
|
+
|
|
380
|
+
const sourceValueSpans = findSourceValueSpans(line);
|
|
381
|
+
const placeholderPattern = /\[image ([1-9]\d*)\]/g;
|
|
382
|
+
while (true) {
|
|
383
|
+
const match = placeholderPattern.exec(line);
|
|
384
|
+
if (!match) return null;
|
|
385
|
+
|
|
386
|
+
const startCol = match.index;
|
|
387
|
+
const placeholder = match[0];
|
|
388
|
+
const imageIndex = Number.parseInt(match[1] ?? "", 10);
|
|
389
|
+
if (!Number.isSafeInteger(imageIndex)) continue;
|
|
390
|
+
|
|
391
|
+
const enclosingSpan = sourceValueSpans.find(span => startCol >= span.start && startCol < span.end);
|
|
392
|
+
if (enclosingSpan) {
|
|
393
|
+
placeholderPattern.lastIndex = enclosingSpan.end;
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const placeholderEnd = startCol + placeholder.length;
|
|
398
|
+
const sourcePrefix = " source=";
|
|
399
|
+
if (line.startsWith(sourcePrefix, placeholderEnd)) {
|
|
400
|
+
const fullEnd = findJsonStringEnd(line, placeholderEnd + sourcePrefix.length);
|
|
401
|
+
if (fullEnd === null) return null;
|
|
402
|
+
if (cursorCol === placeholderEnd || cursorCol === fullEnd) {
|
|
403
|
+
return { imageIndex, startCol, endCol: fullEnd };
|
|
404
|
+
}
|
|
405
|
+
placeholderPattern.lastIndex = fullEnd;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (cursorCol === placeholderEnd) return { imageIndex, startCol, endCol: placeholderEnd };
|
|
410
|
+
}
|
|
411
|
+
}
|
package/src/web/search/index.ts
CHANGED
|
@@ -339,7 +339,7 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
339
339
|
: undefined;
|
|
340
340
|
prewarmSearchProviders({
|
|
341
341
|
authStorage,
|
|
342
|
-
sessionId: session.getSessionId?.() ?? undefined,
|
|
342
|
+
sessionId: session.getCredentialSessionId?.() ?? session.getSessionId?.() ?? undefined,
|
|
343
343
|
activeModelContext,
|
|
344
344
|
});
|
|
345
345
|
} catch {
|
|
@@ -356,7 +356,7 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
356
356
|
_context?: AgentToolContext,
|
|
357
357
|
): Promise<AgentToolResult<SearchRenderDetails>> {
|
|
358
358
|
const authStorage = this.#session.authStorage ?? (await discoverAuthStorage());
|
|
359
|
-
const sessionId = this.#session.getSessionId?.() ?? undefined;
|
|
359
|
+
const sessionId = this.#session.getCredentialSessionId?.() ?? this.#session.getSessionId?.() ?? undefined;
|
|
360
360
|
const activeModelContext = this.#session.model
|
|
361
361
|
? this.#session.modelRegistry?.getActiveSearchModelContext(this.#session.model)
|
|
362
362
|
: undefined;
|
|
@@ -379,7 +379,7 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRende
|
|
|
379
379
|
signal?: AbortSignal,
|
|
380
380
|
) {
|
|
381
381
|
const authStorage = ctx.modelRegistry?.authStorage ?? (await discoverAuthStorage());
|
|
382
|
-
const sessionId = ctx.sessionManager.getSessionId();
|
|
382
|
+
const sessionId = ctx.credentialSessionId ?? ctx.sessionManager.getSessionId();
|
|
383
383
|
return executeSearch(toolCallId, params, {
|
|
384
384
|
authStorage,
|
|
385
385
|
sessionId,
|