@securecoms/agent-bridge 0.26.1 → 0.26.3
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/dist/bin.js +342 -36
- package/dist/bridge-agent.d.ts +128 -3
- package/dist/index.js +342 -36
- package/dist/memory-taps.d.ts +3 -2
- package/dist/service-log.d.ts +14 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
package/dist/bridge-agent.d.ts
CHANGED
|
@@ -51,10 +51,32 @@ export type BridgeToolGrant = {
|
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
53
|
* Immutable authorization context derived from authenticated backend roster
|
|
54
|
-
* data. The E2E plaintext never contributes to these claims
|
|
55
|
-
*
|
|
56
|
-
*
|
|
54
|
+
* data. The E2E plaintext never contributes to these claims, with ONE bounded
|
|
55
|
+
* exception: a relayed voice turn's `speaker_identity_id`, honoured only when
|
|
56
|
+
* the envelope's authenticated sender is the channel's server-marked relay.
|
|
57
|
+
*
|
|
58
|
+
* Tool grants (architect ruling, 2026-10-02): until channel-grant persistence
|
|
59
|
+
* lands, the ONLY non-empty grant list is OWNER_DM_DEFAULT_READ_GRANTS
|
|
60
|
+
* (`current_time` + `web_search`, read mode), and only on a DM whose sender is
|
|
61
|
+
* the pinned structural owner (or a relayed voice turn whose speaker is). Every
|
|
62
|
+
* other audience, an unpinned or anomalous owner, and the kill switch
|
|
63
|
+
* `SECURECOMS_OWNER_DM_DEFAULT_GRANTS` (anything but unset/`on`) get `[]`.
|
|
64
|
+
* SEC-06 is LIVE for these two tools: docs/security/sec-06-runtime-bounds-audit.md.
|
|
65
|
+
*/
|
|
66
|
+
/**
|
|
67
|
+
* Architect ruling 2026-10-02: the default read grants an owner DM carries.
|
|
68
|
+
* A frozen constant in the daemon, NOT runtime config: a runtime cannot widen
|
|
69
|
+
* it, and the runtime's own tool allow-list still intersects it (a tool the
|
|
70
|
+
* runtime does not load is never offered). `fetch_url`, `echo`, `sql_query`
|
|
71
|
+
* and `http_request` are deliberately absent.
|
|
72
|
+
*/
|
|
73
|
+
export declare const OWNER_DM_DEFAULT_READ_GRANTS: readonly BridgeToolGrant[];
|
|
74
|
+
/**
|
|
75
|
+
* Kill switch for OWNER_DM_DEFAULT_READ_GRANTS, read once at BridgeAgent
|
|
76
|
+
* construction (daemon startup). Unset or exactly `on` keeps the grants;
|
|
77
|
+
* any other value (`off`, `0`, `false`, empty) sends `[]` on every turn.
|
|
57
78
|
*/
|
|
79
|
+
export declare function ownerDmDefaultGrantsEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
58
80
|
export interface BridgeAuthorization {
|
|
59
81
|
readonly version: 1;
|
|
60
82
|
readonly audience: AgentAudience;
|
|
@@ -272,6 +294,13 @@ export interface BridgeAgentDeps {
|
|
|
272
294
|
fastLlm?: {
|
|
273
295
|
complete(messages: ChatMessage[]): Promise<string>;
|
|
274
296
|
};
|
|
297
|
+
/**
|
|
298
|
+
* Voice repeats (a): how long a relayed transcribed voice turn waits for
|
|
299
|
+
* the rest of a VAD-split utterance, measured from its first fragment
|
|
300
|
+
* (default: env `SECURECOMS_VOICE_COALESCE_MS`, else 0 = disabled).
|
|
301
|
+
* Injected so tests do not wait the full window.
|
|
302
|
+
*/
|
|
303
|
+
voiceCoalesceWindowMs?: number;
|
|
275
304
|
/**
|
|
276
305
|
* B7 (FileSafe vault): persistence for the vault kekset's owner-pin TOFU
|
|
277
306
|
* state. Defaults to a {@link FileOwnerAuthorityStore} at
|
|
@@ -280,6 +309,22 @@ export interface BridgeAgentDeps {
|
|
|
280
309
|
*/
|
|
281
310
|
vaultOwnerStore?: OwnerAuthorityStore;
|
|
282
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Voice repeats (a), owner report 2026-10-02: one spoken question reached the
|
|
314
|
+
* bridge as two relayed turns ~2 s apart (the relay's VAD split it), which
|
|
315
|
+
* produced a fast reply to the fragment, an ack, and a runtime answer.
|
|
316
|
+
* Relayed transcribed voice turns from the same speaker on the same channel
|
|
317
|
+
* that arrive within this window of the FIRST fragment are joined into one
|
|
318
|
+
* turn before the fast path plans it. The cost is that every relayed voice
|
|
319
|
+
* turn waits this long before it is answered, so it is OFF by default (0):
|
|
320
|
+
* the observed ~2 s split would not fit a window short enough for live
|
|
321
|
+
* voice. Enable with env `SECURECOMS_VOICE_COALESCE_MS` (read at startup).
|
|
322
|
+
*/
|
|
323
|
+
export declare const VOICE_COALESCE_WINDOW_MS = 0;
|
|
324
|
+
/** Upper bound for the window: a typo must not park every voice turn for minutes. */
|
|
325
|
+
export declare const VOICE_COALESCE_WINDOW_MAX_MS = 5000;
|
|
326
|
+
/** `SECURECOMS_VOICE_COALESCE_MS` as integer ms, clamped to VOICE_COALESCE_WINDOW_MAX_MS; anything else is 0 (disabled). */
|
|
327
|
+
export declare function voiceCoalesceWindowFromEnv(env?: NodeJS.ProcessEnv): number;
|
|
283
328
|
/** Default inbound-buffer cap (messages) when deps.maxBuffer is unset. */
|
|
284
329
|
export declare const DEFAULT_MAX_BUFFER = 1000;
|
|
285
330
|
/**
|
|
@@ -738,6 +783,19 @@ export declare class BridgeAgent {
|
|
|
738
783
|
/** Room send-floor tokens reserved by queued fast replies, per channel (F3). */
|
|
739
784
|
private readonly fastReplyReserved;
|
|
740
785
|
private static readonly FAST_REPLY_MAX_JOBS;
|
|
786
|
+
/** Voice repeats (a): see {@link VOICE_COALESCE_WINDOW_MS}. */
|
|
787
|
+
private readonly voiceCoalesceWindowMs;
|
|
788
|
+
/** H1: fast-path reply ids being published right now (recorded as voice memory). */
|
|
789
|
+
private readonly spokenReplyIds;
|
|
790
|
+
/** Open coalescing windows, keyed by channel + speaker. */
|
|
791
|
+
private readonly voiceCoalescing;
|
|
792
|
+
/** Ids of every coalesced fragment (capped FIFO): a redelivered row is acked, never a second turn. */
|
|
793
|
+
private readonly voiceCoalescedIds;
|
|
794
|
+
/** Merged turn id -> the absorbed fragments' held frames, retired with it. */
|
|
795
|
+
private readonly voiceCoalescedTails;
|
|
796
|
+
/** (4) recv.lag: application queue ids already measured (capped FIFO), so a re-pulled row is not re-counted. */
|
|
797
|
+
private readonly recvLagSeen;
|
|
798
|
+
private static readonly RECV_LAG_SEEN_CAP;
|
|
741
799
|
/** Fast-path turns handed to the runtime behind a spoken ack (LLM [[DEFER]], pre-check, or ack_only). */
|
|
742
800
|
private fastPathDeferred;
|
|
743
801
|
/** Of those, `full`-mode turns the deterministic pre-check deferred without calling the fast model. */
|
|
@@ -830,6 +888,8 @@ export declare class BridgeAgent {
|
|
|
830
888
|
* back to the delivery-row-driven Task 11 behaviour only.
|
|
831
889
|
*/
|
|
832
890
|
private readonly selfHealSweepEnabled;
|
|
891
|
+
/** Read once at startup; see ownerDmDefaultGrantsEnabled. */
|
|
892
|
+
private readonly ownerDmDefaultGrants;
|
|
833
893
|
/** Task 12: proactive sweep cadence (in addition to the once-at-boot fire). */
|
|
834
894
|
private static readonly SELF_HEAL_SWEEP_INTERVAL_MS;
|
|
835
895
|
/**
|
|
@@ -1658,6 +1718,7 @@ export declare class BridgeAgent {
|
|
|
1658
1718
|
* Owner report 2026-10-02 (calls never used tools) motivates "ack".
|
|
1659
1719
|
*/
|
|
1660
1720
|
private planFastPath;
|
|
1721
|
+
private noteVoiceCoalesced;
|
|
1661
1722
|
private markFastPathSeen;
|
|
1662
1723
|
/**
|
|
1663
1724
|
* Off-cycle per-channel queue: one promise chain per channel, so within a
|
|
@@ -1668,6 +1729,15 @@ export declare class BridgeAgent {
|
|
|
1668
1729
|
* reserve a room send-floor token until they finish. A job never throws.
|
|
1669
1730
|
*/
|
|
1670
1731
|
private enqueueFastReply;
|
|
1732
|
+
/** Count a fast reply against FAST_REPLY_MAX_JOBS and reserve its room send-floor token; returns the release. */
|
|
1733
|
+
private reserveFastReply;
|
|
1734
|
+
/**
|
|
1735
|
+
* Speak a planned fast turn ("ack" or "fast") and settle its held frame:
|
|
1736
|
+
* retired when the fast model answered alone, otherwise released to the
|
|
1737
|
+
* runtime (with the "ack already spoken" guidance only when an ack or a
|
|
1738
|
+
* deferring fast reply was actually published — F3).
|
|
1739
|
+
*/
|
|
1740
|
+
private speakAndSettle;
|
|
1671
1741
|
/**
|
|
1672
1742
|
* Stage a runtime frame without overtaking a turn of the same channel that
|
|
1673
1743
|
* is still queued behind the fast path (F2): while the channel has a queue,
|
|
@@ -1676,6 +1746,8 @@ export declare class BridgeAgent {
|
|
|
1676
1746
|
private stageRuntimeMessageInOrder;
|
|
1677
1747
|
/** Test seam: resolves once every queued fast reply has settled. */
|
|
1678
1748
|
settleFastReplies(): Promise<void>;
|
|
1749
|
+
/** Send a line the voice fast path speaks: its memory record is "voice" (H1). */
|
|
1750
|
+
private sendSpoken;
|
|
1679
1751
|
/** Speak a fixed acknowledgement line; failures are counted, never thrown. */
|
|
1680
1752
|
private sendFastLine;
|
|
1681
1753
|
/**
|
|
@@ -1705,6 +1777,21 @@ export declare class BridgeAgent {
|
|
|
1705
1777
|
private stageHeldRuntimeMessage;
|
|
1706
1778
|
/** Settle a held frame: the fast path answered alone (retire it) or the runtime answers next (release it, in channel order). */
|
|
1707
1779
|
private settleHeldRuntimeMessage;
|
|
1780
|
+
/**
|
|
1781
|
+
* Voice repeats (b): a held frame's memory was read when its turn ARRIVED,
|
|
1782
|
+
* before the replies queued ahead of it (an earlier turn's fast reply, this
|
|
1783
|
+
* turn's own ack) were spoken. The runtime replaces its history with the
|
|
1784
|
+
* frame's memory, so with the arrival snapshot it answered an
|
|
1785
|
+
* already-answered turn again. At release the context is re-read and the
|
|
1786
|
+
* AGENT turns it holds beyond the snapshot (afterPublish ->
|
|
1787
|
+
* recordOutboundMemory) are appended to the snapshot. Peer turns recorded
|
|
1788
|
+
* since (this turn itself, later turns) are not, so the frame stays
|
|
1789
|
+
* prior-turn-only for what the runtime is about to answer. New turns are
|
|
1790
|
+
* found by recorded time, never by `seq` (the OpenClaw provider renumbers
|
|
1791
|
+
* seq per read). A failed re-read keeps the arrival snapshot.
|
|
1792
|
+
*/
|
|
1793
|
+
private memoryAtRelease;
|
|
1794
|
+
private static readonly TURN_MEMORY_LIMIT;
|
|
1708
1795
|
/**
|
|
1709
1796
|
* List channels in one enclave (default: the daemon's own home enclave).
|
|
1710
1797
|
* Task 3: called per-*known* enclave by refreshChannelMeta so a
|
|
@@ -1867,8 +1954,37 @@ export declare class BridgeAgent {
|
|
|
1867
1954
|
* Missing or stale metadata is UNKNOWN with zero grants, never a permissive
|
|
1868
1955
|
* guess. The returned object and grant array are frozen before they cross the
|
|
1869
1956
|
* daemon boundary so one consumer cannot widen a later execution check.
|
|
1957
|
+
*
|
|
1958
|
+
* Grants (architect ruling 2026-10-02): OWNER_DM_DEFAULT_READ_GRANTS only when
|
|
1959
|
+
* the kill switch is on, the channel is a DM, and EITHER
|
|
1960
|
+
* (a) audience is owner_dm AND the sender is the pinned structural owner
|
|
1961
|
+
* (`channelOwner`, never "anomalous") — a lying `is_agent_owner` flag
|
|
1962
|
+
* alone gets nothing; OR
|
|
1963
|
+
* (b) the sender is this channel's server-marked relay (`is_relay`) and the
|
|
1964
|
+
* relayed `speaker_identity_id` (passed by the caller only for relay
|
|
1965
|
+
* turns) is that same pinned owner, whose roster row is the agent's
|
|
1966
|
+
* owner. A speaker claim from any non-relay sender gets nothing.
|
|
1967
|
+
* Everything else gets the frozen empty list.
|
|
1870
1968
|
*/
|
|
1871
1969
|
private authorizationFor;
|
|
1970
|
+
/**
|
|
1971
|
+
* One content-free INFO line per authorization decision (every call site of
|
|
1972
|
+
* authorizationFor stages exactly one runtime turn). It says WHY a turn got
|
|
1973
|
+
* the grants it got, without any identifier, name or text: every field is a
|
|
1974
|
+
* boolean, a count, or a closed-set label.
|
|
1975
|
+
*
|
|
1976
|
+
* owner_pin pinned | anomalous | missing (channelOwner cache)
|
|
1977
|
+
* owner_row_is_agent_owner the pinned owner's roster row; `absent` when
|
|
1978
|
+
* there is no pin or the row is not in the roster
|
|
1979
|
+
* sender_is_owner sender identity == pinned owner identity
|
|
1980
|
+
* relay sender's roster row has is_relay
|
|
1981
|
+
* relay_speaker_is_owner `na` unless a relay speaker was passed
|
|
1982
|
+
* kill_switch SECURECOMS_OWNER_DM_DEFAULT_GRANTS as read at
|
|
1983
|
+
* startup: `on` = default grants enabled
|
|
1984
|
+
* meta_fresh / roster_fresh whether the channel meta / roster caches were
|
|
1985
|
+
* fresh (either stale forces audience=unknown)
|
|
1986
|
+
*/
|
|
1987
|
+
private logAuthzDecision;
|
|
1872
1988
|
/**
|
|
1873
1989
|
* Task 8 (owner-attested Reset Secure Session): STRUCTURAL owner selection —
|
|
1874
1990
|
* NEVER read the server's `role` field to pick the owner. In a DM the owner
|
|
@@ -2015,6 +2131,15 @@ export declare class BridgeAgent {
|
|
|
2015
2131
|
*/
|
|
2016
2132
|
private ingestOnce;
|
|
2017
2133
|
private startIngestCycle;
|
|
2134
|
+
/**
|
|
2135
|
+
* (4) recv.lag = server message created_at -> this pull's completion, per
|
|
2136
|
+
* application row, counted once per queue id. It makes the daemon-side
|
|
2137
|
+
* receive delay measurable (recv.pull alone times only the HTTP call).
|
|
2138
|
+
* Cross-clock: daemon-vs-server skew lands in the sample; a negative lag
|
|
2139
|
+
* is clamped to 0. The server serialises a naive UTC datetime (no offset),
|
|
2140
|
+
* which Date.parse would read as LOCAL time, so a bare timestamp is UTC.
|
|
2141
|
+
*/
|
|
2142
|
+
private recordRecvLag;
|
|
2018
2143
|
private ingestCycle;
|
|
2019
2144
|
drain(channelId: string | undefined, waitSeconds: number): Promise<BridgeMessage[]>;
|
|
2020
2145
|
/** Notify-only SSE signal: fired after a buffer push. Listener pulls via takeBuffered. */
|