@tangle-network/agent-app 0.43.53 → 0.43.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,8 @@ The application-shell layer for building agent products on the Tangle stack.
8
8
 
9
9
  The substrate packages — `@tangle-network/agent-runtime`, `agent-eval`, `agent-integrations`, `tcloud`, `sandbox` — are the **engine**. This package is the **shell**: the chat tool-loop, the structured agent→app side channel, the integration-hub client, per-workspace billing, field crypto, and the web boundary utilities that every agent app otherwise rewrites by hand. You supply your domain through typed seams; the package supplies the mechanism and imports none of your code.
10
10
 
11
+ **Who it's for:** engineers building an agent product on the Tangle sandbox — a chat app, a copilot, an autonomous worker — who want the shell (chat routes, streaming, durability, approvals, billing, the tool side channel) as composable pieces instead of a per-app rewrite. It is **not** an agent framework or a model SDK: the reasoning lives in the sandbox agent; agent-app is everything around it — the turn plumbing, durability, and money.
12
+
11
13
  ## Highlights
12
14
 
13
15
  - **Structured tool side channel** — `submit_proposal` (approval-gated), `schedule_followup`, `render_ui`, `add_citation`, exposed as validated tool calls over three surfaces (HTTP route, per-turn MCP server, agent-runtime executor). No fenced-text parsing.
@@ -109,43 +111,47 @@ One rule decides where anything lives:
109
111
 
110
112
  Everything here is reached through a typed seam — `AppToolHandlers`, `AppToolTaxonomy`, `streamTurn`, `executeToolCall`, `verifyToken`, `KeyProvisioner` / `WorkspaceKeyStore` / `KeyCrypto`. The package never imports product code and never hard-codes a domain value (a proposal type, a premium, a disclaimer); each is a parameter. New capability arrives as a new subpath, never a breaking change to an existing one.
111
113
 
114
+ ## Choosing a path
115
+
116
+ Three decisions cover most of the surface.
117
+
118
+ **1. How does the turn run?** Pick the transport by who's watching, not by feature.
119
+
120
+ | Your turn | Use | Why |
121
+ |---|---|---|
122
+ | **Interactive** — a user is watching a chat or copilot | `streamPrompt` held open for the turn; the sandbox gateway lets the browser attach directly | Worker lifetime ≈ turn length; a dropped tab replays the buffered tail on reconnect. |
123
+ | **Autonomous** — a mission step, queue job, cron, or inbound email, with nobody watching | `dispatchPrompt({ detach: true })` + poll from a durable driver. `runDetachedTurn` (`/chat-routes`) bridges that detached run into the live buffer, so a browser opening the session mid-run still tails it token-by-token | No consumer exists and Workers die in minutes; the platform runs the turn server-side and a crash re-dispatch is a lookup, not a second run. |
124
+ | **Eval / CI** — a long-lived harness process | `runAppToolLoop` / `streamPrompt` in-process | The process outlives the run; durability adds nothing — a failed run is re-run, not resumed. |
125
+
126
+ **2. Assembled or à la carte?** `createChatTurnRoutes` (`/chat-routes`) wires the whole server chat turn — auth, store, streaming, replay, uploads, interactions — over typed seams. Reach for the individual modules (`/stream`, `/chat-store`, `/interactions`) only to compose something the assembled route doesn't cover.
127
+
128
+ **3. Sandbox or sandbox-free?** The tools, billing, eval, and loop all work without a container: `createOpenAICompatStreamTurn` maps any OpenAI-compatible endpoint into the loop for a browser or edge copilot. Reach for `/sandbox` only when the turn needs a real container — bash, files, sub-agents, MCP.
129
+
112
130
  ## Modules
113
131
 
114
- Each is an independent entry point — import only what you use.
115
-
116
- | Subpath | What it gives you |
117
- |---|---|
118
- | [`/tools`](src/tools) | The structured agent→app side channel: `buildAppToolOpenAITools`, `createAppToolRuntimeExecutor`, `handleAppToolRequest` (HTTP), `buildAppToolMcpServer` / `buildHttpMcpServer` (MCP), `createCapabilityToken` + `authenticateToolRequest` (capability auth), `ToolInputError`. |
119
- | [`/runtime`](src/runtime) | `runAppToolLoop` / `streamAppToolLoop` (bounded tool loop), `resolveTangleModelConfig` (Tangle Router / Anthropic BYOK), and `toLoopEvents` / `createOpenAICompatStreamTurn` (OpenAI-compat stream → loop events, with fragmented tool-call args reassembled). |
120
- | [`/integrations`](src/integrations) | Integration-hub client: `HubExecClient`, `resolveIntegrationAction`, `invokeIntegrationHub`. Composes `@tangle-network/agent-integrations`. |
121
- | [`/eval`](src/eval) | `producedFromToolEvents` (bridge tool events into the eval verifier) and `createTokenRecallChecker` (deterministic content check). Re-exports `@tangle-network/agent-eval`'s `verifyCompletion`, `extractProducedState`, `weightedComposite`, `createLlmCorrectnessChecker`. |
122
- | [`/tangle`](src/tangle) | App-registration consent URL (`buildConsentUrl`) and a cached, auto-refreshing broker-token provider (`createBrokerTokenProvider`). Structural over the tcloud client. |
123
- | [`/billing`](src/billing) | `createWorkspaceKeyManager` mint / rotate / roll over / report usage on per-workspace, budget-capped model keys. Seams for provisioner, store, and crypto. |
124
- | [`/crypto`](src/crypto) | AES-GCM field encryption: `encryptAesGcm`, `decryptAesGcm`, `createFieldCrypto`. Key supplied by the caller. |
125
- | [`/missions`](src/missions) | Durable multi-step mission orchestration over a `MissionStorePort` seam: guarded status/step machine, idempotent plan engine with budget/approval gates, `:::mission` parser, the client-safe live-event reducer, and the canonical `StepAgentActivity` per-step delegated-run lane. |
126
- | [`/trace`](src/trace) | Flow observability: `buildFlowTrace` + ASCII `renderWaterfall`/`renderHistogram`; the mission trace bridge (`createMissionTraceContext`, `childSpanContext`, `traceEnv`) whose ids/env a delegated run inherits; and delegation→FlowSpan converters (`delegationActivityToFlowSpans`, `loopTraceEventsToFlowSpans`, `composeMissionFlowTrace`). |
127
- | [`/web-react`](src/web-react) | Router-safe React chat components: `ChatComposer`, `ModelPicker`, `EffortPicker`, `ChatMessages`, `RunDrillIn`, plus observability surfaces `MissionActivityLane`, `AgentActivityPanel`, `FlowWaterfall`. This path must not import sandbox-only UI. |
128
- | [`/composer`](src/composer) | Sandbox-first `AgentComposer` and profile/model/sandbox-runner controls re-exported from `@tangle-network/sandbox-ui/chat`. Use when the chat owns a sandbox profile or needs the full sandbox composer. |
129
- | `/web-react/terminal` | Sandbox terminal React components, including `WorkspaceTerminalPanel`. Import this explicit path only for container/terminal views. |
130
- | [`/web`](src/web) | Request-boundary utilities: `parseJsonObjectBody`, `requireString`, `extractRequestContext`, `checkRateLimit`, `addSecurityHeaders`. |
131
- | [`/stream`](src/stream) | SSE normalization and turn identity: `normalizeToolEvent`, `resolveChatTurn`, `encodeEvent`, message-part merging. |
132
- | [`/redact`](src/redact) | `redactForIngestion` — PII redaction before content leaves the boundary. |
133
-
134
- **The chat vertical** the assembled server chat stack. Wire it with [`examples/chat-app.md`](./examples/chat-app.md).
135
-
136
- | Subpath | What it gives you |
137
- |---|---|
138
- | [`/chat-routes`](src/chat-routes) | `createChatTurnRoutes` — the assembled turn vertical: NDJSON `turn` + buffered `replay` + `running` reconnect-discovery + composed `interactions` answer endpoints, over `authorize` / `produce` / `store` seams. Plus `createSandboxChatProducer`, `createUploadRoute`, `createSandboxFileIndexRoute`, `withDurableChatProjection`, and the import-free `./wire` contract (`chatTurnRequestInit`, `ChatTurnRequestPayload`). Composes agent-runtime's `handleChatTurn`; subpath-only (not re-exported from the root). |
139
- | [`/chat-store`](src/chat-store) | `createChatTables` + `createChatStore` — drizzle thread/message persistence behind the `ChatStore` port, and the canonical `ChatMessagePart` parts vocabulary (`toChatMessageParts`, part guards). The drizzle store/schema is subpath-only. |
140
- | [`/interactions`](src/interactions) | Human-in-the-loop ask channel: `createInteractionAnswerRoute` (list/answer endpoint factory — validation, 410 mapping, duplicate-answer safety), the server sidecar client, and the shared `ChatInteraction` wire/persisted-part contract + codecs. Composes `@tangle-network/agent-interface` types. |
141
- | [`/durable-chat`](src/durable-chat) | Durable plan/question workflow around the authoritative channels: `createDurablePlanRoutes`, `createDurableChatScope`, `createDurableChatEventProjection`, `createDurableInteractionRoutePersistence`, the `DurablePlanStore` port, and `InMemoryDurableChatStateStore` (tests/demos only — production supplies the store). |
142
- | [`/plans`](src/plans) | Browser-safe durable-plan chat projection: `parsePlanSubmittedEvent`, `planToPersistedPart` / `persistedPartToPlan`, `canTransitionPlanStatus`, and the `ChatPlan` union. Byte-matches the SDK's `SandboxSession.plan()`. |
143
- | [`/object-store`](src/object-store) | Content-addressed durable attachment store: the `ObjectStore` port + `createR2ObjectStore`, `signObjectUrl` / `verifyObjectUrl`, `createProxiedArtifactRoute`, `objectKey`. |
144
- | [`/app-auth`](src/app-auth) | `createAppAuth` — better-auth config factory + request guards (`requireApiUser`) over the standard users/sessions/accounts/verifications tables; composes `/platform`'s SSO cookie minter. Optional `better-auth` peer; subpath-only. |
145
- | [`/sandbox`](src/sandbox) | Workspace sandbox provisioning + turn streaming: `ensureWorkspaceSandbox` / `peekWorkspaceSandbox`, `streamSandboxPrompt` / `runSandboxPrompt`, `createWorkspaceSandboxManager`, and terminal / runtime-proxy handlers. Peer `@tangle-network/sandbox`; subpath-only. |
146
- | [`/platform`](src/platform) | Tangle platform glue: cross-site SSO (`createTangleSsoHandlers`), request guards (`createAuthGuard`, `guardResolution`), the hub proxy, and seat billing. |
147
-
148
- The root entry (`@tangle-network/agent-app`) re-exports every module, but importing the subpath keeps your bundle to what you use.
132
+ Each subpath is an independent entry point — import only what you use; the root re-exports everything, but a subpath import keeps your bundle to what you touch.
133
+
134
+ The **complete, always-current reference** every published subpath, its exported symbols, and its internal dependencies — is generated into **[`docs/CODEMAP.md`](./docs/CODEMAP.md)** and kept honest by a CI check (regenerate with `pnpm docs:gen`). Start with the core entry points:
135
+
136
+ **Run a turn**
137
+ - [`/tools`](src/tools) the structured agent→app side channel (proposals, follow-ups, citations, UI) as validated tool calls, over HTTP / MCP / runtime-executor surfaces.
138
+ - [`/runtime`](src/runtime) the bounded tool loop; the same loop drives a sandbox agent, a Worker, or an in-browser copilot behind one `streamTurn` seam.
139
+
140
+ **The server chat vertical** ([`examples/chat-app.md`](./examples/chat-app.md))
141
+ - [`/chat-routes`](src/chat-routes) `createChatTurnRoutes`: auth store streaming turn with buffered replay uploads sidecar question answering, assembled. Plus `runDetachedTurn` for autonomous turns a browser can still watch live.
142
+ - [`/chat-store`](src/chat-store) · [`/interactions`](src/interactions) · [`/durable-chat`](src/durable-chat) · [`/plans`](src/plans) persistence, human-in-the-loop asks, and the durable plan/question workflow around them.
143
+
144
+ **On the sandbox**
145
+ - [`/sandbox`](src/sandbox) — workspace provisioning + turn streaming.
146
+ - [`/missions`](src/missions) durable multi-step orchestration: sequencing, budgets, approval gates, schedules.
147
+
148
+ **React surfaces**
149
+ - [`/web-react`](src/web-react) router-safe chat + observability components (never imports sandbox-only UI); [`/composer`](src/composer) when the chat owns a full sandbox profile.
150
+
151
+ **Utilities (zero-dependency)**
152
+ - [`/web`](src/web) · [`/stream`](src/stream) · [`/crypto`](src/crypto) · [`/redact`](src/redact) — request boundary, SSE normalization, field crypto, PII redaction.
153
+
154
+ See **[`docs/CODEMAP.md`](./docs/CODEMAP.md)** for the rest `/billing`, `/tangle`, `/object-store`, `/trace`, `/theme`, `/eval`, `/app-auth`, `/platform`, and more.
149
155
 
150
156
  ### Missions: id shape and product columns
151
157
 
@@ -4,13 +4,13 @@ import { ToolDetailRenderers, ChatUiMessage } from '../web-react/index.js';
4
4
  import '../contract-KfqJh_au.js';
5
5
  import '@tangle-network/agent-interface';
6
6
  import '../plans/index.js';
7
- import '../parts-IB-Kbb7z.js';
7
+ import '../parts-1sRYSPjF.js';
8
8
  import '../agent-activity-C8ZG0F0M.js';
9
9
  import '../flow-types-Cb_AblZs.js';
10
10
  import '../sandbox-terminal-BIIC__CP.js';
11
11
  import '../catalog/index.js';
12
12
  import '../harness/index.js';
13
- import '../attachment-validation-DX2KIzMC.js';
13
+ import '../attachment-validation-D0csjCvz.js';
14
14
  import '../stream-normalizer-DWvtmY6F.js';
15
15
 
16
16
  /**
@@ -3,11 +3,11 @@ import {
3
3
  ChatMessages,
4
4
  ModelPicker,
5
5
  ProviderLogo
6
- } from "../chunk-EIG7ZQW2.js";
6
+ } from "../chunk-RVUISYUE.js";
7
7
  import "../chunk-65P3HJY3.js";
8
- import "../chunk-3EKOSBYL.js";
8
+ import "../chunk-K63OWUDJ.js";
9
9
  import "../chunk-2QI7XV2T.js";
10
- import "../chunk-JGYOYY5D.js";
10
+ import "../chunk-XKBIYSSM.js";
11
11
  import "../chunk-5MG74GVQ.js";
12
12
  import "../chunk-XAWFPMAR.js";
13
13
  import "../chunk-SIXYZ2FB.js";
@@ -1,4 +1,4 @@
1
- import { K as FileMention } from './parts-IB-Kbb7z.js';
1
+ import { K as FileMention } from './parts-1sRYSPjF.js';
2
2
 
3
3
  /**
4
4
  * `createSandboxFileIndexRoute` — server side of `@`-file-mentions
@@ -1,6 +1,6 @@
1
- import { L as ChatTurnRequestPayload, M as ChatTurnPartInput, g as ChatMessagePart, N as ChatTurnFilePartInput, a as ChatAttachmentPart, C as ChatAttachmentKind, f as ChatMentionPart } from '../parts-IB-Kbb7z.js';
2
- export { O as ChatAttachmentInput, e as ChatMentionKind, P as ChatTurnInputError, Q as ChatTurnTextPartInput, R as DISPATCH_MAX_MEDIA_PARTS, T as DISPATCH_MAX_PARTS, U as DISPATCH_REQUEST_MAX_BYTES, V as DISPATCH_STRUCTURAL_RESERVE_BYTES, K as FileMention, W as FileMentionsToPartsOptions, X as INLINE_PARTS_MAX_BYTES, Y as MENTION_MAX_COUNT, Z as SandboxMentionPathCheck, _ as assertPromptPartsWithinCap, $ as base64WireLen, a0 as buildMentionPromptBlock, a1 as chatTurnRequestInit, a2 as fileMentionsToParts, a3 as formatBytes, a4 as mediaTypeForMentionPath, a5 as mentionKindForPath, a6 as parseChatTurnParts, a7 as parseFileMentions, a8 as promptPartsByteSize, a9 as validateSandboxMentionPath } from '../parts-IB-Kbb7z.js';
3
- export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-DX2KIzMC.js';
1
+ import { L as ChatTurnRequestPayload, M as ChatTurnPartInput, g as ChatMessagePart, N as ChatTurnFilePartInput, a as ChatAttachmentPart, C as ChatAttachmentKind, f as ChatMentionPart } from '../parts-1sRYSPjF.js';
2
+ export { O as ChatAttachmentInput, e as ChatMentionKind, P as ChatTurnInputError, Q as ChatTurnTextPartInput, R as DISPATCH_MAX_MEDIA_PARTS, T as DISPATCH_MAX_PARTS, U as DISPATCH_REQUEST_MAX_BYTES, V as DISPATCH_STRUCTURAL_RESERVE_BYTES, K as FileMention, W as FileMentionsToPartsOptions, X as INLINE_PARTS_MAX_BYTES, Y as MENTION_MAX_COUNT, Z as ProducerErrorEvent, _ as ProducerNoticeEvent, $ as ProducerPassthroughEvent, a0 as ProducerPassthroughEventType, a1 as ProducerReasoningEvent, a2 as ProducerTextEvent, a3 as ProducerToolCallEvent, a4 as ProducerToolResultEvent, a5 as ProducerUsageEvent, a6 as ProducerWireEvent, a7 as SandboxMentionPathCheck, a8 as assertPromptPartsWithinCap, a9 as base64WireLen, aa as buildMentionPromptBlock, ab as chatTurnRequestInit, ac as fileMentionsToParts, ad as formatBytes, ae as mediaTypeForMentionPath, af as mentionKindForPath, ag as parseChatTurnParts, ah as parseFileMentions, ai as promptPartsByteSize, aj as validateSandboxMentionPath } from '../parts-1sRYSPjF.js';
3
+ export { A as ALLOWED_ATTACHMENT_SNIFFED_MIMES, a as ATTACHMENT_ACCEPT, b as ATTACHMENT_MAX_COUNT, c as AttachmentTypeCheckResult, C as CreateSandboxFileIndexRouteOptions, F as FileIndexAuthorization, d as FileIndexCache, e as FileIndexReadyResponse, f as FileIndexResponse, g as FileIndexWarmingResponse, M as MAX_ATTACHMENT_TOTAL_BYTES, h as MAX_BINARY_ATTACHMENT_BYTES, i as MAX_TEXT_ATTACHMENT_BYTES, S as SandboxFileTreeSource, j as SandboxTreeFile, k as SandboxTreeResult, l as SniffResult, m as attachmentSizeErrorMessage, n as attachmentTotalSizeErrorMessage, o as checkAttachmentType, p as createSandboxFileIndexRoute, s as sanitizeAttachmentFileName, q as sniffBinary } from '../attachment-validation-D0csjCvz.js';
4
4
  import { ChatTurnIdentity, ChatTurnProducer } from '@tangle-network/agent-runtime';
5
5
  import { InteractionAnswerRoute, InteractionAnswerRouteOptions } from '../interactions/index.js';
6
6
  import { PersistedChatMessageForTurn } from '../stream/index.js';
@@ -335,7 +335,8 @@ declare function createChatTurnRoutes<TContext = void>(options: CreateChatTurnRo
335
335
  * the `ChatTurnProducer` shape agent-runtime's `handleChatTurn` consumes AND
336
336
  * the client vocabulary `/web-react`'s `dispatchChatStreamLine` already parses
337
337
  * (`text` / `reasoning` / `tool_call` / `tool_result` / `usage` /
338
- * `interaction`). Legal and tax each hand-rolled this mapping differently;
338
+ * `notice` / structured `error` / `interaction`). Legal and tax each hand-rolled
339
+ * this mapping differently;
339
340
  * this is that middle, composed from `/stream`'s normalizers — no new loop
340
341
  * logic, no SDK import (the event source is an injected `AsyncIterable`).
341
342
  *
@@ -376,11 +377,13 @@ interface SandboxChatProducerOptions {
376
377
  model?: string;
377
378
  /** Which ask kinds the product renders a card for. Anything else is
378
379
  * auto-declined (see `declineInteraction`) so the run never hangs in the
379
- * broker waiting on a card no client will show. Default: question/plan. */
380
+ * broker waiting on a card no client will show. Default: question/plan.
381
+ * Products with per-turn plan mode can close over it without another option:
382
+ * `(kind) => kind === 'question' || (kind === 'plan' && planEnabled)`. */
380
383
  isRenderableInteraction?: (kind: string) => boolean;
381
384
  /** Resolve a non-renderable ask (wire `respondToSessionInteraction` with the
382
- * session's sidecar connection). Without it, non-renderable asks are only
383
- * logged — the run stays blocked until the broker times out. */
385
+ * session's sidecar connection). Without it, a failure notice is emitted and
386
+ * the run stays blocked until the broker times out. */
384
387
  declineInteraction?: (id: string) => Promise<void>;
385
388
  /** Opt-in eager promotion of harness-emitted `file` parts. Unset, a `file`
386
389
  * part persists exactly as the harness sent it — a transient `url` (a
@@ -497,8 +500,8 @@ interface DetachedTurnOptions {
497
500
  }
498
501
  interface DetachedTurnResult {
499
502
  /** `completed` — clean drain: persist + bill. `failed` — a terminal error
500
- * EVENT in the stream: skip billing, render an error row. (A stream that
501
- * THROWS re-throws out of `runDetachedTurn` instead of returning here.) */
503
+ * event, including the producer's structured `sandbox.stream_failed` event
504
+ * when the raw sandbox stream throws: skip billing, render an error row. */
502
505
  state: 'completed' | 'failed';
503
506
  text: string;
504
507
  /** The structured assistant body to persist (tool calls, file/plan/interaction