mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-26 18:44:47 +00:00
fix(app): task error state
This commit is contained in:
@@ -425,14 +425,14 @@ export namespace SessionPrompt {
|
|||||||
extra: { bypassAgentCheck: true },
|
extra: { bypassAgentCheck: true },
|
||||||
messages: msgs,
|
messages: msgs,
|
||||||
async metadata(input) {
|
async metadata(input) {
|
||||||
await Session.updatePart({
|
part = (await Session.updatePart({
|
||||||
...part,
|
...part,
|
||||||
type: "tool",
|
type: "tool",
|
||||||
state: {
|
state: {
|
||||||
...part.state,
|
...part.state,
|
||||||
...input,
|
...input,
|
||||||
},
|
},
|
||||||
} satisfies MessageV2.ToolPart)
|
} satisfies MessageV2.ToolPart)) as MessageV2.ToolPart
|
||||||
},
|
},
|
||||||
async ask(req) {
|
async ask(req) {
|
||||||
await PermissionNext.ask({
|
await PermissionNext.ask({
|
||||||
@@ -493,7 +493,7 @@ export namespace SessionPrompt {
|
|||||||
start: part.state.status === "running" ? part.state.time.start : Date.now(),
|
start: part.state.status === "running" ? part.state.time.start : Date.now(),
|
||||||
end: Date.now(),
|
end: Date.now(),
|
||||||
},
|
},
|
||||||
metadata: part.metadata,
|
metadata: "metadata" in part.state ? part.state.metadata : undefined,
|
||||||
input: part.state.input,
|
input: part.state.input,
|
||||||
},
|
},
|
||||||
} satisfies MessageV2.ToolPart)
|
} satisfies MessageV2.ToolPart)
|
||||||
|
|||||||
@@ -344,6 +344,17 @@ function urls(text: string | undefined) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sessionLink(id: string | undefined, path: string, href?: (id: string) => string | undefined) {
|
||||||
|
if (!id) return
|
||||||
|
|
||||||
|
const direct = href?.(id)
|
||||||
|
if (direct) return direct
|
||||||
|
|
||||||
|
const idx = path.indexOf("/session")
|
||||||
|
if (idx === -1) return
|
||||||
|
return `${path.slice(0, idx)}/session/${id}`
|
||||||
|
}
|
||||||
|
|
||||||
const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"])
|
const CONTEXT_GROUP_TOOLS = new Set(["read", "glob", "grep", "list"])
|
||||||
const HIDDEN_TOOLS = new Set(["todowrite", "todoread"])
|
const HIDDEN_TOOLS = new Set(["todowrite", "todoread"])
|
||||||
|
|
||||||
@@ -1215,6 +1226,7 @@ function ToolFileAccordion(props: { path: string; actions?: JSX.Element; childre
|
|||||||
}
|
}
|
||||||
|
|
||||||
PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
||||||
|
const data = useData()
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const part = () => props.part as ToolPart
|
const part = () => props.part as ToolPart
|
||||||
if (part().tool === "todowrite" || part().tool === "todoread") return null
|
if (part().tool === "todowrite" || part().tool === "todoread") return null
|
||||||
@@ -1229,6 +1241,21 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
|||||||
const input = () => part().state?.input ?? emptyInput
|
const input = () => part().state?.input ?? emptyInput
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const partMetadata = () => part().state?.metadata ?? emptyMetadata
|
const partMetadata = () => part().state?.metadata ?? emptyMetadata
|
||||||
|
const taskId = createMemo(() => {
|
||||||
|
if (part().tool !== "task") return
|
||||||
|
const value = partMetadata().sessionId
|
||||||
|
if (typeof value === "string" && value) return value
|
||||||
|
})
|
||||||
|
const taskHref = createMemo(() => {
|
||||||
|
if (part().tool !== "task") return
|
||||||
|
return sessionLink(taskId(), useLocation().pathname, data.sessionHref)
|
||||||
|
})
|
||||||
|
const taskSubtitle = createMemo(() => {
|
||||||
|
if (part().tool !== "task") return undefined
|
||||||
|
const value = input().description
|
||||||
|
if (typeof value === "string" && value) return value
|
||||||
|
return taskId()
|
||||||
|
})
|
||||||
|
|
||||||
const render = createMemo(() => ToolRegistry.render(part().tool) ?? GenericTool)
|
const render = createMemo(() => ToolRegistry.render(part().tool) ?? GenericTool)
|
||||||
|
|
||||||
@@ -1248,7 +1275,15 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return <ToolErrorCard tool={part().tool} error={error()} defaultOpen={props.defaultOpen} />
|
return (
|
||||||
|
<ToolErrorCard
|
||||||
|
tool={part().tool}
|
||||||
|
error={error()}
|
||||||
|
defaultOpen={props.defaultOpen}
|
||||||
|
subtitle={taskSubtitle()}
|
||||||
|
href={taskHref()}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}}
|
}}
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
@@ -1625,25 +1660,14 @@ ToolRegistry.register({
|
|||||||
return raw[0]!.toUpperCase() + raw.slice(1)
|
return raw[0]!.toUpperCase() + raw.slice(1)
|
||||||
})
|
})
|
||||||
const title = createMemo(() => agentTitle(i18n, type()))
|
const title = createMemo(() => agentTitle(i18n, type()))
|
||||||
const description = createMemo(() => {
|
const subtitle = createMemo(() => {
|
||||||
const value = props.input.description
|
const value = props.input.description
|
||||||
if (typeof value === "string") return value
|
if (typeof value === "string" && value) return value
|
||||||
return undefined
|
return childSessionId()
|
||||||
})
|
})
|
||||||
const running = createMemo(() => props.status === "pending" || props.status === "running")
|
const running = createMemo(() => props.status === "pending" || props.status === "running")
|
||||||
|
|
||||||
const href = createMemo(() => {
|
const href = createMemo(() => sessionLink(childSessionId(), location.pathname, data.sessionHref))
|
||||||
const sessionId = childSessionId()
|
|
||||||
if (!sessionId) return
|
|
||||||
|
|
||||||
const direct = data.sessionHref?.(sessionId)
|
|
||||||
if (direct) return direct
|
|
||||||
|
|
||||||
const path = location.pathname
|
|
||||||
const idx = path.indexOf("/session")
|
|
||||||
if (idx === -1) return
|
|
||||||
return `${path.slice(0, idx)}/session/${sessionId}`
|
|
||||||
})
|
|
||||||
|
|
||||||
const titleContent = () => <TextShimmer text={title()} active={running()} />
|
const titleContent = () => <TextShimmer text={title()} active={running()} />
|
||||||
|
|
||||||
@@ -1653,7 +1677,7 @@ ToolRegistry.register({
|
|||||||
<span data-slot="basic-tool-tool-title" class="capitalize agent-title">
|
<span data-slot="basic-tool-tool-title" class="capitalize agent-title">
|
||||||
{titleContent()}
|
{titleContent()}
|
||||||
</span>
|
</span>
|
||||||
<Show when={description()}>
|
<Show when={subtitle()}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={href()}>
|
<Match when={href()}>
|
||||||
<a
|
<a
|
||||||
@@ -1662,11 +1686,11 @@ ToolRegistry.register({
|
|||||||
href={href()!}
|
href={href()!}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
{description()}
|
{subtitle()}
|
||||||
</a>
|
</a>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<span data-slot="basic-tool-tool-subtitle">{description()}</span>
|
<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -10,19 +10,22 @@ export interface ToolErrorCardProps extends Omit<ComponentProps<typeof Card>, "c
|
|||||||
tool: string
|
tool: string
|
||||||
error: string
|
error: string
|
||||||
defaultOpen?: boolean
|
defaultOpen?: boolean
|
||||||
|
subtitle?: string
|
||||||
|
href?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToolErrorCard(props: ToolErrorCardProps) {
|
export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||||
const i18n = useI18n()
|
const i18n = useI18n()
|
||||||
const [open, setOpen] = createSignal(props.defaultOpen ?? false)
|
const [open, setOpen] = createSignal(props.defaultOpen ?? false)
|
||||||
const [copied, setCopied] = createSignal(false)
|
const [copied, setCopied] = createSignal(false)
|
||||||
const [split, rest] = splitProps(props, ["tool", "error", "defaultOpen"])
|
const [split, rest] = splitProps(props, ["tool", "error", "defaultOpen", "subtitle", "href"])
|
||||||
const name = createMemo(() => {
|
const name = createMemo(() => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
read: "ui.tool.read",
|
read: "ui.tool.read",
|
||||||
list: "ui.tool.list",
|
list: "ui.tool.list",
|
||||||
glob: "ui.tool.glob",
|
glob: "ui.tool.glob",
|
||||||
grep: "ui.tool.grep",
|
grep: "ui.tool.grep",
|
||||||
|
task: "Task",
|
||||||
webfetch: "ui.tool.webfetch",
|
webfetch: "ui.tool.webfetch",
|
||||||
websearch: "ui.tool.websearch",
|
websearch: "ui.tool.websearch",
|
||||||
codesearch: "ui.tool.codesearch",
|
codesearch: "ui.tool.codesearch",
|
||||||
@@ -32,6 +35,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
|||||||
}
|
}
|
||||||
const key = map[split.tool]
|
const key = map[split.tool]
|
||||||
if (!key) return split.tool
|
if (!key) return split.tool
|
||||||
|
if (!key.includes(".")) return key
|
||||||
return i18n.t(key)
|
return i18n.t(key)
|
||||||
})
|
})
|
||||||
const cleaned = createMemo(() => split.error.replace(/^Error:\s*/, "").trim())
|
const cleaned = createMemo(() => split.error.replace(/^Error:\s*/, "").trim())
|
||||||
@@ -43,6 +47,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const subtitle = createMemo(() => {
|
const subtitle = createMemo(() => {
|
||||||
|
if (split.subtitle) return split.subtitle
|
||||||
const parts = tail().split(": ")
|
const parts = tail().split(": ")
|
||||||
if (parts.length <= 1) return "Failed"
|
if (parts.length <= 1) return "Failed"
|
||||||
const head = (parts[0] ?? "").trim()
|
const head = (parts[0] ?? "").trim()
|
||||||
@@ -77,7 +82,19 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
|||||||
<div data-slot="basic-tool-tool-info-structured">
|
<div data-slot="basic-tool-tool-info-structured">
|
||||||
<div data-slot="basic-tool-tool-info-main">
|
<div data-slot="basic-tool-tool-info-main">
|
||||||
<span data-slot="basic-tool-tool-title">{name()}</span>
|
<span data-slot="basic-tool-tool-title">{name()}</span>
|
||||||
<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>
|
<Show
|
||||||
|
when={split.href && split.subtitle}
|
||||||
|
fallback={<span data-slot="basic-tool-tool-subtitle">{subtitle()}</span>}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
data-slot="basic-tool-tool-subtitle"
|
||||||
|
class="clickable subagent-link"
|
||||||
|
href={split.href!}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
{subtitle()}
|
||||||
|
</a>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user