@securecoms/agent-bridge 0.26.4 → 0.26.6
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 +24 -0
- package/connectors/hermes/adapter.py +202 -3
- package/connectors/hermes/securecoms_authz.py +202 -0
- package/connectors/hermes/tests/test_adapter_wiring.py +481 -3
- package/connectors/hermes/tests/test_securecoms_authz.py +260 -0
- package/connectors/openclaw/openclaw-entry.inbound.test.ts +19 -4
- package/connectors/openclaw/openclaw-entry.js +192 -3
- package/connectors/openclaw/openclaw-entry.status.test.ts +11 -1
- package/connectors/openclaw/openclaw-entry.toolgate.test.ts +477 -0
- package/dist/bin.js +401 -75
- package/dist/bridge-agent.d.ts +157 -4
- package/dist/consumer-grants.d.ts +22 -0
- package/dist/harness-report.d.ts +14 -0
- package/dist/http-server.d.ts +10 -0
- package/dist/index.js +401 -75
- package/dist/room-tools.d.ts +23 -0
- package/dist/service/runtime-config-dir.d.ts +22 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +0 -77
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type RoomToolClass = "room_safe_read" | "owner_credential" | "egress" | "side_effect";
|
|
2
|
+
/** §7A.1a, verbatim. A tool carries every class that applies; the most
|
|
3
|
+
* restrictive governs. `READ_ONLY_TOOLS` in the runtime is NOT this table:
|
|
4
|
+
* it says whether an implementation writes, not whose credential it spends
|
|
5
|
+
* or where it sends data (audit H-4). */
|
|
6
|
+
export declare const TOOL_ROOM_CLASSES: Readonly<Record<string, readonly RoomToolClass[]>>;
|
|
7
|
+
/** Edge statuses whose rows are channel MEMBERS for attribution and the
|
|
8
|
+
* existing roster consumers (today's set). The wider rows feed
|
|
9
|
+
* classification only. */
|
|
10
|
+
export declare const LIVE_MEMBER_STATUSES: ReadonlySet<string>;
|
|
11
|
+
/** A row that is a channel member: a live status (or an older backend with no
|
|
12
|
+
* status) and not a leaf row (audit M-B2: a departed member still holding a
|
|
13
|
+
* leaf counts for the class, never as a member). */
|
|
14
|
+
export declare function isLiveRow(r: {
|
|
15
|
+
status?: string;
|
|
16
|
+
left_but_in_group?: boolean | null;
|
|
17
|
+
}): boolean;
|
|
18
|
+
/** §7A.4 (1): a room is cross-org if ANY row's org differs from the agent
|
|
19
|
+
* owner's, leaf rows included. A row with no claim (an older backend, a
|
|
20
|
+
* member with no edge) makes the class unknown, which grants nothing (§7A.5). */
|
|
21
|
+
export declare function roomIsCrossOrg(rows: ReadonlyArray<{
|
|
22
|
+
is_same_org_as_agent_owner?: boolean | null;
|
|
23
|
+
}>): boolean | null;
|
|
@@ -35,6 +35,28 @@ export interface LocalReport {
|
|
|
35
35
|
has_search_key: boolean;
|
|
36
36
|
has_llm_key: boolean;
|
|
37
37
|
legacy_memory: LegacyMemoryReport[];
|
|
38
|
+
/** §B.4 counts; undefined when the runtime reported none or the block was malformed. */
|
|
39
|
+
secret_paths?: SecretPathsReport;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* §B.4: how many server-named secret paths in the managed config are inside
|
|
43
|
+
* the secrets directory, owner-allowlisted, or outside it, and the runtime's
|
|
44
|
+
* mode. Rebuilt field by field from the runtime's file: numbers and one enum,
|
|
45
|
+
* so no path (the owner's `allowed_secret_paths` sits in the same file) can
|
|
46
|
+
* ride along to the backend.
|
|
47
|
+
*/
|
|
48
|
+
export interface SecretPathsReport {
|
|
49
|
+
mode: "report" | "enforce";
|
|
50
|
+
inside: number;
|
|
51
|
+
allowlisted: number;
|
|
52
|
+
outside: number;
|
|
53
|
+
/** `sqlite` (H1) is absent from a runtime that predates it; absent means unknown, not zero. */
|
|
54
|
+
outside_by_field: {
|
|
55
|
+
headers_file: number;
|
|
56
|
+
postgres_url_file: number;
|
|
57
|
+
search_key_file: number;
|
|
58
|
+
sqlite?: number;
|
|
59
|
+
};
|
|
38
60
|
}
|
|
39
61
|
export type LegacyMemoryKind = "global-memory" | "conversation-history" | "captured-memory";
|
|
40
62
|
export declare const LEGACY_MEMORY_FACT_MAX_BYTES = 1024;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const BRIDGE_VERSION = "0.26.
|
|
1
|
+
export declare const BRIDGE_VERSION = "0.26.6";
|
package/dist/ws-server.d.ts
CHANGED
|
@@ -1,80 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent-facing keepalive WebSocket (/v1/ws).
|
|
3
|
-
*
|
|
4
|
-
* Adds a single persistent bidirectional socket for the AGENT⇄DAEMON hop,
|
|
5
|
-
* modeled on AgentVault's keepalive WS (token in `?token=` query OR
|
|
6
|
-
* `Authorization: Bearer`). This replaces the curl/long-poll loop: the agent
|
|
7
|
-
* receives live inbound messages as JSON frames and sends/reports status over
|
|
8
|
-
* the same socket.
|
|
9
|
-
*
|
|
10
|
-
* Protocol (all frames are JSON text):
|
|
11
|
-
* Daemon → agent:
|
|
12
|
-
* { "type":"message", channel_id, message_id, sender_id, text, ts,
|
|
13
|
-
* authorization:{version,audience,sender_membership,tool_grants} } // inbound, decrypted
|
|
14
|
-
* { "type":"sent", message_id } // ack of a send
|
|
15
|
-
* { "type":"ok" } // ack of a status report
|
|
16
|
-
* { "type":"error", error, message, detail? } // canonical codes (see errors.ts):
|
|
17
|
-
* // channel_pending | rate_limited |
|
|
18
|
-
* // invalid_request | agent_paused |
|
|
19
|
-
* // send_forbidden (those two are
|
|
20
|
-
* // TERMINAL: the in_reply_to message
|
|
21
|
-
* // is acked by the daemon, no retry) |
|
|
22
|
-
* // group_superseded (NOT terminal:
|
|
23
|
-
* // the reply is republished after
|
|
24
|
-
* // the agent rejoins)
|
|
25
|
-
* Agent → daemon:
|
|
26
|
-
* { "type":"send", channel_id, text?, a2a?, idempotency_key?,
|
|
27
|
-
* in_reply_to? } // exactly one of text | a2a. SEC-06 #18: in_reply_to
|
|
28
|
-
* // and/or idempotency_key make the send idempotent — a
|
|
29
|
-
* // deterministic keyed message_id and a durable reply
|
|
30
|
-
* // intent written before the publish, so a retry or a
|
|
31
|
-
* // redelivery republishes the SAME reply
|
|
32
|
-
* { "type":"status", channel_id, state, detail? }
|
|
33
|
-
* { "type":"ack", message_id } // confirms a delivered `message` was consumed
|
|
34
|
-
* { "type":"nack", message_id, reason? } // final-wave I1(b): THIS message's turn
|
|
35
|
-
* // failed — requeued behind a backoff; the
|
|
36
|
-
* // socket and other channels' turns stay up
|
|
37
|
-
* { "type":"hello", features? } // → { "type":"hello", features:["nack"] }; a
|
|
38
|
-
* // runtime nacks only after seeing "nack" here
|
|
39
|
-
* { "type":"action_begin", channel_id } // → { "type":"action_begun", run_id }
|
|
40
|
-
* { "type":"action_start", run_id, node_type, title, parent_id? } // → { "type":"action_started", node_id }
|
|
41
|
-
* { "type":"action_update", run_id, node_id, status?, detail? } // → { "type":"ok" }
|
|
42
|
-
* { "type":"action_finish", run_id, node_id, status, detail? } // → { "type":"ok" }
|
|
43
|
-
* { "type":"action_settle", run_id, result_message_id? } // → { "type":"action_settled", run_id }
|
|
44
|
-
*
|
|
45
|
-
* Inbound delivery is at-least-once. A delivered `message` is held in-flight
|
|
46
|
-
* until the agent sends a matching `ack`; if the socket drops first, the frame
|
|
47
|
-
* is re-buffered and redelivered to the next consumer. The agent dedups by
|
|
48
|
-
* message_id. SEC-06 #18: a redelivered message whose reply already has a
|
|
49
|
-
* durable intent is NOT handed to the agent again; the daemon republishes that
|
|
50
|
-
* reply itself (same message_id), so the model does not run twice.
|
|
51
|
-
*
|
|
52
|
-
* Bounded work (Phase 3):
|
|
53
|
-
* - F2 ordering: at most ONE message per channel is in flight on a socket;
|
|
54
|
-
* the next message for that channel is sent only after the previous one is
|
|
55
|
-
* acked (or retired). A backlog replays in order instead of flooding the
|
|
56
|
-
* runtime, which used to answer the overflow with a "busy" reply and ack it.
|
|
57
|
-
* - F1 attempt cap: every delivery is counted durably; a message that
|
|
58
|
-
* reaches the cap is dead-lettered (content-free log + ack + one
|
|
59
|
-
* content-free notice to the channel per hour) rather than redelivered
|
|
60
|
-
* forever, and a channel where the agent is paused is not delivered at
|
|
61
|
-
* all (no model call).
|
|
62
|
-
* - Final-wave I1: a FAILED delivery (a `nack`, or the socket closing with
|
|
63
|
-
* it in flight — older runtimes close on any failed turn) is requeued
|
|
64
|
-
* behind a 2^attempts × 5 s backoff. A nack fails only its own message.
|
|
65
|
-
* Messages never handed over (mid-flush close, a failed frame write) go
|
|
66
|
-
* back without that penalty.
|
|
67
|
-
* - #842: a delivery still un-acked after INFLIGHT_STALE_AFTER_MS is LOGGED
|
|
68
|
-
* once (`runtime_inbox.inflight_stale`) and otherwise left alone. There
|
|
69
|
-
* is deliberately no lease: see INFLIGHT_STALE_AFTER_MS.
|
|
70
|
-
*
|
|
71
|
-
* Single consumer: the daemon serves one agent per process. A new `/v1/ws`
|
|
72
|
-
* connection replaces the prior one (newest wins, mirroring the SSE path); the
|
|
73
|
-
* replaced socket's un-acked in-flight messages are re-buffered.
|
|
74
|
-
*
|
|
75
|
-
* Keepalive uses the ws library's server-side ping (the protocol-level ping
|
|
76
|
-
* frame, not an application message); dead sockets are reaped via pong tracking.
|
|
77
|
-
*/
|
|
78
1
|
import type { Server as HttpServer } from "node:http";
|
|
79
2
|
import type { BridgeAgent } from "./bridge-agent.js";
|
|
80
3
|
export interface AgentWebSocketOptions {
|