From c8231976f30bbbf0995a4b47a00a364a38f908fc Mon Sep 17 00:00:00 2001 From: Gab Date: Thu, 9 Apr 2026 22:28:20 +1000 Subject: [PATCH] feat: cherries --- .../tfcode/src/agent/prompt/compaction.txt | 1 + packages/tfcode/src/cli/cmd/run.ts | 31 +++++++++++++------ packages/tfcode/src/provider/provider.ts | 1 + packages/tfcode/src/session/compaction.ts | 1 + packages/tfcode/src/session/prompt.ts | 11 ++++++- packages/tfcode/src/tool/tool.ts | 2 +- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/tfcode/src/agent/prompt/compaction.txt b/packages/tfcode/src/agent/prompt/compaction.txt index 3308627e1..11deccb3a 100644 --- a/packages/tfcode/src/agent/prompt/compaction.txt +++ b/packages/tfcode/src/agent/prompt/compaction.txt @@ -12,3 +12,4 @@ Focus on information that would be helpful for continuing the conversation, incl Your summary should be comprehensive enough to provide context but concise enough to be quickly understood. Do not respond to any questions in the conversation, only output the summary. +Respond in the same language the user used in the conversation. diff --git a/packages/tfcode/src/cli/cmd/run.ts b/packages/tfcode/src/cli/cmd/run.ts index 2ce50010e..32959e210 100644 --- a/packages/tfcode/src/cli/cmd/run.ts +++ b/packages/tfcode/src/cli/cmd/run.ts @@ -302,6 +302,11 @@ export const RunCommand = cmd({ describe: "show thinking blocks", default: false, }) + .option("dangerously-skip-permissions", { + type: "boolean", + describe: "auto-approve permissions that are not explicitly denied (dangerous!)", + default: false, + }) }, handler: async (args) => { let message = [...args.message, ...(args["--"] || [])] @@ -544,15 +549,23 @@ export const RunCommand = cmd({ if (event.type === "permission.asked") { const permission = event.properties if (permission.sessionID !== sessionID) continue - UI.println( - UI.Style.TEXT_WARNING_BOLD + "!", - UI.Style.TEXT_NORMAL + - `permission requested: ${permission.permission} (${permission.patterns.join(", ")}); auto-rejecting`, - ) - await sdk.permission.reply({ - requestID: permission.id, - reply: "reject", - }) + + if (args["dangerously-skip-permissions"]) { + await sdk.permission.reply({ + requestID: permission.id, + reply: "once", + }) + } else { + UI.println( + UI.Style.TEXT_WARNING_BOLD + "!", + UI.Style.TEXT_NORMAL + + `permission requested: ${permission.permission} (${permission.patterns.join(", ")}); auto-rejecting`, + ) + await sdk.permission.reply({ + requestID: permission.id, + reply: "reject", + }) + } } } } diff --git a/packages/tfcode/src/provider/provider.ts b/packages/tfcode/src/provider/provider.ts index 08efafa30..07a637c98 100644 --- a/packages/tfcode/src/provider/provider.ts +++ b/packages/tfcode/src/provider/provider.ts @@ -1074,6 +1074,7 @@ export namespace Provider { options: mergeDeep(existingModel?.options ?? {}, model.options ?? {}), limit: { context: model.limit?.context ?? existingModel?.limit?.context ?? 0, + input: model.limit?.input ?? existingModel?.limit?.input, output: model.limit?.output ?? existingModel?.limit?.output ?? 0, }, headers: mergeDeep(existingModel?.headers ?? {}, model.headers ?? {}), diff --git a/packages/tfcode/src/session/compaction.ts b/packages/tfcode/src/session/compaction.ts index da9650f4b..960b58b14 100644 --- a/packages/tfcode/src/session/compaction.ts +++ b/packages/tfcode/src/session/compaction.ts @@ -178,6 +178,7 @@ export namespace SessionCompaction { const defaultPrompt = `Provide a detailed prompt for continuing our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next. The summary that you construct will be used so that another agent can read it and continue the work. +Respond in the same language as the user's messages in the conversation. When constructing the summary, try to stick to this template: --- diff --git a/packages/tfcode/src/session/prompt.ts b/packages/tfcode/src/session/prompt.ts index f57c234ea..cea05d859 100644 --- a/packages/tfcode/src/session/prompt.ts +++ b/packages/tfcode/src/session/prompt.ts @@ -319,9 +319,18 @@ export namespace SessionPrompt { } if (!lastUser) throw new Error("No user message found in stream. This should never happen.") + + const lastAssistantMsg = msgs.findLast( + (msg) => msg.info.role === "assistant" && msg.info.id === lastAssistant?.id, + ) + // Some providers return "stop" even when the assistant message contains tool calls. + // Keep the loop running so tool results can be sent back to the model. + const hasToolCalls = lastAssistantMsg?.parts.some((part) => part.type === "tool") ?? false + if ( lastAssistant?.finish && - !["tool-calls", "unknown"].includes(lastAssistant.finish) && + !["tool-calls"].includes(lastAssistant.finish) && + !hasToolCalls && lastUser.id < lastAssistant.id ) { log.info("exiting loop", { sessionID }) diff --git a/packages/tfcode/src/tool/tool.ts b/packages/tfcode/src/tool/tool.ts index 6c3f4efaf..0f0c1d419 100644 --- a/packages/tfcode/src/tool/tool.ts +++ b/packages/tfcode/src/tool/tool.ts @@ -53,7 +53,7 @@ export namespace Tool { return { id, init: async (initCtx) => { - const toolInfo = init instanceof Function ? await init(initCtx) : init + const toolInfo = init instanceof Function ? await init(initCtx) : { ...init } const execute = toolInfo.execute toolInfo.execute = async (args, ctx) => { try {