Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/opencode/src/altimate/telemetry/error-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,24 @@ export function describeToolError(raw: string) {
}
}

/**
* A running tool that the processor closed because the session's own abort signal fired
* (Stop/Esc, dispose) is a requested cancellation, already recorded as the session's
* "aborted" outcome, not a tool failure. Interruptions the session did not request
* (signal not aborted) and every real tool error are still fingerprinted.
*/
export function isRequestedToolInterruption(
state: { status: string; error?: unknown; metadata?: Record<string, unknown> },
sessionAborted: boolean,
): boolean {
return (
sessionAborted &&
state.status === "error" &&
state.error === "Tool execution aborted" &&
state.metadata?.interrupted === true
)
}

export function buildErrorFingerprintEvent(
sessionID: string,
err: {
Expand Down
2 changes: 2 additions & 0 deletions packages/opencode/src/session/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2241,6 +2241,8 @@ export namespace SessionPrompt {
lastToolName = part.tool
// altimate_change end
if (toolChain.length < 50) toolChain.push(part.tool)
// altimate_change — a requested abort closing a running tool is not a tool failure
if (ErrorDetail.isRequestedToolInterruption(part.state, abort.aborted)) continue
const isError = part.state?.status === "error"
if (isError) {
toolErrorCount++
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { describe, expect, test } from "bun:test"
import { ErrorDetail } from "../../src/altimate/telemetry/error-detail"

// Shape written by processor.ts interruptedToolPart when a running tool is closed on abort.
const interrupted = {
status: "error",
input: { filePath: "/tmp/x.sql" },
error: "Tool execution aborted",
metadata: { interrupted: true },
time: { start: 1, end: 2 },
}

describe("error_fingerprint: requested tool interruption", () => {
test("the interrupted-tool error is the reported 'tool <x> aborted' fingerprint", () => {
expect(ErrorDetail.describeToolError(interrupted.error).errorMessage).toBe("tool <x> aborted")
})

test("a tool closed by the session's own abort is not a tool failure", () => {
expect(ErrorDetail.isRequestedToolInterruption(interrupted, true)).toBe(true)
})

test("an interruption the session did not request is still reported", () => {
expect(ErrorDetail.isRequestedToolInterruption(interrupted, false)).toBe(false)
})

test("real tool errors are still reported during an aborted session", () => {
expect(
ErrorDetail.isRequestedToolInterruption(
{ status: "error", error: "File not found: /tmp/x.sql", metadata: {} },
true,
),
).toBe(false)
expect(
ErrorDetail.isRequestedToolInterruption({ status: "error", error: "Tool execution aborted", metadata: {} }, true),
).toBe(false)
expect(ErrorDetail.isRequestedToolInterruption({ status: "completed", metadata: { interrupted: true } }, true)).toBe(
false,
)
})
})
Loading