Skip to content

Commit 4ffada7

Browse files
ThomasK33claude
andcommitted
docs(agent): guard and bound every rule-4 fragment; make errorText fully total
Convergence review of the previous commit: - README rule 4: the settle fragment now carries the AbortSignal third arg (its own bold sentence mandates bounding every recovery request), and the prose spells out the agent.chatId guard (undefined before the first turn) instead of prescribing a fragment that neither typechecks nor no-ops safely. - errorText: an Error subclass with a throwing `message` getter could escape the try blocks — the instanceof branch is now wrapped too, so the totality contract holds. Change-Id: I8580e2de0d4326f29fc40cccbbd4625df21803f5 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Thomas Kosiewski <tk@coder.com>
1 parent 15fa2d9 commit 4ffada7

2 files changed

Lines changed: 11 additions & 5 deletions

File tree

packages/agent/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -364,15 +364,17 @@ Rules that keep it robust — each guards against a failure mode observed live:
364364
4. **Settle a turn that stopped on a tool call.** If the loop stops on a
365365
tool‑call step — e.g. your `stopWhen` ceiling lands exactly on the
366366
`structured_output` call (`finishReason: "tool-calls"`) — the tool results
367-
ran locally but never reached the server. Submit the stranded step's
368-
(`result.steps.at(-1)`) locally‑executed client outcomes directly via
369-
`agent.client.submitToolResults(agent.chatId, { results: [{ tool_call_id, output, is_error }] })`
367+
ran locally but never reached the server. Guard on `agent.chatId` (it is
368+
`undefined` until the first turn creates the chat), then submit the
369+
stranded step's (`result.steps.at(-1)`) locally‑executed client outcomes
370+
directly via
371+
`agent.client.submitToolResults(chatId, { results: [{ tool_call_id, output, is_error }] }, AbortSignal.timeout(8_000))`
370372
before touching the chat again, or it strands as in rule 1. Read the
371373
outcomes off the step's **content parts**: a `tool-result` part is a
372374
success, a `tool-error` part (the tool's `execute` threw) must be submitted
373375
with `is_error: true` — mirroring what the resume path would have sent. If
374376
a pending call has no local outcome (or the submit fails), end the stranded
375-
turn with `agent.client.interruptChat(agent.chatId, AbortSignal.timeout(8_000))`
377+
turn with `agent.client.interruptChat(chatId, AbortSignal.timeout(8_000))`
376378
instead. **Bound every one of these recovery requests with an
377379
`AbortSignal`** — they target a server that may already be stalled, and the
378380
bare `agent.interrupt()` / `agent.archive()` helpers carry no timeout. A

packages/agent/examples/06-structured-output.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ function isAborted(err: unknown): boolean {
4444
function errorText(err: unknown): string {
4545
if (err == null) return "unknown error";
4646
if (typeof err === "string") return err;
47-
if (err instanceof Error) return err.message;
47+
try {
48+
if (err instanceof Error && typeof err.message === "string") return err.message;
49+
} catch {
50+
// pathological `message` getter — fall through
51+
}
4852
try {
4953
const json = JSON.stringify(err); // undefined for symbols/functions
5054
if (typeof json === "string") return json;

0 commit comments

Comments
 (0)