fix: permissions, sidebar and system prompt

This commit is contained in:
Gab 2026-03-25 20:39:20 +11:00
parent 06db5cd050
commit 5b6dfd75e8
6 changed files with 52 additions and 11 deletions

View File

@ -784,7 +784,7 @@ function App() {
await DialogAlert.show( await DialogAlert.show(
dialog, dialog,
"Update Complete", "Update Complete",
`Successfully updated to TF Code v${result.data.version}. Please restart the application.`, `Successfully updated to TF Code (tfcode) v${result.data.version}. Please restart the application.`,
) )
exit() exit()

View File

@ -157,11 +157,15 @@ export function PermissionPrompt(props: { request: PermissionRequest }) {
body={ body={
<Switch> <Switch>
<Match when={props.request.always.length === 1 && props.request.always[0] === "*"}> <Match when={props.request.always.length === 1 && props.request.always[0] === "*"}>
<TextBody title={"This will allow " + props.request.permission + " until TF Code is restarted."} /> <TextBody
title={"This will allow " + props.request.permission + " until TF Code (tfcode) is restarted."}
/>
</Match> </Match>
<Match when={true}> <Match when={true}>
<box paddingLeft={1} gap={1}> <box paddingLeft={1} gap={1}>
<text fg={theme.textMuted}>This will allow the following patterns until TF Code is restarted</text> <text fg={theme.textMuted}>
This will allow the following patterns until TF Code (tfcode) is restarted
</text>
<box> <box>
<For each={props.request.always}> <For each={props.request.always}>
{(pattern) => ( {(pattern) => (
@ -501,7 +505,7 @@ function RejectPrompt(props: { onConfirm: (message: string) => void; onCancel: (
<text fg={theme.text}>Reject permission</text> <text fg={theme.text}>Reject permission</text>
</box> </box>
<box paddingLeft={1}> <box paddingLeft={1}>
<text fg={theme.textMuted}>Tell TF Code what to do differently</text> <text fg={theme.textMuted}>Tell TF Code (tfcode) what to do differently</text>
</box> </box>
</box> </box>
<box <box

View File

@ -104,7 +104,6 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
</text> </text>
<text fg={theme.textMuted}>{context()?.tokens ?? 0} tokens</text> <text fg={theme.textMuted}>{context()?.tokens ?? 0} tokens</text>
<text fg={theme.textMuted}>{context()?.percentage ?? 0}% used</text> <text fg={theme.textMuted}>{context()?.percentage ?? 0}% used</text>
<text fg={theme.textMuted}>{cost()} spent</text>
</box> </box>
<Show when={mcpEntries().length > 0}> <Show when={mcpEntries().length > 0}>
<box> <box>
@ -292,7 +291,7 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
</text> </text>
</box> </box>
<text fg={theme.textMuted}>TF Code includes free models so you can start immediately.</text> <text fg={theme.textMuted}>TF Code (tfcode) includes free models so you can start immediately.</text>
<text fg={theme.textMuted}> <text fg={theme.textMuted}>
Connect from 75+ providers to use other models, including Claude, GPT, Gemini etc Connect from 75+ providers to use other models, including Claude, GPT, Gemini etc
</text> </text>

View File

@ -151,6 +151,13 @@ export function createToothFairyAI(options: ToothFairyAIProviderSettings = {}):
finish_reason: parsed.choices[0].finish_reason, finish_reason: parsed.choices[0].finish_reason,
}) })
} }
if (parsed.usage) {
log.info("stream usage", {
prompt_tokens: parsed.usage.prompt_tokens,
completion_tokens: parsed.usage.completion_tokens,
total_tokens: parsed.usage.total_tokens,
})
}
} catch {} } catch {}
} }
} else { } else {
@ -181,6 +188,7 @@ export function createToothFairyAI(options: ToothFairyAIProviderSettings = {}):
headers: () => baseHeaders, headers: () => baseHeaders,
url: ({ path }) => `${baseURL}${path}`, url: ({ path }) => `${baseURL}${path}`,
fetch: customFetch as FetchFunction, fetch: customFetch as FetchFunction,
includeUsage: true,
}) })
} }

View File

@ -152,7 +152,18 @@ export namespace SessionProcessor {
const parts = await MessageV2.parts(input.assistantMessage.id) const parts = await MessageV2.parts(input.assistantMessage.id)
const lastThree = parts.slice(-DOOM_LOOP_THRESHOLD) const lastThree = parts.slice(-DOOM_LOOP_THRESHOLD)
if ( const getLoopKey = (tool: string, input: unknown): string | null => {
if (typeof input !== "object" || !input) return null
const i = input as Record<string, unknown>
if (tool === "read" && typeof i.filePath === "string") return `read:${i.filePath}`
if (tool === "glob" && typeof i.pattern === "string") return `glob:${i.pattern}`
if (tool === "grep" && typeof i.pattern === "string") return `grep:${i.pattern}`
return null
}
const loopKey = getLoopKey(value.toolName, value.input)
const isIdenticalLoop =
lastThree.length === DOOM_LOOP_THRESHOLD && lastThree.length === DOOM_LOOP_THRESHOLD &&
lastThree.every( lastThree.every(
(p) => (p) =>
@ -161,7 +172,19 @@ export namespace SessionProcessor {
p.state.status !== "pending" && p.state.status !== "pending" &&
JSON.stringify(p.state.input) === JSON.stringify(value.input), JSON.stringify(p.state.input) === JSON.stringify(value.input),
) )
) {
const isSimilarLoop =
loopKey &&
lastThree.length === DOOM_LOOP_THRESHOLD &&
lastThree.every(
(p) =>
p.type === "tool" &&
p.tool === value.toolName &&
p.state.status !== "pending" &&
getLoopKey(p.tool, p.state.input) === loopKey,
)
if (isIdenticalLoop || isSimilarLoop) {
const agent = await Agent.get(input.assistantMessage.agent) const agent = await Agent.get(input.assistantMessage.agent)
await Permission.ask({ await Permission.ask({
permission: "doom_loop", permission: "doom_loop",
@ -170,6 +193,7 @@ export namespace SessionProcessor {
metadata: { metadata: {
tool: value.toolName, tool: value.toolName,
input: value.input, input: value.input,
reason: isSimilarLoop ? "same file/pattern read multiple times" : "identical input",
}, },
always: [value.toolName], always: [value.toolName],
ruleset: agent.permission, ruleset: agent.permission,

View File

@ -1,12 +1,12 @@
You are opencode, an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user. You are TF Code (also referred to as tfcode), an interactive CLI tool that helps users with software engineering tasks. Users may refer to you as "TF Code", "tfcode", or both interchangeably. Use the instructions below and the tools available to you to assist the user.
IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files. IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident that the URLs are for helping the user with programming. You may use URLs provided by the user in their messages or local files.
If the user asks for help or wants to give feedback inform them of the following: If the user asks for help or wants to give feedback inform them of the following:
- /help: Get help with using opencode - /help: Get help with using TF Code
- To give feedback, users should report the issue at https://github.com/anomalyco/opencode/issues - To give feedback, users should report the issue at https://github.com/anomalyco/opencode/issues
When the user directly asks about opencode (eg 'can opencode do...', 'does opencode have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the WebFetch tool to gather information to answer the question from opencode docs at https://opencode.ai When the user directly asks about TF Code (eg 'can TF Code do...', 'does TF Code have...') or asks in second person (eg 'are you able...', 'can you do...'), first use the WebFetch tool to gather information to answer the question from opencode docs at https://opencode.ai
# Tone and style # Tone and style
You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system). You should be concise, direct, and to the point. When you run a non-trivial bash command, you should explain what the command does and why you are running it, to make sure the user understands what you are doing (this is especially important when you are running a command that will make changes to the user's system).
@ -85,6 +85,12 @@ The user will primarily request you perform software engineering tasks. This inc
- VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time. - VERY IMPORTANT: When you have completed a task, you MUST run the lint and typecheck commands (e.g. npm run lint, npm run typecheck, ruff, etc.) with Bash if they were provided to you to ensure your code is correct. If you are unable to find the correct command, ask the user for the command to run and if they supply it, proactively suggest writing it to AGENTS.md so that you will know to run it next time.
NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive. NEVER commit changes unless the user explicitly asks you to. It is VERY IMPORTANT to only commit when explicitly asked, otherwise the user will feel that you are being too proactive.
# Avoiding loops
- CRITICAL: Avoid verification loops. After making edits, do NOT re-read the same file to verify your changes. Trust that your edits were applied correctly.
- If you need to verify something, read the file ONCE before editing, not repeatedly after each edit.
- If you believe an edit was not applied correctly, check the file ONCE. If it looks correct, move on. Do not re-read the same file multiple times.
- Reading the same file 3 or more times is a sign of a loop - STOP and ask the user for guidance.
- Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result. - Tool results and user messages may include <system-reminder> tags. <system-reminder> tags contain useful information and reminders. They are NOT part of the user's provided input or the tool result.
# Tool usage policy # Tool usage policy