@securecoms/agent-bridge 0.25.4 → 0.26.1
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 +15 -0
- package/connectors/hermes/adapter.py +24 -0
- package/connectors/hermes/tests/test_adapter_wiring.py +29 -0
- package/connectors/openclaw/openclaw-entry.inbound.test.ts +16 -0
- package/connectors/openclaw/openclaw-entry.js +23 -0
- package/dist/attachments.d.ts +55 -0
- package/dist/bin.js +500 -81
- package/dist/bridge-agent.d.ts +110 -35
- package/dist/config.d.ts +8 -0
- package/dist/errors.d.ts +1 -1
- package/dist/http-server.d.ts +5 -4
- package/dist/index.js +500 -81
- package/dist/vault.d.ts +23 -0
- package/dist/version.d.ts +1 -1
- package/dist/voice/defer-intent.d.ts +22 -0
- package/dist/voice/fast-prompt.d.ts +14 -0
- package/dist/voice/prompt-compiler.d.ts +8 -0
- package/package.json +1 -1
package/dist/bridge-agent.d.ts
CHANGED
|
@@ -31,10 +31,10 @@ export type ChannelObservation = {
|
|
|
31
31
|
import { type OwnerAuthorityStore, type OwnerPin } from "@agentvault/connect-clients-core";
|
|
32
32
|
import type { BridgeConfig } from "./config.js";
|
|
33
33
|
import { LatencyRecorder } from "./latency.js";
|
|
34
|
-
import type { AttachmentMeta } from "./attachments.js";
|
|
34
|
+
import type { AttachmentMeta, VaultRefEntry } from "./attachments.js";
|
|
35
35
|
import { type ChatMessage } from "./voice/fast-prompt.js";
|
|
36
36
|
import { VOICE_CALL_START_MARKER } from "./voice/prompt-compiler.js";
|
|
37
|
-
import { type VaultListNode } from "./vault.js";
|
|
37
|
+
import { type VaultListNode, type VaultReadExpectations } from "./vault.js";
|
|
38
38
|
import type { AgentActionNodeType, AgentActionNodeStatus, AgentActionNodeDetail } from "@agentvault/connect-core";
|
|
39
39
|
import type { RuntimeReplyReceipt } from "./reply-receipts.js";
|
|
40
40
|
export type AgentAudience = "owner_dm" | "team_room" | "partner_room" | "other_human_dm" | "agent_to_agent" | "unknown";
|
|
@@ -94,6 +94,16 @@ export interface BridgeMessage {
|
|
|
94
94
|
* Undefined for plain `text` messages.
|
|
95
95
|
*/
|
|
96
96
|
attachments?: AttachmentMeta[];
|
|
97
|
+
/**
|
|
98
|
+
* Files link (`vault_ref`, review 2026-10-01 section 6): references to
|
|
99
|
+
* FileSafe items, NOT fetched. Open one with POST /v1/vault-read passing
|
|
100
|
+
* `enclave_id`, `node_id`, `expected_content_hash` = `content_hash` and
|
|
101
|
+
* `expected_dek_commit` = `dek_commit`; the daemon refuses it if the item
|
|
102
|
+
* changed after it was shared. `name_hint`/`mime_hint`/`size_hint` are
|
|
103
|
+
* UNTRUSTED claims by the sender, data only (like `speaker`): never a path,
|
|
104
|
+
* an instruction or an authorization input. Undefined for every other turn.
|
|
105
|
+
*/
|
|
106
|
+
vault_refs?: VaultRefEntry[];
|
|
97
107
|
/**
|
|
98
108
|
* Task 4 (voice-mode carrier): true when the InboxEntry that produced this
|
|
99
109
|
* buffered message had `voiceMode` set by the server (the agent device's
|
|
@@ -129,6 +139,8 @@ export interface BridgeMessage {
|
|
|
129
139
|
* for voice-flagged turns. Selected by `InboxEntry.voiceMode` in the entry
|
|
130
140
|
* loop below — never transmitted, never a playback trigger.
|
|
131
141
|
*/
|
|
142
|
+
/** Files link: the turn text of a `vault_ref` that carries no caption. */
|
|
143
|
+
export declare const VAULT_REF_BARE_TEXT = "[Files link]";
|
|
132
144
|
export declare const VOICE_MODE_GUIDANCE = "Reply immediately and conversationally, one short sentence per message; never bundle multiple points into one message.";
|
|
133
145
|
/**
|
|
134
146
|
* Task 2 (room voice guidance): compiled reply guidance for a voice-flagged
|
|
@@ -370,7 +382,7 @@ export declare class InvalidMessageIdError extends Error {
|
|
|
370
382
|
* `messageId`, formatted as an RFC 4122 UUID. Live finding 2026-09-06: the
|
|
371
383
|
* previous `fast-<messageId>` string failed `resolveMessageId` ("message_id
|
|
372
384
|
* must be a UUID") on every turn, so the fast path never sent once. Same input
|
|
373
|
-
* → same UUID, so the idempotency argument in `
|
|
385
|
+
* → same UUID, so the idempotency argument in `runFastReply`'s doc still holds.
|
|
374
386
|
*
|
|
375
387
|
* SEC-06 #18: KEYED with the identity-seed-derived reply key and bound to the
|
|
376
388
|
* agent and channel. It used to be SHA-256 of `fast:<messageId>` alone, which
|
|
@@ -678,6 +690,8 @@ export declare class BridgeAgent {
|
|
|
678
690
|
* counter is incremented. Surfaced on health()/metrics().
|
|
679
691
|
*/
|
|
680
692
|
private failedAttachments;
|
|
693
|
+
/** Files link: vault_ref bodies refused by R1 (strict parse) or R2 (enclave binding). */
|
|
694
|
+
private refusedVaultRefs;
|
|
681
695
|
/**
|
|
682
696
|
* Task 4 (self-hosted fast voice): test/deploy seam for the fast-path LLM
|
|
683
697
|
* client. Undefined whenever `cfg.voice?.fastPath.enabled` is not true —
|
|
@@ -718,6 +732,16 @@ export declare class BridgeAgent {
|
|
|
718
732
|
private fastPathFailed;
|
|
719
733
|
/** Cumulative count of fast-path turns skipped in a room channel because the shared send-floor bucket lacked headroom for both the ack and the runtime's own reply (controller ruling) — surfaced on health()/metrics(). */
|
|
720
734
|
private fastPathSkipped;
|
|
735
|
+
/** Off-cycle fast replies: per-channel promise chains + the in-flight job count. */
|
|
736
|
+
private readonly fastReplyChains;
|
|
737
|
+
private fastReplyJobs;
|
|
738
|
+
/** Room send-floor tokens reserved by queued fast replies, per channel (F3). */
|
|
739
|
+
private readonly fastReplyReserved;
|
|
740
|
+
private static readonly FAST_REPLY_MAX_JOBS;
|
|
741
|
+
/** Fast-path turns handed to the runtime behind a spoken ack (LLM [[DEFER]], pre-check, or ack_only). */
|
|
742
|
+
private fastPathDeferred;
|
|
743
|
+
/** Of those, `full`-mode turns the deterministic pre-check deferred without calling the fast model. */
|
|
744
|
+
private fastPathIntentDeferred;
|
|
721
745
|
/**
|
|
722
746
|
* Task 8: count of live AUTHENTICATED /v1/ws sessions, so a later `status`
|
|
723
747
|
* verb can tell "bridge process up" (the app's green dot, which only
|
|
@@ -1581,7 +1605,7 @@ export declare class BridgeAgent {
|
|
|
1581
1605
|
* useVault.downloadFile returns `{name, bytes}` in-memory — here the
|
|
1582
1606
|
* caller already supplies nodeId so we return bytes, not a re-resolved
|
|
1583
1607
|
* name the bridge has no cached listing to look up). */
|
|
1584
|
-
vaultRead(enclaveId: string, vaultChannelId: string | null | undefined, nodeId: string): Promise<{
|
|
1608
|
+
vaultRead(enclaveId: string, vaultChannelId: string | null | undefined, nodeId: string, expected?: VaultReadExpectations): Promise<{
|
|
1585
1609
|
bytes_b64: string;
|
|
1586
1610
|
}>;
|
|
1587
1611
|
/** List + decrypt names for the vault nodes under `parentId` (null = root). */
|
|
@@ -1618,37 +1642,69 @@ export declare class BridgeAgent {
|
|
|
1618
1642
|
private memoryForTurn;
|
|
1619
1643
|
memoryContext(conversationId: string, limit?: number): Promise<import("@agentvault/connect-mls-store").MemoryContextResult>;
|
|
1620
1644
|
/**
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
1623
|
-
*
|
|
1624
|
-
*
|
|
1625
|
-
*
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1628
|
-
*
|
|
1629
|
-
*
|
|
1630
|
-
*
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1634
|
-
*
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
*
|
|
1640
|
-
* channel
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1643
|
-
*
|
|
1644
|
-
*
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
*
|
|
1649
|
-
*
|
|
1650
|
-
|
|
1651
|
-
|
|
1645
|
+
* Voice front desk, step 1 — decided SYNCHRONOUSLY inside the ingest cycle,
|
|
1646
|
+
* so the cycle never waits on a model call or a publish (Cortina011
|
|
1647
|
+
* recv.cycle max 10.8 s came from two inline fast replies back to back).
|
|
1648
|
+
* Pure: it changes no state; the caller commits a plan (markFastPathSeen,
|
|
1649
|
+
* counters) only after the turn is durably staged.
|
|
1650
|
+
* - "dup": this turn already went through the fast path (a redelivery);
|
|
1651
|
+
* it is neither spoken again nor handed to the runtime again.
|
|
1652
|
+
* - "skip": no fast path; the runtime gets the turn with plain guidance
|
|
1653
|
+
* (mode `off`, no fast client, or the reply queue is full).
|
|
1654
|
+
* - "ack": the turn certainly goes to the runtime (mode `ack_only`, or the
|
|
1655
|
+
* deterministic pre-check in `full`): `line` is spoken first.
|
|
1656
|
+
* - "fast": the fast model (or, in `ack_only`, the greeting template)
|
|
1657
|
+
* decides whether the runtime answers next.
|
|
1658
|
+
* Owner report 2026-10-02 (calls never used tools) motivates "ack".
|
|
1659
|
+
*/
|
|
1660
|
+
private planFastPath;
|
|
1661
|
+
private markFastPathSeen;
|
|
1662
|
+
/**
|
|
1663
|
+
* Off-cycle per-channel queue: one promise chain per channel, so within a
|
|
1664
|
+
* channel spoken lines publish, and runtime frames are released, strictly
|
|
1665
|
+
* in turn order (a turn's ack always publishes before its frame reaches
|
|
1666
|
+
* the runtime). `spends` marks a job that will publish a fast reply: those
|
|
1667
|
+
* are bounded by FAST_REPLY_MAX_JOBS (planFastPath degrades to "skip") and
|
|
1668
|
+
* reserve a room send-floor token until they finish. A job never throws.
|
|
1669
|
+
*/
|
|
1670
|
+
private enqueueFastReply;
|
|
1671
|
+
/**
|
|
1672
|
+
* Stage a runtime frame without overtaking a turn of the same channel that
|
|
1673
|
+
* is still queued behind the fast path (F2): while the channel has a queue,
|
|
1674
|
+
* the frame is staged durably but held, and released in queue order.
|
|
1675
|
+
*/
|
|
1676
|
+
private stageRuntimeMessageInOrder;
|
|
1677
|
+
/** Test seam: resolves once every queued fast reply has settled. */
|
|
1678
|
+
settleFastReplies(): Promise<void>;
|
|
1679
|
+
/** Speak a fixed acknowledgement line; failures are counted, never thrown. */
|
|
1680
|
+
private sendFastLine;
|
|
1681
|
+
/**
|
|
1682
|
+
* Voice front desk, step 2 for a "fast" plan (runs off the ingest cycle):
|
|
1683
|
+
* the fast model's reply — or, in `ack_only`, the greeting template — is
|
|
1684
|
+
* spoken, and the outcome decides whether the runtime answers next.
|
|
1685
|
+
*
|
|
1686
|
+
* MUST NEVER throw: every failure path (memory lookup, the LLM call,
|
|
1687
|
+
* normalization, or the send itself) is caught, counted in
|
|
1688
|
+
* `fastPathFailed`, and logged WITHOUT the turn text or the model's reply.
|
|
1689
|
+
* The `fastReplyId(...)` send id is deterministic, so a duplicate that
|
|
1690
|
+
* slipped past the in-memory dedupe (e.g. a restart) resolves to the same
|
|
1691
|
+
* envelope message id rather than a second spoken reply.
|
|
1692
|
+
*
|
|
1693
|
+
* Room token reservation (controller ruling): the CALL SITE only plans a
|
|
1694
|
+
* fast path when the room bucket has headroom for this reply AND the
|
|
1695
|
+
* runtime's, and decides the buffered guidance from that same decision.
|
|
1696
|
+
*/
|
|
1697
|
+
private runFastReply;
|
|
1698
|
+
/**
|
|
1699
|
+
* Durably stage a runtime frame WITHOUT handing it to the runtime yet (a
|
|
1700
|
+
* "fast" plan: the fast reply decides). Same durability and backpressure
|
|
1701
|
+
* as stageRuntimeMessage; a restart restores it as an ordinary pending
|
|
1702
|
+
* frame (the "fast path failed" semantics), so a crash mid-call never loses
|
|
1703
|
+
* the turn.
|
|
1704
|
+
*/
|
|
1705
|
+
private stageHeldRuntimeMessage;
|
|
1706
|
+
/** Settle a held frame: the fast path answered alone (retire it) or the runtime answers next (release it, in channel order). */
|
|
1707
|
+
private settleHeldRuntimeMessage;
|
|
1652
1708
|
/**
|
|
1653
1709
|
* List channels in one enclave (default: the daemon's own home enclave).
|
|
1654
1710
|
* Task 3: called per-*known* enclave by refreshChannelMeta so a
|
|
@@ -1703,6 +1759,15 @@ export declare class BridgeAgent {
|
|
|
1703
1759
|
* 5xx, and genuine non-member 404s only.
|
|
1704
1760
|
*/
|
|
1705
1761
|
private refreshChannelMeta;
|
|
1762
|
+
/**
|
|
1763
|
+
* Cache server-listed channel metadata (is_dm, name) for the receive cycle.
|
|
1764
|
+
* The ONLY input is a `listChannels` result, the same backend rows
|
|
1765
|
+
* refreshChannelMeta has always cached: refreshChannelMeta and the
|
|
1766
|
+
* self-heal sweep both feed it, so the sweep's own listing (every 90-120 s)
|
|
1767
|
+
* keeps the 5 min cache warm and a turn arriving after idle does not pay a
|
|
1768
|
+
* listing round trip per enclave before it reaches the runtime.
|
|
1769
|
+
*/
|
|
1770
|
+
private recordListedChannelMeta;
|
|
1706
1771
|
/** Cache for the server-side enclave enumeration (see enclavesToCover). */
|
|
1707
1772
|
private enclaveEnumCache;
|
|
1708
1773
|
/** Enclaves whose channel listing failed on the NEWEST sweep (audit HIGH:
|
|
@@ -2142,12 +2207,20 @@ export declare class BridgeAgent {
|
|
|
2142
2207
|
custody_store_unavailable: boolean;
|
|
2143
2208
|
/** Track 5: cumulative artifact_share messages where download/decrypt/digest failed (fail-soft — acked, no redeliver). */
|
|
2144
2209
|
failed_attachments: number;
|
|
2210
|
+
/** Files link: vault_ref bodies refused by R1 (strict parse) or R2 (enclave binding), acked. */
|
|
2211
|
+
refused_vault_refs: number;
|
|
2145
2212
|
/** Task 4 (self-hosted fast voice): cumulative fast-path replies actually sent. */
|
|
2146
2213
|
fast_path_sent: number;
|
|
2147
2214
|
/** Task 4 (self-hosted fast voice): cumulative fast-path attempts that errored (LLM call, normalization, or send) — fail-soft, never blocks the buffered turn. */
|
|
2148
2215
|
fast_path_failed: number;
|
|
2149
2216
|
/** Task 4 (self-hosted fast voice): cumulative fast-path turns skipped in a room because the send-floor bucket lacked headroom for both the ack and the runtime's reply. */
|
|
2150
2217
|
fast_path_skipped: number;
|
|
2218
|
+
/** Fast-path turns handed to the runtime behind a spoken ack (LLM [[DEFER]], pre-check, or ack_only). */
|
|
2219
|
+
fast_path_deferred: number;
|
|
2220
|
+
/** `full`-mode turns deferred by the deterministic pre-check (no fast model call). */
|
|
2221
|
+
fast_path_intent_deferred: number;
|
|
2222
|
+
/** Credential `voice_fast_path_mode`: "full" (default), "ack_only" or "off". */
|
|
2223
|
+
fast_path_mode: "full" | "ack_only" | "off";
|
|
2151
2224
|
/** Plan 2026-09-06 (agent voice settings): version of the loaded backend
|
|
2152
2225
|
* voice-settings row, or null when no backend row has ever loaded
|
|
2153
2226
|
* (source is "file" or "none"). */
|
|
@@ -2292,6 +2365,8 @@ export declare class BridgeAgent {
|
|
|
2292
2365
|
welcomeRequestsSent: number;
|
|
2293
2366
|
/** Track 5: cumulative artifact_share failures (download/decrypt/digest). */
|
|
2294
2367
|
failedAttachments: number;
|
|
2368
|
+
/** Files link: vault_ref bodies refused by R1 (strict parse) or R2 (enclave binding), acked. */
|
|
2369
|
+
refusedVaultRefs: number;
|
|
2295
2370
|
/** SDK-6: bridge build id, so a /v1/metrics reading captured across a daemon
|
|
2296
2371
|
* restart can be attributed to a version — counters are process-lifetime
|
|
2297
2372
|
* and zero on restart (spec §5.5). */
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type VoiceConfig } from "./voice/persona.js";
|
|
2
|
+
import { type FastPathMode } from "./voice/defer-intent.js";
|
|
2
3
|
export interface BridgeConfig {
|
|
3
4
|
apiKey: string;
|
|
4
5
|
identityId: string;
|
|
@@ -30,6 +31,13 @@ export interface BridgeConfig {
|
|
|
30
31
|
memoryFactsCharBudget?: number;
|
|
31
32
|
/** Optional voice persona + fast-path LLM config (self-hosted fast voice reply). Undefined when the credential has no `voice` block. */
|
|
32
33
|
voice?: VoiceConfig;
|
|
34
|
+
/**
|
|
35
|
+
* Voice fast-path mode (credential top-level `voice_fast_path_mode`):
|
|
36
|
+
* "full" (default), "ack_only" or "off" — see voice/defer-intent.ts. Kept
|
|
37
|
+
* outside `voice` because backend-managed agents have no `voice` block and
|
|
38
|
+
* a backend voice-settings refetch replaces `voice` wholesale.
|
|
39
|
+
*/
|
|
40
|
+
voiceFastPathMode?: FastPathMode;
|
|
33
41
|
}
|
|
34
42
|
export declare const REQUIRED: readonly ["api_key", "identity_id", "device_id", "signing_private_key", "enclave_id", "api_base"];
|
|
35
43
|
export declare function loadConfig(opts: {
|
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" | "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" | "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/http-server.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export declare function capabilities(opts: {
|
|
|
43
43
|
receive_modes: string[];
|
|
44
44
|
send_types: string[];
|
|
45
45
|
webhook_push: boolean;
|
|
46
|
+
vault_ref: number;
|
|
46
47
|
};
|
|
47
48
|
export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
|
|
48
49
|
conversation_id: z.ZodString;
|
|
@@ -56,7 +57,7 @@ export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
|
|
|
56
57
|
text: string;
|
|
57
58
|
role: "agent" | "peer";
|
|
58
59
|
conversation_id: string;
|
|
59
|
-
modality: "
|
|
60
|
+
modality: "text" | "voice";
|
|
60
61
|
message_id?: string | undefined;
|
|
61
62
|
idempotency_key?: string | undefined;
|
|
62
63
|
peer_id?: string | undefined;
|
|
@@ -64,7 +65,7 @@ export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
|
|
|
64
65
|
text: string;
|
|
65
66
|
role: "agent" | "peer";
|
|
66
67
|
conversation_id: string;
|
|
67
|
-
modality: "
|
|
68
|
+
modality: "text" | "voice";
|
|
68
69
|
message_id?: string | undefined;
|
|
69
70
|
idempotency_key?: string | undefined;
|
|
70
71
|
peer_id?: string | undefined;
|
|
@@ -72,7 +73,7 @@ export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
|
|
|
72
73
|
text: string;
|
|
73
74
|
role: "agent" | "peer";
|
|
74
75
|
conversation_id: string;
|
|
75
|
-
modality: "
|
|
76
|
+
modality: "text" | "voice";
|
|
76
77
|
message_id?: string | undefined;
|
|
77
78
|
idempotency_key?: string | undefined;
|
|
78
79
|
peer_id?: string | undefined;
|
|
@@ -80,7 +81,7 @@ export declare const memoryRecordSchema: z.ZodEffects<z.ZodObject<{
|
|
|
80
81
|
text: string;
|
|
81
82
|
role: "agent" | "peer";
|
|
82
83
|
conversation_id: string;
|
|
83
|
-
modality: "
|
|
84
|
+
modality: "text" | "voice";
|
|
84
85
|
message_id?: string | undefined;
|
|
85
86
|
idempotency_key?: string | undefined;
|
|
86
87
|
peer_id?: string | undefined;
|