@securecoms/agent-bridge 0.33.0 → 0.34.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.
@@ -45,7 +45,8 @@ import { type CallBufferTurn } from "./voice/call-buffer.js";
45
45
  import { type VaultListNode, type VaultReadExpectations } from "./vault.js";
46
46
  import type { AgentActionNodeType, AgentActionNodeStatus, AgentActionNodeDetail } from "@agentvault/connect-core";
47
47
  import { removeManagedLlmSource, writeManagedFiles, writeManifestPolicy } from "./service/runtime-config-dir.js";
48
- import { bindRuntimeConfig } from "./grants/binding.js";
48
+ import { type ReplyCredit } from "./reply-credit.js";
49
+ import { bindRuntimeConfig, type FeatureSet } from "./grants/binding.js";
49
50
  import type { RuntimeReplyReceipt } from "./reply-receipts.js";
50
51
  import type { SearchProvider } from "./grants/search-providers.js";
51
52
  export type AgentAudience = "owner_dm" | "team_room" | "partner_room" | "other_human_dm" | "agent_to_agent" | "unknown";
@@ -692,6 +693,11 @@ export interface SendOptions {
692
693
  * enter the call buffer. `POST /v1/send`, SSE and harness replies never set it.
693
694
  */
694
695
  callBufferReply?: true;
696
+ /**
697
+ * Weather/currency Task 17 (OQ-P1): set only by the ws send handler from a frame whose credit is the closed value.
698
+ * The published text gets the U-25 suffix; history, unified memory and the call buffer get the text without it.
699
+ */
700
+ credit?: ReplyCredit;
695
701
  }
696
702
  /**
697
703
  * Task 4b (spec §2.2 A-6): a voice-scoped turn's history, read ONCE at ingest, the buffer seq it ends at and the
@@ -2047,7 +2053,12 @@ export declare class BridgeAgent {
2047
2053
  */
2048
2054
  private reclassifyForReply;
2049
2055
  private enforceBodyRateFloor;
2050
- /** Bookkeeping after a CONFIRMED publish of `body` under `id`. */
2056
+ /**
2057
+ * Bookkeeping after a CONFIRMED publish of `body` under `id`. `recordText` (weather/currency Task 17) is what a text
2058
+ * reply is RECORDED as: the published text minus any credit line. Every record site below reads `recorded`, never
2059
+ * `body.text`. Every production caller passes it (pinned by reply-credit.test.ts: text, or null for an a2a body);
2060
+ * undefined is left only for direct test calls of an uncredited body, where the published and record texts are equal.
2061
+ */
2051
2062
  private afterPublish;
2052
2063
  /**
2053
2064
  * Final review bridge I-1 (ledger T25): the agent reply's U11 capture, taken BEFORE the publish await. The clear generation
@@ -2103,6 +2114,12 @@ export declare class BridgeAgent {
2103
2114
  * scope and part, and its body matches its MAC (which binds id and channel).
2104
2115
  */
2105
2116
  private intentVerifies;
2117
+ /**
2118
+ * Weather/currency Task 17: what a verified row's reply is RECORDED as. Null credit: the decoded text unchanged (null
2119
+ * for an a2a body). "weatherapi": the text minus the exact credit suffix. Undefined: the row is refused (an unknown
2120
+ * credit, a credited a2a body, or a credited text without the exact suffix).
2121
+ */
2122
+ private recordTextOf;
2106
2123
  /**
2107
2124
  * SEC-06 #18 item 3: a rate-limited reply retries the PUBLISH in-process
2108
2125
  * with backoff, never the model turn; so does an exact resend whose
@@ -2557,6 +2574,12 @@ export declare class BridgeAgent {
2557
2574
  * frozen at receipt) AND a window-eligible turn (voiceWindowEligible), whatever the window size. Two call sites: the voice
2558
2575
  * window flush, and the ingest tap block when the window did not take the turn (off, 0 by default). Like the typed tap, the write-time owner scope's pinned identity must be the speaking identity;
2559
2576
  * otherwise nothing is written (H-4). Logs are content-free (U17). Never throws.
2577
+ *
2578
+ * Bridge 0.33.1 (architect Q2, C5 not accepted): a turn on a voice channel V carries `parent`, its P from the
2579
+ * per-frame verdict. The scope is ownerScopeForWrite(P) (P's pin and roster, as for a turn on P), the item keeps
2580
+ * channelId = V (so V's own capture still guards it in the store), and P's receipt capture is checked at commit,
2581
+ * inside the memory mutex: a purge or room clear of P since receipt writes nothing. No P capture at receipt, or no
2582
+ * persisted V -> P link (the purge of P reaches V's items only through it): nothing is written.
2560
2583
  */
2561
2584
  private tapUnifiedVoice;
2562
2585
  /** Section 8.1 steps 1 to 7, before the turn is staged. H-4: the frozen owner predicate and the owner scope now. Never throws. */
@@ -3498,17 +3521,31 @@ export declare class BridgeAgent {
3498
3521
  */
3499
3522
  private readonly searchProvidersSockets;
3500
3523
  /**
3501
- * The D28 value the files were last recomputed for; null until the first socket observation, so that observation
3502
- * always recomputes (final review I-1), even when it is non-capable.
3524
+ * Weather/currency U6 (H-1): the same AND-over-sockets state for utility_tools_v1, kept separately per socket, so a
3525
+ * runtime that declares search_providers_v2 but not utility_tools_v1 (0.14.0, 0.14.1) keeps web_extract and never
3526
+ * sees a utility name.
3527
+ */
3528
+ private readonly utilityToolsSockets;
3529
+ /**
3530
+ * The (search, utility) tuple the files were last recomputed for; null until the first socket observation, so that
3531
+ * observation always recomputes (final review I-1), even when it is non-capable. One trigger keyed on BOTH values.
3503
3532
  */
3504
- private searchProvidersState;
3533
+ private capabilityState;
3505
3534
  private searchCapabilityRefetchTimer;
3506
3535
  /** When the current burst's first change happened (valid while the timer is set). */
3507
3536
  private searchCapabilityBurstStart;
3508
3537
  /** The D28 value in the last report posted (null until one is); a change needs a refetch to reach the web. */
3509
3538
  private lastReportedSearchProviders;
3539
+ /** U6: the utility_tools_v1 value in the last report posted (null until one is). */
3540
+ private lastReportedUtilityTools;
3510
3541
  noteSearchProvidersSocket(token: object, declared: boolean): void;
3511
3542
  clearSearchProvidersSocket(token: object): void;
3543
+ noteUtilityToolsSocket(token: object, declared: boolean): void;
3544
+ clearUtilityToolsSocket(token: object): void;
3545
+ /** U6: every attached runtime socket declared utility_tools_v1; no socket attached means absent (false). */
3546
+ utilityToolsCapable(): boolean;
3547
+ /** U6: the features every attached runtime socket declared, for the policy functions. */
3548
+ capabilityFeatures(): FeatureSet;
3512
3549
  /** D28: every attached runtime socket declared search_providers_v2; no socket attached means absent (false). */
3513
3550
  searchProvidersCapable(): boolean;
3514
3551
  /**
@@ -3523,7 +3560,7 @@ export declare class BridgeAgent {
3523
3560
  * D35 (P-4): on a capability change, rewrite manifest-policy.json at once from the stored manifest, the kill
3524
3561
  * switches and the new capability, then a debounced refetch (one per burst) only when needed.
3525
3562
  */
3526
- private onSearchProvidersMaybeChanged;
3563
+ private onCapabilityMaybeChanged;
3527
3564
  /**
3528
3565
  * A refetch re-renders skills.json (owner_dm_search_cap counts web_extract only with the capability, D39) and posts
3529
3566
  * the report. Needed under a manifest in force, or when the reported value is stale. Never before the boot fetch.
@@ -26,6 +26,8 @@ export declare function withConsumerGrants(m: BridgeMessage, perToolConsumer: bo
26
26
  export declare const MANIFEST_GRANTS_FEATURE = "manifest_grants_v1";
27
27
  /** The /v1/ws hello feature of a runtime that supports the five signed search providers and web_extract (spec D3, D28). Pinned to the runtime's by capability-parity.test.ts. */
28
28
  export declare const SEARCH_PROVIDERS_FEATURE = "search_providers_v2";
29
+ /** Weather/currency spec §8.1 (U6): the /v1/ws hello feature of a runtime that knows weather_forecast and currency_convert. Pinned to the runtime's by capability-parity.test.ts. */
30
+ export declare const UTILITY_TOOLS_FEATURE = "utility_tools_v1";
29
31
  export declare const CONSTANT_TOOL_NAMES: Readonly<Record<"owner_dm" | "team_room", readonly string[]>>;
30
32
  /**
31
33
  * Spec §7.9: a consumer that did not declare manifest_grants_v1 (runtime 0.12.2, a harness,
@@ -55,3 +57,10 @@ export declare function withHarnessGrants(m: BridgeMessage, withheld: boolean):
55
57
  * Every other grant and field is unchanged. Returns `m` by reference when nothing is dropped; never mutates `m`.
56
58
  */
57
59
  export declare function withSearchCapability(m: BridgeMessage, consumerHasFeature: boolean, signedProvider: SearchProvider | undefined): BridgeMessage;
60
+ /**
61
+ * Weather/currency spec §8.1 U6 item 3: the per-delivery utility filter, on every delivery path, judged by the
62
+ * consumer's OWN hello. A consumer that did not declare utility_tools_v1 (0.14.x and older runtimes, a harness, every
63
+ * HTTP path) never receives weather_forecast or currency_convert, marked or unmarked. Defence in depth beside the
64
+ * policy file and TOOL_FEATURE. Returns `m` by reference when nothing drops; never mutates `m`.
65
+ */
66
+ export declare function withUtilityCapability(m: BridgeMessage, consumerHasFeature: boolean): BridgeMessage;
package/dist/errors.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * WebSocket returns `{type:"error", error, message, detail?}`. A client gets the
7
7
  * same `error` code regardless of which transport it used. (A2A §9.5/§10.6 pattern.)
8
8
  */
9
- export type BridgeErrorCode = "unauthorized" | "forbidden_origin" | "not_found" | "invalid_request" | "invalid_json" | "body_too_large" | "channel_pending" | "rate_limited" | "agent_paused" | "send_forbidden" | "group_superseded" | "unknown_action_run" | "vault_integrity_failed" | "vault_ref_changed" | "session_superseded" | "internal";
9
+ export type BridgeErrorCode = "unauthorized" | "forbidden_origin" | "not_found" | "invalid_request" | "invalid_json" | "body_too_large" | "channel_pending" | "rate_limited" | "agent_paused" | "send_forbidden" | "group_superseded" | "unknown_action_run" | "vault_integrity_failed" | "vault_ref_changed" | "session_superseded" | "reply_credit_unknown" | "internal";
10
10
  export interface BridgeErrorSpec {
11
11
  /** HTTP status this code maps to (the WS binding ignores it). */
12
12
  http: number;
@@ -64,8 +64,21 @@ export declare function legacyAssignToken(d: {
64
64
  fact_hash: string;
65
65
  channel_id: string;
66
66
  }): string;
67
- /** The manifest's signed tools for one audience, inside the code ceiling; web_extract only with the capability (D19 item 1, D28). */
68
- export declare function signedManifestTools(manifest: ToolGrantManifest, audience: "owner_dm" | "team_room", searchProvidersCapable: boolean): string[];
67
+ /**
68
+ * Weather/currency spec §8.1 (U6, H-1): a runtime hello feature that a manifest tool needs. An older runtime reads an
69
+ * unknown name in manifest-policy.json as an unreadable file and drops EVERY manifest grant, so a tool listed here is
70
+ * written to the policy file (and delivered) only when every attached runtime socket declared its feature.
71
+ */
72
+ export type Feature = "search_providers_v2" | "utility_tools_v1";
73
+ export type FeatureSet = ReadonlySet<Feature>;
74
+ /** No socket attached, or none declared anything: fail closed. */
75
+ export declare const NO_FEATURES: FeatureSet;
76
+ /** The tool to feature table. A tool absent from it needs no feature. */
77
+ export declare const TOOL_FEATURE: Readonly<Record<string, Feature>>;
78
+ /** True when `tool` needs no feature, or its feature is in `features`. */
79
+ export declare function toolAllowedByFeatures(tool: string, features: FeatureSet): boolean;
80
+ /** The manifest's signed tools for one audience, inside the code ceiling; a TOOL_FEATURE tool only with its feature (D19 item 1, D28, U6). */
81
+ export declare function signedManifestTools(manifest: ToolGrantManifest, audience: "owner_dm" | "team_room", features: FeatureSet): string[];
69
82
  /**
70
83
  * manifest-policy.json from the stored manifest, the kill switches and the capability alone (D35): the bridge
71
84
  * recomputes it locally on a capability change, without a refetch or a cached BindResult. An audience switched off
@@ -74,13 +87,13 @@ export declare function signedManifestTools(manifest: ToolGrantManifest, audienc
74
87
  export declare function manifestPolicyFor(manifest: ToolGrantManifest, switches: {
75
88
  readonly ownerDm: boolean;
76
89
  readonly room: boolean;
77
- }, searchProvidersCapable: boolean): ManifestPolicyDoc;
90
+ }, features: FeatureSet): ManifestPolicyDoc;
78
91
  export declare function bindRuntimeConfig(manifest: ToolGrantManifest, payload: BindPayload, prior: BindPrior,
79
92
  /** Review H-1: the two manifest kill switches; an audience switched off contributes no tool to the policy file. */
80
93
  switches?: {
81
94
  readonly ownerDm: boolean;
82
95
  readonly room: boolean;
83
96
  },
84
- /** D19 item 1, D28: every attached runtime socket declared search_providers_v2. Default false = fail closed:
97
+ /** D19 item 1, D28, U6: the features every attached runtime socket declared. Default empty = fail closed:
85
98
  * an older runtime reads an unknown tool in manifest-policy.json as an unreadable file and drops every grant. */
86
- searchProvidersCapable?: boolean): BindResult;
99
+ features?: FeatureSet): BindResult;
@@ -58,47 +58,21 @@ export declare function capabilities(opts: {
58
58
  webhook_push: boolean;
59
59
  vault_ref: number;
60
60
  };
61
- export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
61
+ export declare const memoryRecordSchema: z.ZodObject<{
62
62
  conversation_id: z.ZodString;
63
- role: z.ZodEnum<["peer", "agent"]>;
63
+ role: z.ZodEnum<{
64
+ agent: "agent";
65
+ peer: "peer";
66
+ }>;
64
67
  peer_id: z.ZodOptional<z.ZodString>;
65
68
  text: z.ZodString;
66
- modality: z.ZodEnum<["text", "voice"]>;
69
+ modality: z.ZodEnum<{
70
+ text: "text";
71
+ voice: "voice";
72
+ }>;
67
73
  message_id: z.ZodOptional<z.ZodString>;
68
74
  idempotency_key: z.ZodOptional<z.ZodString>;
69
- }, "strip", z.ZodTypeAny, {
70
- text: string;
71
- role: "agent" | "peer";
72
- conversation_id: string;
73
- modality: "text" | "voice";
74
- message_id?: string | undefined;
75
- idempotency_key?: string | undefined;
76
- peer_id?: string | undefined;
77
- }, {
78
- text: string;
79
- role: "agent" | "peer";
80
- conversation_id: string;
81
- modality: "text" | "voice";
82
- message_id?: string | undefined;
83
- idempotency_key?: string | undefined;
84
- peer_id?: string | undefined;
85
- }>, {
86
- text: string;
87
- role: "agent" | "peer";
88
- conversation_id: string;
89
- modality: "text" | "voice";
90
- message_id?: string | undefined;
91
- idempotency_key?: string | undefined;
92
- peer_id?: string | undefined;
93
- }, {
94
- text: string;
95
- role: "agent" | "peer";
96
- conversation_id: string;
97
- modality: "text" | "voice";
98
- message_id?: string | undefined;
99
- idempotency_key?: string | undefined;
100
- peer_id?: string | undefined;
101
- }>;
75
+ }, z.core.$strip>;
102
76
  export declare function createHttpServer(agent: BridgeAgent, opts: HttpServerOptions): {
103
77
  server: Server;
104
78
  closeAllSseStreams: () => void;