diff --git a/src/agents/gigachat-stream.tool-calls.test.ts b/src/agents/gigachat-stream.tool-calls.test.ts index c05df0e6d68..0c73157a4a0 100644 --- a/src/agents/gigachat-stream.tool-calls.test.ts +++ b/src/agents/gigachat-stream.tool-calls.test.ts @@ -641,6 +641,73 @@ describe("createGigachatStreamFn tool calling", () => { ); }); + it("preserves historical tool results as plain text when functions are disabled", async () => { + vi.stubEnv("GIGACHAT_DISABLE_FUNCTIONS", "1"); + request.mockResolvedValueOnce({ + status: 200, + data: createSseStream(['data: {"choices":[{"delta":{"content":"done"}}]}', "data: [DONE]"]), + }); + + const streamFn = createGigachatStreamFn({ + baseUrl: "https://gigachat.devices.sberbank.ru/api/v1", + authMode: "oauth", + }); + + const stream = await streamFn( + { api: "gigachat", provider: "gigachat", id: "GigaChat-2-Max" } as never, + { + messages: [ + { + role: "assistant", + content: [ + { + type: "toolCall", + id: "call_1", + name: "llm-task", + arguments: { prompt: "hi" }, + }, + ], + }, + { + role: "toolResult", + toolName: "llm-task", + content: '{"summary":"tool output"}', + }, + ], + tools: [ + { + name: "llm-task", + description: "Run a task", + parameters: { + type: "object", + properties: { + prompt: { type: "string" }, + }, + }, + }, + ], + } as never, + { apiKey: "token" } as never, + ); + + await expect(stream.result()).resolves.toMatchObject({ + content: [{ type: "text", text: "done" }], + }); + + const requestPayload = request.mock.calls[0]?.[0]?.data as { + messages?: Array<{ role: string; content?: string }>; + functions?: unknown; + }; + expect(requestPayload.functions).toBeUndefined(); + expect(requestPayload.messages).toEqual([ + expect.objectContaining({ role: "assistant", content: "[Called llm-task]" }), + expect.objectContaining({ + role: "user", + content: '[Tool Result: llm-task]\n{"summary":"tool output"}', + }), + ]); + }); + it("preserves all historical tool calls from a single assistant turn", async () => { request.mockResolvedValueOnce({ status: 200, diff --git a/src/agents/gigachat-stream.ts b/src/agents/gigachat-stream.ts index d1f34bd33d0..f33dfe2508c 100644 --- a/src/agents/gigachat-stream.ts +++ b/src/agents/gigachat-stream.ts @@ -296,6 +296,24 @@ function sanitizeContent(content: string | null | undefined): string { ); } +function extractToolResultTextContent(content: unknown): string { + if (Array.isArray(content)) { + return content + .filter((c): c is TextContent => c.type === "text") + .map((c) => c.text) + .join("\n"); + } + if (typeof content === "string") { + return content; + } + return JSON.stringify(content ?? {}); +} + +function formatToolResultReplayText(toolName: string | undefined, content: unknown): string { + const replayContent = extractToolResultTextContent(content) || "ok"; + return `[Tool Result: ${toolName?.trim() || "unknown"}]\n${replayContent}`; +} + function tryParseJsonObjectString(content: string): string | null { const trimmed = content.trim(); if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) { @@ -776,24 +794,27 @@ export function createGigachatStreamFn(opts: GigachatStreamOptions): StreamFn { content: toolCalls.map((toolCall) => `[Called ${toolCall.name}]`).join(" "), }); } - } else if (msg.role === "toolResult" && functionsEnabled) { + } else if (msg.role === "toolResult") { const toolName = msg.toolName ?? "unknown"; - const msgContent = msg.content; - const resultContent = Array.isArray(msgContent) - ? msgContent - .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) - .join("\n") - : typeof msgContent === "string" - ? msgContent - : JSON.stringify(msgContent ?? {}); - const coercedContent = ensureJsonObjectStr(resultContent || "ok", toolName); - const gigaToolName = rememberToolNameMapping(toolNameToGiga, gigaToToolName, toolName); - messages.push({ - role: "function", - content: coercedContent, - name: gigaToolName, - }); + if (functionsEnabled) { + const resultContent = extractToolResultTextContent(msg.content); + const coercedContent = ensureJsonObjectStr(resultContent || "ok", toolName); + const gigaToolName = rememberToolNameMapping( + toolNameToGiga, + gigaToToolName, + toolName, + ); + messages.push({ + role: "function", + content: coercedContent, + name: gigaToolName, + }); + } else { + messages.push({ + role: "user", + content: sanitizeContent(formatToolResultReplayText(toolName, msg.content)), + }); + } } } diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 3bf0c7402f4..afb41064501 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -165,7 +165,6 @@ export async function applyAuthChoiceApiProviders( { secretInputMode: mode ?? requestedSecretInputMode }, { authMode: "oauth", - insecureTls: "false", scope: gigachatScope, }, ); @@ -248,7 +247,6 @@ export async function applyAuthChoiceApiProviders( const basicMetadata: Record = { authMode: "basic", - insecureTls: "false", ...(gigachatBasicScope ? { scope: gigachatBasicScope } : {}), }; diff --git a/src/commands/auth-choice.test.ts b/src/commands/auth-choice.test.ts index 03fa9a0320c..1dccf458597 100644 --- a/src/commands/auth-choice.test.ts +++ b/src/commands/auth-choice.test.ts @@ -342,10 +342,10 @@ describe("applyAuthChoice", () => { key: "basic-user:basic-pass", metadata: { authMode: "basic", - insecureTls: "false", scope: "GIGACHAT_API_PERS", }, }); + expect((await readAuthProfile("gigachat:default"))?.metadata).not.toHaveProperty("insecureTls"); expect((await readAuthProfile("gigachat:default"))?.keyRef).toBeUndefined(); }); @@ -376,7 +376,6 @@ describe("applyAuthChoice", () => { expect(await readAuthProfile("gigachat:default")).toMatchObject({ metadata: { authMode: "basic", - insecureTls: "false", scope: "GIGACHAT_API_B2B", }, }); @@ -447,9 +446,9 @@ describe("applyAuthChoice", () => { metadata: { authMode: "oauth", scope: "GIGACHAT_API_PERS", - insecureTls: "false", }, }); + expect(profile?.metadata).not.toHaveProperty("insecureTls"); }); it("resets a custom Basic GigaChat base URL when switching to OAuth", async () => { @@ -497,7 +496,6 @@ describe("applyAuthChoice", () => { metadata: { authMode: "oauth", scope: "GIGACHAT_API_PERS", - insecureTls: "false", }, }); }); diff --git a/src/commands/onboard-non-interactive.provider-auth.test.ts b/src/commands/onboard-non-interactive.provider-auth.test.ts index 8c7e1c722fd..8d8e9809146 100644 --- a/src/commands/onboard-non-interactive.provider-auth.test.ts +++ b/src/commands/onboard-non-interactive.provider-auth.test.ts @@ -377,9 +377,12 @@ describe("onboard (non-interactive): provider auth", () => { metadata: { authMode: "oauth", scope: "GIGACHAT_API_PERS", - insecureTls: "false", }, }); + const gigachatProfile = cfg.auth?.profiles?.["gigachat:default"] as + | { metadata?: Record } + | undefined; + expect(gigachatProfile?.metadata).not.toHaveProperty("insecureTls"); }); }); diff --git a/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.test.ts b/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.test.ts index 57ebc329fd9..808b3511263 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.test.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.test.ts @@ -168,7 +168,6 @@ describe("applySimpleNonInteractiveApiKeyChoice", () => { undefined, { authMode: "oauth", - insecureTls: "false", scope: "GIGACHAT_API_PERS", }, ); @@ -282,7 +281,6 @@ describe("applySimpleNonInteractiveApiKeyChoice", () => { undefined, { authMode: "oauth", - insecureTls: "false", scope: "GIGACHAT_API_PERS", }, ); diff --git a/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.ts b/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.ts index 1dd99adcd12..5d5a7fe8cd0 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.api-key-providers.ts @@ -98,7 +98,6 @@ async function applyGigachatNonInteractiveApiKeyChoice(params: { !(await params.maybeSetResolvedApiKey(resolved, (value) => setGigachatApiKey(value, params.agentDir, params.apiKeyStorageOptions, { authMode: "oauth", - insecureTls: "false", scope: "GIGACHAT_API_PERS", }), ))