diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0d0af95..22d31f8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -21,18 +21,18 @@ Agent state. Streams reduce latency but never become recovery state. ## Package ownership -| Package | Owns | Must not own | -| ---------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------ | -| `agent` | Public constructors, stable application types, model/tool extension interfaces | Private Dex descriptors, provider protocols, process lifecycle | -| `model` | Public built-in provider adapters, router, and process-memory credentials | Dex resources, provider protocol implementation, process lifecycle | -| `internal/agent` | Domain IDs/enums, Flow graph, private Dex descriptors, command client | Provider protocols, HTTP transport models, global configuration | -| `internal/api` | ogen implementation, validation mapping, problem responses | Handwritten routes, generated-model duplicates, durable state | -| `internal/app` | Dependency construction, goroutine ownership, startup and shutdown | Domain decisions or provider-specific payloads | -| `internal/config` | Environment parsing and validated immutable sections | Runtime singletons or secret logging | -| `internal/model` | Provider routing, protocol adapters, in-memory credential lookup | Dex resources or HTTP API responses | -| `internal/mcp` | Trusted server config, discovery, policy, single-attempt sessions, brokers | Agent state transitions, retry loops, or exported Dex resources | -| `web` | React portal and generated Fetch client | Handwritten API response types or durable-state reconstruction | -| `web/packages/superagent-ui` | Transport-free React conversation components, timeline merge/sort, ToolCall cards, and local interaction behavior | Dex/API clients, routing, durable state, or product workflows | +| Package | Owns | Must not own | +| ---------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `agent` | Public constructors, stable application types, model/tool extension interfaces | Private Dex descriptors, provider protocols, process lifecycle | +| `model` | Public built-in provider adapters, router, and process-memory credentials | Dex resources, provider protocol implementation, process lifecycle | +| `internal/agent` | Domain IDs/enums, Flow graph, private Dex descriptors, command client | Provider protocols, HTTP transport models, global configuration | +| `internal/api` | ogen implementation, validation mapping, problem responses | Handwritten routes, generated-model duplicates, durable state | +| `internal/app` | Dependency construction, goroutine ownership, startup and shutdown | Domain decisions or provider-specific payloads | +| `internal/config` | Environment parsing and validated immutable sections | Runtime singletons or secret logging | +| `internal/model` | Provider routing, protocol adapters, in-memory credential lookup | Dex resources or HTTP API responses | +| `internal/mcp` | Trusted server config, discovery, policy, single-attempt sessions, brokers | Agent state transitions, retry loops, or exported Dex resources | +| `web` | React portal and generated Fetch client | Handwritten API response types or durable-state reconstruction | +| `web/packages/superagent-ui` | Transport-free React conversation components, timeline merge/sort, ToolCall cards, and local interaction behavior | Dex/API clients, routing, durable state, or product workflows | Interfaces live at their consuming boundary. Concrete single-use components do not receive speculative interfaces, and there is no general-purpose helpers @@ -226,6 +226,8 @@ Stream loss is corrected by Snapshot. Manual tool recovery moves programmatic focus to its labelled warning region. The heading remains the accessible label without becoming a focused control. +Affected calls use bounded argument disclosures and explicit decision cards so +large payloads cannot dominate the pending-action surface. Consumed user bubbles are anchored after the latest explicit message sequence seen before the consumption event. The event's own message sequence associates diff --git a/web/packages/superagent-ui/README.md b/web/packages/superagent-ui/README.md index 8f265cc..f7b190d 100644 --- a/web/packages/superagent-ui/README.md +++ b/web/packages/superagent-ui/README.md @@ -94,6 +94,10 @@ message reveals its local timestamp below the content. appears. The existing warning card communicates that focus without drawing a browser-default input outline around its heading. +Each affected call uses a compact card with formatted, height-bounded arguments +and two explicit recovery choices. The disclosure keeps a visible inset focus +indicator without resembling a text input. + Completed model and tool duration is `createdAt - startedAt`. Missing or negative legacy timing is omitted. Running durations update once per second but are excluded from live-region announcements. Stream timestamps remain ordering diff --git a/web/packages/superagent-ui/src/ToolRecoveryPanel.test.tsx b/web/packages/superagent-ui/src/ToolRecoveryPanel.test.tsx index 7153c9a..0a595f3 100644 --- a/web/packages/superagent-ui/src/ToolRecoveryPanel.test.tsx +++ b/web/packages/superagent-ui/src/ToolRecoveryPanel.test.tsx @@ -39,7 +39,7 @@ describe("ToolRecoveryPanel", () => { screen.getByRole("region", { name: "Execution outcome is unknown" }), ).toHaveFocus(); const continueOptions = screen.getAllByRole("radio", { - name: "Continue with unknown result", + name: /Continue with unknown result/, }); const secondContinueOption = continueOptions.at(1); if (secondContinueOption === undefined) { @@ -93,4 +93,32 @@ describe("ToolRecoveryPanel", () => { ).toBeDisabled(); expect(onResolve).not.toHaveBeenCalled(); }); + + it("formats valid arguments and preserves malformed payloads", () => { + render( + , + ); + + expect(screen.getByText(/\{\s+"path": "a\.txt"\s+\}/)).toBeInTheDocument(); + expect(screen.getByText("not-json")).toBeInTheDocument(); + }); }); diff --git a/web/packages/superagent-ui/src/ToolRecoveryPanel.tsx b/web/packages/superagent-ui/src/ToolRecoveryPanel.tsx index e3b9048..ac11e22 100644 --- a/web/packages/superagent-ui/src/ToolRecoveryPanel.tsx +++ b/web/packages/superagent-ui/src/ToolRecoveryPanel.tsx @@ -86,46 +86,80 @@ export function ToolRecoveryPanel({ keeps the same call ID, but the MCP server may not deduplicate it.

- {recovery.calls.map((call) => ( -
- {call.toolName} - Error type: {call.errorType} -
- Arguments -
{call.argumentsJson}
-
- - -
- ))} + {recovery.calls.map((call, index) => { + const callHeadingID = `${headingID}-call-${String(index)}`; + const retryInputID = `${callHeadingID}-retry`; + const continueInputID = `${callHeadingID}-continue`; + const isRetrySelected = selections[call.callId] === "retry"; + return ( +
+
+

{call.toolName}

+ + Error · {call.errorType} + +
+
+ + Arguments + + +
{formatArguments(call.argumentsJson)}
+
+
+ + +
+
+ ); + })}