@sema-agent/core 5.1.0 → 5.3.0

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/agents/roster-store.js +6 -1
  3. package/dist/bin/sema-tb.js +3 -4
  4. package/dist/brain/openai.js +3 -5
  5. package/dist/brain/terminal-cause.d.ts +1 -1
  6. package/dist/core/a2a-task-state.d.ts +15 -0
  7. package/dist/core/a2a-task-state.js +68 -0
  8. package/dist/core/a2a.d.ts +42 -0
  9. package/dist/core/a2a.js +651 -0
  10. package/dist/core/checkpoint-store.d.ts +6 -1
  11. package/dist/core/checkpoint-store.js +3 -1
  12. package/dist/core/hooks.js +8 -3
  13. package/dist/core/mcp.d.ts +12 -5
  14. package/dist/core/mcp.js +11 -31
  15. package/dist/core/memory-engine/dual-root.js +2 -1
  16. package/dist/core/memory-engine/engine.d.ts +4 -0
  17. package/dist/core/memory-engine/engine.js +25 -6
  18. package/dist/core/memory-engine/file-backend.d.ts +7 -5
  19. package/dist/core/memory-engine/file-backend.js +2 -2
  20. package/dist/core/memory-engine/index.d.ts +1 -1
  21. package/dist/core/memory-engine/index.js +1 -1
  22. package/dist/core/memory-engine/layout.d.ts +6 -2
  23. package/dist/core/memory-engine/layout.js +73 -31
  24. package/dist/core/memory.js +6 -0
  25. package/dist/core/protocol-naming.d.ts +7 -0
  26. package/dist/core/protocol-naming.js +32 -0
  27. package/dist/core/protocol-table.d.ts +13 -8
  28. package/dist/core/protocol-table.js +34 -15
  29. package/dist/core/runner/prepare-memory.js +4 -4
  30. package/dist/core/runner/prepare-task.d.ts +7 -0
  31. package/dist/core/runner/prepare-task.js +73 -28
  32. package/dist/core/runner/runtask.js +42 -9
  33. package/dist/core/runner/tool-disclosure.d.ts +1 -1
  34. package/dist/core/runner/tool-disclosure.js +7 -2
  35. package/dist/core/runner/turn-attachments.d.ts +1 -2
  36. package/dist/core/runner/turn-attachments.js +1 -12
  37. package/dist/core/store-contracts/background-agent-store-contract.d.ts +5 -0
  38. package/dist/core/store-contracts/background-agent-store-contract.js +213 -0
  39. package/dist/core/task-registry-agent.d.ts +14 -1
  40. package/dist/core/task-registry-agent.js +1 -1
  41. package/dist/core/tool-policy.d.ts +1 -0
  42. package/dist/core/tool-policy.js +12 -2
  43. package/dist/core/types.d.ts +16 -2
  44. package/dist/index.d.ts +10 -5
  45. package/dist/index.js +7 -3
  46. package/dist/orchestration/run-workflow-tool.js +5 -1
  47. package/dist/prompt-assembly/assemble.js +0 -1
  48. package/dist/prompt-assembly/event-registry.js +1 -0
  49. package/dist/stores/cc/mailbox-store.js +58 -14
  50. package/dist/stores/file/file-snapshot-store.d.ts +9 -1
  51. package/dist/stores/file/file-snapshot-store.js +28 -5
  52. package/dist/stores/file/fs-atomic.d.ts +4 -1
  53. package/dist/stores/file/fs-atomic.js +2 -1
  54. package/dist/stores/file/index.d.ts +10 -3
  55. package/dist/stores/file/index.js +4 -3
  56. package/dist/stores/file/mailbox-store.d.ts +5 -0
  57. package/dist/stores/file/mailbox-store.js +15 -3
  58. package/dist/stores/file/session-policy-store.d.ts +11 -8
  59. package/dist/stores/file/session-policy-store.js +21 -4
  60. package/dist/stores/file/session-store.d.ts +9 -1
  61. package/dist/stores/file/session-store.js +19 -4
  62. package/dist/tools/fs/fs-search-tools.js +9 -0
  63. package/dist/tools/fs/fs-shared.d.ts +1 -1
  64. package/dist/tools/fs/fs-shared.js +1 -1
  65. package/dist/tools/todo.js +13 -6
  66. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.3.0 (2026-08-03)
4
+
5
+ _The A2A arc: client+server legs land end-to-end (all additive), the workflow-size notice lane closes its channel, and three naming/containment defects fix. Major stays locked at 5._
6
+
7
+ ### Fixed
8
+
9
+ - Protocol naming (ticket #10): a minted peer segment can no longer parse back as a DIFFERENT peer — adjacent out-of-charset characters used to normalize into the `__` separator (`"prod. db"` → `prod__db`, read back as peer `prod`), and a clamp could splice one in at the digest join. The peer leg folds `_` runs after the clamp (an all-separator peer degrades to a readable digest), the digest joins with `-`; only names for peers over the assembly budget change spelling. The tool segment is untouched (`repo__delete` round-trips as before).
10
+ - MCP tool `description` is neutralized (`sanitizeUntrustedText`) before it reaches the model-facing tool card (ticket #11) — it was the one remote text with no neutralization at all (instructions are sanitized+fenced, drop reasons inlined, error bodies fenced); the text is otherwise verbatim (no fence: that would poison every tool table for the honest majority).
11
+ - MCP refresh receipt `prefix` (ticket #9): the refresh leg recomputed the server prefix without the assembly budget, so a server name surviving normalization but not the 64-char budget produced a prefix matching none of the registered names — the splice domain then removed nothing and the fresh tools mounted alongside the stale ones under the same names. Minting (normalize + budget + clamp) is now single-sourced in `protocol-naming.ts`; the intake and the receipt call the same arithmetic.
12
+
13
+ ### Added
14
+
15
+ - `workflow_size_guideline_change` turn-boundary frame: a mid-run retune of `RunnerDeps.workflowLimits.sizeGuideline` now reaches the model at the next tool-calling boundary (the Workflow tool card is a per-mount snapshot and never re-renders). Same discipline as the `date_change`/`instructions_change` lanes: continuation-gated (a retune landing on a text-final turn never revives a finished run), fixed one-sentence frame above the bundle byte cap, comparison on the RESOLVED guideline (writing down the value already in force is not a change), armed only when the Workflow tool is mounted. `TaskEvent.steering_injected.source` gains the member (additive).
16
+ - Internal cleanup: the todo projection's dead `statusCounts` field is gone (computed on every TodoWrite, never consumed by any model-facing renderer).
17
+ - `toA2ATaskState` / `fromA2ATaskState` / `A2A_TASK_STATES` (exported): the pure projection between the durable background-agent row status and the A2A TaskState vocabulary. Forward is total (`killed` splits on `stoppedBy`: user/parent → `canceled`, anything else → `failed`); reverse is 9→5 and returns a discriminated `{lossy, status, note?}` — `lossy: false` holds exactly for the states that survive the round trip (pinned as a property test). Serving layers must still gate every row through `canAccessAgentRecord`.
18
+ - `queryBackgroundAgents` / `serveDurableAgentRowLane` / `buildAgentPollDetails` (+ their named input/result types) reach the package root — the row-serving primitives an A2A-style host endpoint composes, previously module-internal.
19
+ - `backgroundAgentStoreContract` + `backgroundAgentStoreScopesContract`: the contract kit the store family was missing — third-party `BackgroundAgentStore` backends can now run the same assertion set the bundled InMemory/File backends pass (rev-CAS, guarded updateIf, filter semantics, stale-running reap attribution; the optional `listScopes` layer is opt-in so an unimplemented method cannot skip silently).
20
+ - `TaskSpec.a2a` + `A2aServerSpec` + `materializeA2aTools` / `MaterializedA2a` (exported) + `onError` phase `"a2a"`: the A2A client leg — declared peers' agent cards are discovered (`agent-card.json` with legacy fallback, explicit `cardUrl` override), their skills mount as task-scoped tools (`a2a__<peer>__<skill>`; the protocol gives skills no input schema, so the engine synthesizes `{message, taskId?, contextId?}` and folds tags/examples into the sanitized, bounded description), calls ride JSON-RPC `message/send` with terminal-state polling under dual clocks, and every request declares `A2A-Version: 1.0`. Both legal reply shapes are honored (a direct Message and a polled Task); `input-required`/`auth-required` return immediately with the continuation keys instead of burning the ceiling. Default axes are `egress` + `write` (a remote agent is an external write; the caller's `toolAxes` is the only lowering channel), success bodies come back fenced as untrusted data (a deliberate divergence from the MCP success path — the sender is an agent, not a data source), and a `FilePart` uri is shown as text, never fetched. One unreachable peer never bricks its healthy sibling.
21
+ - `A2A_NAMESPACE` (exported): the protocol table's second entry — the `a2a__<peer>__<skill>` name shape, parse/makeName/displayGroupKey under the same destructuring-safe contract as MCP (both entries now built by one factory). The reserved-name and policy-audit messages now enumerate every table prefix instead of spelling `mcp__` by hand.
22
+ - `CheckpointError.detail` (additive, optional): structured discrimination where the code alone is ambiguous — `detail.field` names WHICH decision-action binding failed on `checkpoint.invalid_outcome` (`"boundCallId"`: the action you decided on was replaced, re-fetch the pending list; `"boundInputHash"`: the input you reviewed changed under the same action, re-review). `code` remains the only required discriminant.
23
+
24
+ ## 5.2.0 (2026-08-03)
25
+
26
+ _The v5.0.1..v5.1.0 full-window dual-lens review's repair batch (67 findings, all dispositioned; see docs/REVIEW-BACKLOG.md). Theme: the 5.1.0 retirements now refuse loudly EVERYWHERE they promised to, and the new disclosure seats reach the deployments they were built for._
27
+
28
+ ### Behavior / BREAKING-leaning (all loud-by-design)
29
+
30
+ - **memory**: a `TaskSpec.memory` still carrying the retired singular `scope` throws `config.memory_scope_spelling` at task start (it silently normalized to memory-OFF for the whole task).
31
+ - **tool policy**: a caller policy decision carrying the retired `reason` field is refused fail-closed at the policy fold — `deny` keeps denying with a migration text, `allow`/`ask` are NOT honored. (The 5.1.0 claim that `{reason}` "compiles red" was false in method-position shapes; this is the runtime signal that was missing.)
32
+ - **hooks / resume**: the same tripwire reaches the two decision faces that do not cross the fold — a PreToolUse hook result carrying `reason` is refused fail-closed at both hook entry points (its `additionalContext`/`updatedInput` do not take effect either; `allow{reason}` used to run the tool), and the durable-resume re-checks (edited-args policy re-judgment, session-rule narrowing layer) refuse the retired dialect on the same helper.
33
+ - **openai brain**: an out-of-contract `reasoning` value is treated as absent on every wire format (it used to pass through verbatim as `reasoning_effort`, so the two brains disagreed on the same input).
34
+ - **ToolSearch**: a `select` argument key in any shape (the retired array form included) is a loud invalid-arguments naming `query:"select:A,B"` (it used to parse as an empty query and answer "No deferred tools matched"); stored activations replay tolerantly.
35
+ - **Grep**: the retired `regex`/`max_results` keys refuse with their living replacements (pattern IS a regex; `head_limit`) — `max_results` was silently accepted and ignored.
36
+ - **TodoWrite**: the public raw-spec execute lane refuses a missing/empty `activeForm` (it used to synthesize the label and accept what the schema refuses); the simple-arm card teaches the required form.
37
+ - **protocol table**: `MCP_NAMESPACE.parse` rejects an empty tool segment; `makeName` throws on empty/separator-carrying segments (round-trip symmetry); both survive destructuring (`this`-free).
38
+ - **workflow store**: a `resolveName` implementation still returning the retired bare-string resolution gets a structured migration error naming the `{script}` form (was a TypeError quoting the whole script source).
39
+ - **Read/Edit/Write**: `file_path` is schema-REQUIRED, so a call carrying only the retired `path` alias is loud invalid-arguments naming `file_path` — on the live lane and on durable replay alike. 5.1.0 retired the alias from the schema's declared properties but left `file_path` optional; TypeBox's object check is non-strict, so `{path}` still validated with no `file_path` present and the tool body read the target back out through the gate reader's over-read arm. The alias therefore kept working end-to-end and no caller ever learned it had to migrate — the 5.1.0 note's "re-validates loudly on resume" was not true until now. The security-gate readers keep their `path` arm unchanged: that side is fail-closed coverage across other tool vocabularies (Glob's real `path` parameter, deployment-authored tools), and the asymmetry is deliberate — the schema decides what a caller may send, the gate decides what gets judged, and the gate must never see less than the tool might act on.
40
+ - **memory engine (semantics fix)**: `deriveControlPlaneDir` mints resolve-only again — canonicalizing a model-writable memory dir made a symlink swap a redirect handle onto a fresh empty control plane; the repo-partition face keeps the canonical key via the new paired mint (below). Adoption no longer trips over its own backend's mkdir scaffolding (the common pinned-backend deployment adopts instead of reporting a spurious `memory.partition_split` every mount); a failed adoption rename re-checks the source before reading as adopted-by-peer.
41
+
42
+ ### Added
43
+
44
+ - `deriveRepoControlPlaneDir` (exported): the canonical-key control-plane mint paired with `deriveRepoMemoryDir` — self-assembled hosts no longer mispair the two key families on alias paths.
45
+ - `FileStorageBackendOptions.onCorruptRead`: the packaged file backend threads the corrupt-read disclosure seat into the stores it constructs; `FileMemoryEngineBackendOptions.onIncident` + `MemoryEngineOptions.onIncident`: partition incidents are observable on the self-constructed mount paths.
46
+ - Disclosure coverage: session-policy `listBySession` corrupt-skips disclose; cc-mailbox element-grade corruption is dropped AND disclosed (sinks run after the cross-process lock releases); roster row-drops disclose with a count.
47
+ - `FileMailboxStoreOptions.onCorruptRead` (server pickup receipt named the gap): the dataRoot-form file mailbox store discloses a corrupt interior JSONL record dropped on replay (both the warm and cold replay arms); the torn tail stays silent by design — that is the append-log's documented crash-recovery shape, not corruption. `readJsonlRecords` grew the optional `onCorrupt` seat (additive; other consumers unchanged).
48
+ - `MCP_PREFIX` regains its literal type (`"mcp__"`) via the generic `ProtocolNamespace<P>`.
49
+ - `FileSessionRepoOptions.onCorruptRead` + `FileFileSnapshotStoreOptions.onCorruptRead` (additive): the two enumerating file stores stop reading "could not read it" as "there is none". The session repo discloses an unreadable sessions directory, a session log skipped from a listing, and a corrupt interior JSONL record dropped on replay; the snapshot store discloses a corrupt/wrong-shape manifest collapsed to "no snapshot", an unreadable manifest scope directory on `listKeys`/`reap`, and every blob-GC pass that aborted (blobs kept). Plain ENOENT / `not_found` never fires either, and the torn tail stays silent. `FileStorageBackendOptions.onCorruptRead` reaches both (its payload is now the named union `FileStorageCorruptReadInfo` — `path`+`reason` always, `sessionId`/`principal` only from the session-policy face), so one sink covers the packaged deployment.
50
+ - `MemoryEngineOptions.onIncident` also carries `memory.announce_failed`: the memory announcement queue — the disclosure lane for out-of-session memory events — used to swallow its own enqueue/drain failures at four call sites, so a host with an unwritable control plane saw healthy sessions while every notice was dropped. All four arms stay fail-open; a throwing sink is swallowed at the boundary and never re-enters the queue.
51
+
52
+ ### Fixed
53
+
54
+ - Repair-loop spend accounting across a human-decision park: an `approval`/`plan_review` park now stamps the in-flight leg's spend into the serialized bundle, so a resume no longer under-counts the money already spent (the resource-slice park was always exact and is unchanged).
55
+ - The retired-`reason` tripwire also wraps the `changed_files` advisory probe's direct policy `check` — the screen is a property of every consumption point. All layers in that stack are core-constructed today, so the end-to-end behavior is unchanged; a retired-dialect decision would suppress that file's turn-boundary notice (never widen a permission).
56
+
3
57
  ## 5.1.0 (2026-08-03)
4
58
 
5
59
  _The second (and final planned) baggage sweep: every remaining compatibility arm, plus a durable memory-key fix with an automatic adoption leg and the protocol table that clears the road for a second tool protocol. Version-line policy (project ruling): the major stays at 5 — breaking changes below are declared loudly here and coordinated with all in-org consumers, but do not mint a new major._
@@ -110,7 +110,12 @@ export class FileRosterStore {
110
110
  const raw = JSON.parse(text);
111
111
  if (!Array.isArray(raw.entries))
112
112
  throw new Error("roster file: entries is not an array");
113
- return raw.entries.filter((e) => typeof e === "object" && e !== null && typeof e.name === "string" && typeof e.agentId === "string" && typeof e.createdAt === "number");
113
+ const kept = raw.entries.filter((e) => typeof e === "object" && e !== null && typeof e.name === "string" && typeof e.agentId === "string" && typeof e.createdAt === "number");
114
+ const dropped = raw.entries.length - kept.length;
115
+ if (dropped > 0) {
116
+ this.discloseCorrupt(`${dropped} schema-invalid roster rows dropped (of ${raw.entries.length}); a subsequent write persists the filtered document`);
117
+ }
118
+ return kept;
114
119
  }
115
120
  catch (e) {
116
121
  if (mode === "lenient") {
@@ -7,10 +7,9 @@ import { inferMaxTokensField } from "../brain/openai.js";
7
7
  function usage(code = 2) {
8
8
  const out = code === 0 ? process.stdout : process.stderr;
9
9
  out.write("Usage: sema-tb --model <provider/model> [--objective-file <path>] [--jsonl <path>]\n" +
10
- " Instruction legs: --objective-file <path> (preferred for real task text) | stdin\n" +
11
- " | stdin (default when neither flag is given). Both flags together = usage error.\n" +
12
- " WARNING: argv is visible to every process in the sandbox (ps / pgrep -f / /proc/*/cmdline) — long\n" +
13
- " Instructions ride --objective-file or stdin; the retired --message argv leg is refused (process-list exposure).\n" +
10
+ " Instruction legs: --objective-file <path> (preferred for real task text) | stdin (default when the flag is absent).\n" +
11
+ " WARNING: argv is visible to every process in the sandbox (ps / pgrep -f / /proc/*/cmdline), so instructions ride\n" +
12
+ " --objective-file or stdin; the retired --message argv leg is refused (process-list exposure).\n" +
14
13
  "Env: <PROVIDER>_API_KEY / <PROVIDER>_BASE_URL, TB_MAX_TURNS, TB_TIMEOUT_SEC, TB_FULLBODY, TB_TODOREMIND, TB_BGTASKS, TB_SUMMARIZE,\n" +
15
14
  " TB_FINALVERIFY, TB_CALLCAP, TB_FINALIZE, TB_REASONING, TB_JSONL, TB_CONTEXT_WINDOW, TB_MAX_OUTPUT_TOKENS, TB_TASKLIST, TB_VISION\n" +
16
15
  ' TB_COST="input,output[,cacheRead[,cacheWrite]]" — USD per MTok; explicit pricing override (omitted\n' +
@@ -90,15 +90,13 @@ function applyThinking(body, model, reasoning) {
90
90
  }
91
91
  return;
92
92
  }
93
- if (!model.reasoning || !reasoning || reasoning === "off")
93
+ if (!model.reasoning || !isThinkingLevel(reasoning) || reasoning === "off")
94
94
  return;
95
95
  const compat = thinkingCompat(model);
96
96
  const supportsEffort = compat.supportsReasoningEffort ?? true;
97
- const effort = isThinkingLevel(reasoning)
98
- ? resolveEffort(reasoning, compat.reasoningEffortLevels ?? DEFAULT_EFFORT_LEVELS).effective
99
- : reasoning;
97
+ const effort = resolveEffort(reasoning, compat.reasoningEffortLevels ?? DEFAULT_EFFORT_LEVELS).effective;
100
98
  let effortWire = effort;
101
- if (isThinkingLevel(effort) && model.thinkingLevelMap) {
99
+ if (model.thinkingLevelMap) {
102
100
  const mapped = model.thinkingLevelMap[effort];
103
101
  if (mapped === null)
104
102
  effortWire = undefined;
@@ -1,4 +1,4 @@
1
1
  import type { AssistantMessage } from "../internal/llm.js";
2
- export type TerminalCauseCarrier = Pick<AssistantMessage, "errorKind" | "errorMessage">;
2
+ export type TerminalCauseCarrier = Pick<AssistantMessage, "errorKind">;
3
3
  export declare function isDegenerateCutMessage(message: TerminalCauseCarrier): boolean;
4
4
  export declare function isWalltimeCutMessage(message: TerminalCauseCarrier): boolean;
@@ -0,0 +1,15 @@
1
+ import type { BackgroundAgentRecord } from "./background-agent-store.js";
2
+ export declare const A2A_TASK_STATES: readonly ["submitted", "working", "input-required", "completed", "canceled", "failed", "rejected", "auth-required", "unknown"];
3
+ export type A2ATaskState = (typeof A2A_TASK_STATES)[number];
4
+ export declare function toA2ATaskState(record: Pick<BackgroundAgentRecord, "status" | "stoppedBy">): A2ATaskState;
5
+ export interface A2ATaskStateReversalFaithful {
6
+ lossy: false;
7
+ status: BackgroundAgentRecord["status"];
8
+ }
9
+ export interface A2ATaskStateReversalLossy {
10
+ lossy: true;
11
+ status: BackgroundAgentRecord["status"];
12
+ note: string;
13
+ }
14
+ export type A2ATaskStateReversal = A2ATaskStateReversalFaithful | A2ATaskStateReversalLossy;
15
+ export declare function fromA2ATaskState(state: A2ATaskState): A2ATaskStateReversal;
@@ -0,0 +1,68 @@
1
+ export const A2A_TASK_STATES = [
2
+ "submitted",
3
+ "working",
4
+ "input-required",
5
+ "completed",
6
+ "canceled",
7
+ "failed",
8
+ "rejected",
9
+ "auth-required",
10
+ "unknown",
11
+ ];
12
+ const CANCELLING_STOP_SOURCES = new Set(["user", "parent"]);
13
+ export function toA2ATaskState(record) {
14
+ switch (record.status) {
15
+ case "running":
16
+ return "working";
17
+ case "parked":
18
+ return "input-required";
19
+ case "completed":
20
+ return "completed";
21
+ case "failed":
22
+ return "failed";
23
+ case "killed":
24
+ return record.stoppedBy !== undefined && CANCELLING_STOP_SOURCES.has(record.stoppedBy) ? "canceled" : "failed";
25
+ }
26
+ }
27
+ export function fromA2ATaskState(state) {
28
+ switch (state) {
29
+ case "working":
30
+ return { lossy: false, status: "running" };
31
+ case "input-required":
32
+ return { lossy: false, status: "parked" };
33
+ case "completed":
34
+ return { lossy: false, status: "completed" };
35
+ case "failed":
36
+ return { lossy: false, status: "failed" };
37
+ case "submitted":
38
+ return {
39
+ lossy: true,
40
+ status: "running",
41
+ note: "the durable ledger has no accepted-but-not-yet-started state — a row exists only once its child is running, so the distinction between submitted and working is dropped.",
42
+ };
43
+ case "canceled":
44
+ return {
45
+ lossy: true,
46
+ status: "killed",
47
+ note: "the cancellation attribution lives in the row's stoppedBy field, which a state alone cannot carry: a killed row WITHOUT stoppedBy in (user, parent) projects back as failed, not canceled.",
48
+ };
49
+ case "rejected":
50
+ return {
51
+ lossy: true,
52
+ status: "failed",
53
+ note: "rejection happens before a row exists (admission-time), so it is recorded as a failed terminal — the fact that the task was never admitted at all is lost.",
54
+ };
55
+ case "auth-required":
56
+ return {
57
+ lossy: true,
58
+ status: "failed",
59
+ note: "auth-required is a resumable waiting state with no durable seat here (parked is bound to an approval-checkpoint token, which this state has none of), so it lands as a failed terminal and is NOT resumable through this mapping.",
60
+ };
61
+ case "unknown":
62
+ return {
63
+ lossy: true,
64
+ status: "failed",
65
+ note: "unknown is a read-projection state (an outcome this engine instance cannot see), not a durable outcome; writing it back asserts a terminal failure the row itself never recorded.",
66
+ };
67
+ }
68
+ }
@@ -0,0 +1,42 @@
1
+ import type { AgentTool } from "../internal/harness-types.js";
2
+ import type { A2aServerSpec, ToolEffect } from "./types.js";
3
+ export declare const A2A_SKILL_DESCRIPTION_MAX_CHARS = 2048;
4
+ export interface A2aToolAxis {
5
+ name: string;
6
+ irreversibility?: "always";
7
+ egress?: true;
8
+ effect?: ToolEffect;
9
+ }
10
+ export interface A2aPeerStatus {
11
+ name: string;
12
+ status: "ready" | "failed";
13
+ agentName?: string;
14
+ agentDescription?: string;
15
+ endpoint?: string;
16
+ toolNames?: string[];
17
+ error?: string;
18
+ }
19
+ export interface A2aRefreshResult {
20
+ peer: string;
21
+ prefix: string;
22
+ status: "refreshed" | "failed" | "disposed";
23
+ toolCount: number;
24
+ added: string[];
25
+ removed: string[];
26
+ tools?: AgentTool[];
27
+ axes?: A2aToolAxis[];
28
+ error?: string;
29
+ }
30
+ export interface MaterializedA2a {
31
+ tools: AgentTool[];
32
+ toolAxes: A2aToolAxis[];
33
+ warnings: Error[];
34
+ statuses: A2aPeerStatus[];
35
+ refresh: (peer?: string) => Promise<A2aRefreshResult[]>;
36
+ dispose: () => Promise<void>;
37
+ }
38
+ export declare class A2aRpcError extends Error {
39
+ readonly code: number;
40
+ constructor(peer: string, method: string, code: number, message: string);
41
+ }
42
+ export declare function materializeA2aTools(specs: readonly A2aServerSpec[], principal?: string, signal?: AbortSignal): Promise<MaterializedA2a>;