From a91d4efaf43045b3663d4aa4ba3d90fce2ae1bf2 Mon Sep 17 00:00:00 2001 From: DJJ Date: Mon, 31 Aug 2026 11:55:51 -0700 Subject: [PATCH] Teach single-call mailbox delivery and durable operations Device commands now surface a delivery capability (realtime|mailbox|both) and tb call takes a per-call --delivery realtime|mailbox|fallback policy. The skill teaches the unified single-call form: fallback enqueues only on a proven non-dispatch, completed and unknown outcomes never enqueue, and agents never send a second enqueue or fixed-interval polling. Adds the tb device op ls/get/cancel surface, execution-ambiguity safety rules (result_unknown, expired+executionMayHaveOccurred), and store:// result handling with tb store commands. --- skills/tool-bridge/SKILL.md | 42 +++++++++++++++- .../tool-bridge/references/cli-reference.md | 49 ++++++++++++++++++- 2 files changed, 88 insertions(+), 3 deletions(-) diff --git a/skills/tool-bridge/SKILL.md b/skills/tool-bridge/SKILL.md index cefbbe8..c295a71 100644 --- a/skills/tool-bridge/SKILL.md +++ b/skills/tool-bridge/SKILL.md @@ -1,6 +1,6 @@ --- name: tool-bridge -description: Discover and invoke self-described tools through a Tool Bridge gateway using the shortest safe tb CLI path, and use operational feedback when it materially affects a call. Use when an agent needs to find an available organizational tool, inspect an HTBP/MCP/HTTP capability, query connected context, call a gateway tool, explore the visible tool tree, or troubleshoot abnormal tool behavior. Requires an authenticated Tool Bridge target. +description: Discover and invoke self-described tools through a Tool Bridge gateway using the shortest safe tb CLI path, and use operational feedback when it materially affects a call. Use when an agent needs to find an available organizational tool, inspect an HTBP/MCP/HTTP capability, query connected context, call a gateway tool, reach a device command that may be offline via durable mailbox delivery, explore the visible tool tree, or troubleshoot abnormal tool behavior. Requires an authenticated Tool Bridge target. --- # Tool Bridge @@ -16,6 +16,8 @@ Use the gateway's live descriptions as the source of truth. Never guess a path, - Do not register providers, mount nodes, create keys, change secrets, or administer the gateway unless the user explicitly asks for that management action. - Interpret `404` as either nonexistent or invisible. Do not probe around it to infer hidden paths. - Do not automatically retry calls that may have side effects. Check the error's `retryable` signal and the command effect first. +- Treat a mailbox operation ending in `result_unknown`, or `expired` with `executionMayHaveOccurred: true`, as execution ambiguity: the device may have run the command. Do not re-call, re-enqueue, or retry; create a new execution only when business idempotency is independently proven and the user authorizes it. +- Delivery mode never relaxes authorization: `effect`, `confirm`, and the user's confirmation boundary apply identically to realtime, mailbox, and fallback calls. - Never publish credentials, customer data, private payloads, or unverified speculation as feedback. Prefer voting on an existing matching entry over creating a duplicate. ## Choose the shortest safe path @@ -36,6 +38,8 @@ If the exact full command path, arguments schema, `effect: read`, and `confirm: tb call '/' --args '' --json ``` +For a known device command that must also work while the device is offline, the fast path is still a single call: add `--delivery fallback` (see the device delivery section) instead of adding discovery or a second request. + If `tb` is missing, tell the user that Node.js 22+ and `@tool-bridge/cli` are required. Ask before installing a global package. If authentication fails, ask the user to configure a profile or inject `TB_BASE_URL` and `TB_SK`; do not ask them to paste a secret into chat when a secret-input mechanism is available. ### Discovery path: unknown capability or contract @@ -54,7 +58,7 @@ tb ls '' --json tb help '' --json ``` -JSON search results already carry `items[].tool.inputSchema`, `effect`, and `confirm`. If one result is unambiguous and contains enough information for a read-only call, use its exact `/` pair and call it without another help request. +JSON search results already carry `items[].tool.inputSchema`, `effect`, `confirm`, and — for device commands — `delivery`. If one result is unambiguous and contains enough information for a read-only call, use its exact `/` pair and call it without another help request. Open command-level help only when a required field is missing, results are ambiguous, the command is unfamiliar or failure-prone, or the operation may mutate state: @@ -78,6 +82,30 @@ Do not list feedback on every normal read call, and do not fetch every visible e For any write, destructive, or `confirm: true` operation, inspect command help and apply the user's authorization boundary before calling. +### Device delivery: one call, no second enqueue + +Device commands carry a `delivery` capability in live metadata: `realtime` (default when absent), `mailbox` (durable enqueue only), or `both`. Separately, each call chooses a policy with `--delivery realtime|mailbox|fallback` (default `realtime`). Do not confuse the two: metadata says what the command supports, the flag says what this call wants. + +When the full path, schema, and `delivery` are known and the task must tolerate an offline device, make exactly one call: + +```sh +tb call '/' --args '' --delivery fallback --json +``` + +The gateway owns the fallback decision. For a `both` command it tries realtime first and enqueues only when it can prove the call was never dispatched; for a mailbox-only command it enqueues directly. A completed realtime result — including a business error from the device — and an outcome-unknown send never enqueue, and outcome-unknown is never retryable. Never follow a failed synchronous call with a second "enqueue" request of your own; that is the gateway's decision, not a client retry strategy. + +A `200` response with `x-tb-delivery: realtime` is the command result. A `202` with `x-tb-delivery: mailbox` is an operation identity (`operationId`, state `queued`). When a call returns an operation identity, the synchronous flow is finished: report the queued operation and stop. Do not poll on a fixed interval. Only when the user's current goal actually needs the state or terminal result, read it once: + +```sh +tb device op get '' '' --json +``` + +`tb device op ls '' --json` lists a device's operations and `tb device op cancel` requests cancellation — cancel of an already-claimed operation is cooperative and does not prove the device stopped. Claiming, renewing, and completing operations belong to the device runtime, never to the calling agent. + +Offline presence does not hide a device: an offline device and its mailbox-capable commands stay discoverable, and enqueueing to them is normal. + +For any write, destructive, or `confirm: true` operation, delivery mode changes nothing: inspect command help and apply the user's authorization boundary before calling. + ### Recovery path: abnormal behavior Treat errors, timeouts, schema-valid but surprising results, and upstream inconsistencies as abnormal behavior: @@ -91,6 +119,16 @@ Keep recovery bounded: normally make at most one workaround retry. Do not claim Feedback writes are not part of the happy path. After securing the task result, vote for a useful existing entry or submit a verified new lesson only when gateway writes are already authorized and doing so adds value. Otherwise mention a draft only when it would materially help the user. +## Store references in results + +A result containing a `store://default/...` URI is an object identity, not content. Read the current owner's object with `tb store stat` / `tb store get`. Create a share only when the user explicitly asks to hand the file to an external audience: + +```sh +tb store share 'store://default/' --json +``` + +Treat the successful share output as a secret being delivered to the user: hand it over immediately and never copy it into logs, feedback, or other diagnostics. Revoke with `tb store revoke-share` when it is no longer needed. Stable `store://` URIs may be persisted; grants, share URLs, and presigned URLs may not. + ## Validate and report Check the returned data against the task, not merely the process exit code. Summarize which gateway path and tool were used, the relevant result, and any limitation or partial failure. Never include the secret key. diff --git a/skills/tool-bridge/references/cli-reference.md b/skills/tool-bridge/references/cli-reference.md index 30c2b10..27d0e4d 100644 --- a/skills/tool-bridge/references/cli-reference.md +++ b/skills/tool-bridge/references/cli-reference.md @@ -1,6 +1,6 @@ # Tool Bridge CLI reference -Load this reference when target configuration or command syntax is unclear, a Context write/upload is needed, or discovery, authentication, invocation, or feedback handling fails. It is not a required preflight for a known read-only call. +Load this reference when target configuration or command syntax is unclear, a Context write/upload is needed, a durable device operation or Store object must be inspected or managed, or discovery, authentication, invocation, or feedback handling fails. It is not a required preflight for a known read-only call. ## Target configuration @@ -61,6 +61,7 @@ Node-level help is an index; it lists the commands under a node. Request ` - `scope`: required permission - `effect`: typically `read`, `write`, or `destructive` - `confirm`: whether the user must confirm before the call +- `delivery`: for device commands, the supported channels — `realtime`, `mailbox`, or `both`; absent means realtime-only - `feedback`: high-value operational notes from prior users Unknown optional fields are forward-compatible and should be ignored. @@ -97,6 +98,52 @@ Prefer `--args-file` for long payloads. Keep sensitive temporary files outside t Do not reuse a failed write or destructive call automatically. A timeout can leave the remote outcome unknown. +## Device delivery and durable operations + +`tb call` accepts a per-call delivery policy for device-backed commands: + +```sh +tb call '/' --args '' --delivery fallback --json +tb call '/' --args '' --delivery mailbox --ttl 3600 --idempotency-key '' --json +``` + +- `--delivery` is `realtime` (default), `mailbox` (enqueue a durable operation), or `fallback` (gateway decides). +- `--ttl ` bounds how long a queued operation stays deliverable; `--idempotency-key ` makes a retried enqueue reuse the same operation. Both require `--delivery mailbox` or `fallback`. +- The command's metadata `delivery` (`realtime`/`mailbox`/`both`, absent = realtime-only) limits which policies can succeed; the flag never expands a command's capability. + +`fallback` semantics are owned by the gateway: for a `both` command it attempts realtime and enqueues only when the gateway can prove the call was never dispatched to the device. A completed realtime call — success or business error — never enqueues. An outcome-unknown send never enqueues either and returns `retryable: false`; do not send a manual follow-up enqueue. + +A `200` response carries the command result with header `x-tb-delivery: realtime`. A `202` carries the operation detail with `x-tb-delivery: mailbox`; in `--json` mode the CLI prints that operation object, in human mode it prints `queued (queued)`. + +Inspect and manage operations with the `tb device` commands, scoped to the caller's visibility: + +```sh +tb device ls --json +tb device op ls '' --state queued --json +tb device op get '' '' --json +tb device op cancel '' '' --json +``` + +Operation states: `queued`, `claimed`, then a terminal `succeeded`, `rejected`, `failed`, `result_unknown`, `cancelled`, or `expired`. Interpretation rules: + +- Cancelling a `queued` operation terminates it before execution; cancelling a `claimed` one only records the request — the device stops cooperatively, and side effects are not proven stopped. +- `result_unknown`, and `expired` with `executionMayHaveOccurred: true`, mean the device may have executed the command without a recoverable result. Never re-enqueue or re-call on that basis alone. +- Claiming, renewing, and completing operations are the device runtime's protocol. A calling agent reads state with `op get` at most when the current task needs it; there is no fixed polling loop in the default flow. + +## Store objects + +Deployment-level objects live in the default Store and appear in results as stable `store://default/...` URIs. Owner-scoped commands: + +```sh +tb store list --json +tb store stat 'store://default/' --json +tb store get 'store://default/' --out '' +tb store upload '' --json +tb store rm 'store://default/' --json +``` + +`tb store share 'store://default/' --json` creates a short-lived revocable bearer link for an external audience; `tb store revoke-share ''` revokes it. The share output is a secret: deliver it to the user and keep it out of logs, feedback, and generated artifacts. Persist only the `store://` URI itself. + ## Context writes and uploads Use `tb ctx put` for text or JSON that can be sent inline, from a UTF-8 file, or through stdin. It creates or replaces an entry and supports metadata and optimistic concurrency: