diff --git a/packages/core/src/tool/PLAN.md b/packages/core/src/tool/PLAN.md index 232c15c057..52cf723d3d 100644 --- a/packages/core/src/tool/PLAN.md +++ b/packages/core/src/tool/PLAN.md @@ -2,9 +2,9 @@ ## Status -This document describes the agreed target design for V2 tools. It is an implementation plan, not documentation for the current API. +Implemented on this branch. This document is the historical plan kept for reviewer context; the authoritative semantic overview is [`specs/v2/tools.md`](../../../../specs/v2/tools.md), and where the two disagree the spec and code win. Delete this file at merge. -The current semantic overview remains in [`specs/v2/tools.md`](../../../../specs/v2/tools.md) until this plan is implemented. The plan's base is `origin/v2` at `b91dd78ab3`. Progress-event ephemerality already landed there (`5a9ed4d350`, `fix: make tool progress live-only`), including the failed-event partial snapshot this plan builds on. Delta-compressed progress is a later follow-up. +The plan's base was `origin/v2` at `b91dd78ab3`. Progress-event ephemerality already landed there (`5a9ed4d350`, `fix: make tool progress live-only`), including the failed-event partial snapshot this plan builds on. Delta-compressed progress is a later follow-up. ## Reader And Job diff --git a/packages/core/src/tool/glob.ts b/packages/core/src/tool/glob.ts index adf2a8f4d8..093b3aee47 100644 --- a/packages/core/src/tool/glob.ts +++ b/packages/core/src/tool/glob.ts @@ -8,7 +8,7 @@ import { FileSystem } from "../filesystem" import { FSUtil } from "@opencode-ai/util/fs-util" import { Location } from "../location" import { Ripgrep } from "../ripgrep" -import { NonNegativeInt, RelativePath } from "../schema" +import { RelativePath } from "../schema" import { PermissionV2 } from "../permission" import { Tool } from "./tool" diff --git a/packages/core/src/tool/grep.ts b/packages/core/src/tool/grep.ts index 0c4b793dc4..83dd65557e 100644 --- a/packages/core/src/tool/grep.ts +++ b/packages/core/src/tool/grep.ts @@ -9,7 +9,7 @@ import { FSUtil } from "@opencode-ai/util/fs-util" import { Location } from "../location" import { PermissionV2 } from "../permission" import { Ripgrep } from "../ripgrep" -import { NonNegativeInt, RelativePath } from "../schema" +import { RelativePath } from "../schema" import { Tool } from "./tool" export const name = "grep" diff --git a/packages/plugin/src/v2/effect/tool.ts b/packages/plugin/src/v2/effect/tool.ts index 2cfb593cfb..39e716cbc6 100644 --- a/packages/plugin/src/v2/effect/tool.ts +++ b/packages/plugin/src/v2/effect/tool.ts @@ -95,15 +95,12 @@ export type Definition, Output extends SchemaType< * Optional model projection. Receives the typed domain output. When absent, an * encoded string becomes text and any other encoded JSON is serialized once. */ - readonly toModelOutput?: (input: { + readonly toModelOutput?: (call: { readonly input: InputValue readonly output: OutputValue }) => ModelOutput /** Optional compact UI metadata. Receives the typed domain output. Absent means absent: no defaults. */ - readonly toMetadata?: (input: { - readonly input: InputValue - readonly output: OutputValue - }) => Metadata + readonly toMetadata?: (call: { readonly input: InputValue; readonly output: OutputValue }) => Metadata } /** A dynamic tool's split result: a machine value for Code Mode and content for the model. */ diff --git a/packages/schema/src/session-event.ts b/packages/schema/src/session-event.ts index 74c2d2e385..e7fe0ee208 100644 --- a/packages/schema/src/session-event.ts +++ b/packages/schema/src/session-event.ts @@ -414,6 +414,9 @@ export namespace Tool { type: "session.tool.progress", schema: { ...ToolBase, + // Deliberately looser than terminal metadata (JSON-only): live snapshots + // are ephemeral, and only the JSON-validated subset survives into a + // durable failure snapshot. metadata: Schema.Record(Schema.String, Schema.Unknown), content: Schema.Array(ToolContent), }, diff --git a/specs/v2/tools.md b/specs/v2/tools.md index e555d38245..2043757db1 100644 --- a/specs/v2/tools.md +++ b/specs/v2/tools.md @@ -34,14 +34,13 @@ Every local tool receives the same concrete invocation context: interface Tool.Context { readonly sessionID: Session.ID readonly agent: Agent.ID - readonly assistantMessageID: SessionMessage.ID - readonly toolCallID: string + readonly messageID: SessionMessage.ID + readonly callID: string + readonly progress: (update: Progress) => Effect.Effect } ``` -`assistantMessageID` is the durable ID of the assistant message containing the call. The Session runner owns this association and supplies the complete context to the registry; the registry does not infer it. - -Durable events call the invocation identifier `callID`; `Tool.Context.toolCallID` is the same value at the executor boundary. +`messageID` is the durable ID of the assistant message containing the call. The Session runner owns this association and supplies the complete context to the registry; the registry does not infer it. `callID` carries the same invocation identifier durable events use. Decoded tool input is passed separately to `execute`. Raw provider input and domain services do not belong in the invocation context. @@ -105,8 +104,8 @@ yield * agent: context.agent, source: { type: "tool", - messageID: context.assistantMessageID, - callID: context.toolCallID, + messageID: context.messageID, + callID: context.callID, }, action: "grep", resources: [input.pattern], @@ -160,7 +159,7 @@ Outcomes remain distinct: - `ToolFailure` is an expected model-visible failure. - Interruption cancels the invocation and is not a tool result. - Unexpected typed errors and defects follow the runner's operational failure policy. -- Unknown and invalid calls become explicit model-visible settlement errors without invoking a handler. +- Unknown and invalid calls become explicit model-visible execution errors without invoking a handler. Leaf tools translate only errors they deliberately classify as recoverable. Broad cause-catching around an executor is invalid because it consumes interruption and defects.