@securecoms/agent-bridge 0.33.1 → 0.35.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.
- package/README.md +61 -3
- package/dist/attachments.d.ts +13 -0
- package/dist/bin.js +687 -325
- package/dist/bridge-agent.d.ts +53 -7
- package/dist/consumer-grants.d.ts +9 -0
- package/dist/errors.d.ts +1 -1
- package/dist/grants/binding.d.ts +18 -5
- package/dist/http-server.d.ts +10 -36
- package/dist/index.js +626 -264
- package/dist/outbox.d.ts +42 -0
- package/dist/reply-credit.d.ts +22 -0
- package/dist/reply-intent.d.ts +6 -1
- package/dist/service/runtime-config-dir.d.ts +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +1 -1
- package/package.json +2 -2
package/dist/bridge-agent.d.ts
CHANGED
|
@@ -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 {
|
|
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";
|
|
@@ -506,6 +507,13 @@ export interface BridgeAgentDeps {
|
|
|
506
507
|
* tests to avoid real file IO / assert pin persistence directly.
|
|
507
508
|
*/
|
|
508
509
|
vaultOwnerStore?: OwnerAuthorityStore;
|
|
510
|
+
/**
|
|
511
|
+
* Security fix 2026-10-30 (option A): the ONLY directory `/v1/vault-write`
|
|
512
|
+
* and `/v1/send-attachment` read from. Defaults to
|
|
513
|
+
* `join(dirname(cfg.storePath), "vault-outbox")`, or null (every upload
|
|
514
|
+
* refused) for an in-memory store. Override in tests.
|
|
515
|
+
*/
|
|
516
|
+
vaultOutboxDir?: string | null;
|
|
509
517
|
}
|
|
510
518
|
/**
|
|
511
519
|
* Voice repeats (a), owner report 2026-10-02: one spoken question reached the
|
|
@@ -692,6 +700,11 @@ export interface SendOptions {
|
|
|
692
700
|
* enter the call buffer. `POST /v1/send`, SSE and harness replies never set it.
|
|
693
701
|
*/
|
|
694
702
|
callBufferReply?: true;
|
|
703
|
+
/**
|
|
704
|
+
* Weather/currency Task 17 (OQ-P1): set only by the ws send handler from a frame whose credit is the closed value.
|
|
705
|
+
* The published text gets the U-25 suffix; history, unified memory and the call buffer get the text without it.
|
|
706
|
+
*/
|
|
707
|
+
credit?: ReplyCredit;
|
|
695
708
|
}
|
|
696
709
|
/**
|
|
697
710
|
* 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
|
|
@@ -1514,6 +1527,9 @@ export declare class BridgeAgent {
|
|
|
1514
1527
|
private readonly uploadBlobImpl;
|
|
1515
1528
|
/** Track 5: directory where received attachments are saved. */
|
|
1516
1529
|
private readonly attachmentsDirImpl;
|
|
1530
|
+
private readonly vaultOutboxDirImpl;
|
|
1531
|
+
/** Audit 2026-10-30 HIGH 2: the outbox pinned at start(); null = every upload refused. */
|
|
1532
|
+
private outboxPin;
|
|
1517
1533
|
/** B7: persistence for the vault kekset's owner-pin TOFU state. */
|
|
1518
1534
|
private readonly vaultOwnerStoreImpl;
|
|
1519
1535
|
/** B7: last-seen kekset generation per enclave, enforced monotonic across
|
|
@@ -2047,7 +2063,12 @@ export declare class BridgeAgent {
|
|
|
2047
2063
|
*/
|
|
2048
2064
|
private reclassifyForReply;
|
|
2049
2065
|
private enforceBodyRateFloor;
|
|
2050
|
-
/**
|
|
2066
|
+
/**
|
|
2067
|
+
* Bookkeeping after a CONFIRMED publish of `body` under `id`. `recordText` (weather/currency Task 17) is what a text
|
|
2068
|
+
* reply is RECORDED as: the published text minus any credit line. Every record site below reads `recorded`, never
|
|
2069
|
+
* `body.text`. Every production caller passes it (pinned by reply-credit.test.ts: text, or null for an a2a body);
|
|
2070
|
+
* undefined is left only for direct test calls of an uncredited body, where the published and record texts are equal.
|
|
2071
|
+
*/
|
|
2051
2072
|
private afterPublish;
|
|
2052
2073
|
/**
|
|
2053
2074
|
* Final review bridge I-1 (ledger T25): the agent reply's U11 capture, taken BEFORE the publish await. The clear generation
|
|
@@ -2103,6 +2124,12 @@ export declare class BridgeAgent {
|
|
|
2103
2124
|
* scope and part, and its body matches its MAC (which binds id and channel).
|
|
2104
2125
|
*/
|
|
2105
2126
|
private intentVerifies;
|
|
2127
|
+
/**
|
|
2128
|
+
* Weather/currency Task 17: what a verified row's reply is RECORDED as. Null credit: the decoded text unchanged (null
|
|
2129
|
+
* for an a2a body). "weatherapi": the text minus the exact credit suffix. Undefined: the row is refused (an unknown
|
|
2130
|
+
* credit, a credited a2a body, or a credited text without the exact suffix).
|
|
2131
|
+
*/
|
|
2132
|
+
private recordTextOf;
|
|
2106
2133
|
/**
|
|
2107
2134
|
* SEC-06 #18 item 3: a rate-limited reply retries the PUBLISH in-process
|
|
2108
2135
|
* with backoff, never the model turn; so does an exact resend whose
|
|
@@ -2317,12 +2344,17 @@ export declare class BridgeAgent {
|
|
|
2317
2344
|
* checks, so the HTTP boundary maps ALL vault trust failures to one code.
|
|
2318
2345
|
*/
|
|
2319
2346
|
private getVaultSession;
|
|
2320
|
-
/** Read `path`
|
|
2347
|
+
/** Read `path` from the vault outbox (security fix 2026-10-30; see
|
|
2348
|
+
* ./outbox.ts), encrypt + wrap its DEK under the vault's
|
|
2321
2349
|
* current KEK generation, and create+upload+commit it as a new vault
|
|
2322
2350
|
* file node named `name` (which may differ from `path`'s basename). */
|
|
2323
2351
|
vaultWrite(enclaveId: string, vaultChannelId: string | null | undefined, path: string, name: string): Promise<{
|
|
2324
2352
|
node_id: string;
|
|
2325
2353
|
}>;
|
|
2354
|
+
/** Audit 2026-10-30 MED 5: an uploaded file leaves the outbox (no plaintext lingers). Content-free log. */
|
|
2355
|
+
private consumeUploaded;
|
|
2356
|
+
/** Audit 2026-10-30 MED 3: the pinned outbox path, served only on the authenticated GET /v1/vault-outbox. */
|
|
2357
|
+
vaultOutboxPath(): string | null;
|
|
2326
2358
|
/** Download + verify + decrypt a vault file node. Returns the plaintext
|
|
2327
2359
|
* base64-encoded (the loopback boundary is JSON-only, mirroring how
|
|
2328
2360
|
* useVault.downloadFile returns `{name, bytes}` in-memory — here the
|
|
@@ -3504,17 +3536,31 @@ export declare class BridgeAgent {
|
|
|
3504
3536
|
*/
|
|
3505
3537
|
private readonly searchProvidersSockets;
|
|
3506
3538
|
/**
|
|
3507
|
-
*
|
|
3508
|
-
*
|
|
3539
|
+
* Weather/currency U6 (H-1): the same AND-over-sockets state for utility_tools_v1, kept separately per socket, so a
|
|
3540
|
+
* runtime that declares search_providers_v2 but not utility_tools_v1 (0.14.0, 0.14.1) keeps web_extract and never
|
|
3541
|
+
* sees a utility name.
|
|
3542
|
+
*/
|
|
3543
|
+
private readonly utilityToolsSockets;
|
|
3544
|
+
/**
|
|
3545
|
+
* The (search, utility) tuple the files were last recomputed for; null until the first socket observation, so that
|
|
3546
|
+
* observation always recomputes (final review I-1), even when it is non-capable. One trigger keyed on BOTH values.
|
|
3509
3547
|
*/
|
|
3510
|
-
private
|
|
3548
|
+
private capabilityState;
|
|
3511
3549
|
private searchCapabilityRefetchTimer;
|
|
3512
3550
|
/** When the current burst's first change happened (valid while the timer is set). */
|
|
3513
3551
|
private searchCapabilityBurstStart;
|
|
3514
3552
|
/** The D28 value in the last report posted (null until one is); a change needs a refetch to reach the web. */
|
|
3515
3553
|
private lastReportedSearchProviders;
|
|
3554
|
+
/** U6: the utility_tools_v1 value in the last report posted (null until one is). */
|
|
3555
|
+
private lastReportedUtilityTools;
|
|
3516
3556
|
noteSearchProvidersSocket(token: object, declared: boolean): void;
|
|
3517
3557
|
clearSearchProvidersSocket(token: object): void;
|
|
3558
|
+
noteUtilityToolsSocket(token: object, declared: boolean): void;
|
|
3559
|
+
clearUtilityToolsSocket(token: object): void;
|
|
3560
|
+
/** U6: every attached runtime socket declared utility_tools_v1; no socket attached means absent (false). */
|
|
3561
|
+
utilityToolsCapable(): boolean;
|
|
3562
|
+
/** U6: the features every attached runtime socket declared, for the policy functions. */
|
|
3563
|
+
capabilityFeatures(): FeatureSet;
|
|
3518
3564
|
/** D28: every attached runtime socket declared search_providers_v2; no socket attached means absent (false). */
|
|
3519
3565
|
searchProvidersCapable(): boolean;
|
|
3520
3566
|
/**
|
|
@@ -3529,7 +3575,7 @@ export declare class BridgeAgent {
|
|
|
3529
3575
|
* D35 (P-4): on a capability change, rewrite manifest-policy.json at once from the stored manifest, the kill
|
|
3530
3576
|
* switches and the new capability, then a debounced refetch (one per burst) only when needed.
|
|
3531
3577
|
*/
|
|
3532
|
-
private
|
|
3578
|
+
private onCapabilityMaybeChanged;
|
|
3533
3579
|
/**
|
|
3534
3580
|
* A refetch re-renders skills.json (owner_dm_search_cap counts web_extract only with the capability, D39) and posts
|
|
3535
3581
|
* 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" | "path_outside_outbox" | "internal";
|
|
10
10
|
export interface BridgeErrorSpec {
|
|
11
11
|
/** HTTP status this code maps to (the WS binding ignores it). */
|
|
12
12
|
http: number;
|
package/dist/grants/binding.d.ts
CHANGED
|
@@ -64,8 +64,21 @@ export declare function legacyAssignToken(d: {
|
|
|
64
64
|
fact_hash: string;
|
|
65
65
|
channel_id: string;
|
|
66
66
|
}): string;
|
|
67
|
-
/**
|
|
68
|
-
|
|
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
|
-
},
|
|
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
|
|
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
|
-
|
|
99
|
+
features?: FeatureSet): BindResult;
|
package/dist/http-server.d.ts
CHANGED
|
@@ -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.
|
|
61
|
+
export declare const memoryRecordSchema: z.ZodObject<{
|
|
62
62
|
conversation_id: z.ZodString;
|
|
63
|
-
role: z.ZodEnum<
|
|
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<
|
|
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
|
-
},
|
|
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;
|