@securecoms/agent-bridge 0.29.0 → 0.30.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 +156 -9
- package/dist/bin.d.ts +83 -1
- package/dist/bin.js +3395 -586
- package/dist/bridge-agent.d.ts +269 -2
- package/dist/config.d.ts +7 -0
- package/dist/consumer-grants.d.ts +19 -0
- package/dist/errors.d.ts +1 -1
- package/dist/grants/binding.d.ts +58 -0
- package/dist/grants/effective.d.ts +47 -0
- package/dist/grants/manifest.d.ts +68 -0
- package/dist/grants/owner-turn.d.ts +52 -0
- package/dist/grants/persona-render.d.ts +30 -0
- package/dist/grants/pin-command.d.ts +31 -0
- package/dist/grants/pin.d.ts +35 -0
- package/dist/grants/room-search-cap.d.ts +15 -0
- package/dist/grants/voice-fence.d.ts +53 -0
- package/dist/http-server.d.ts +1 -0
- package/dist/index.js +2289 -192
- package/dist/memory/unified/item-book.d.ts +3 -0
- package/dist/memory/unified/migrate.d.ts +11 -0
- package/dist/memory/unified/read-book.d.ts +2 -1
- package/dist/memory/unified/worker-protocol.d.ts +4 -0
- package/dist/memory-taps.d.ts +1 -1
- package/dist/memory-worker.js +18 -3
- package/dist/service/install-prep.d.ts +21 -1
- package/dist/service/instance-lock.d.ts +88 -0
- package/dist/service/launchd.d.ts +1 -1
- package/dist/service/runtime-config-dir.d.ts +15 -0
- package/dist/service/status.d.ts +3 -5
- package/dist/service/systemd.d.ts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +5 -1
- package/package.json +1 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentVaultEnvelope } from "@agentvault/connect-core";
|
|
2
|
+
import { type OwnerGrantPin } from "./pin.js";
|
|
3
|
+
export declare const OWNER_VERIFY_STATES: readonly ["ok", "bad_signature", "malformed", "unknown_algo", "no_signature", "not_owner_dm_leaves", "wrong_dm_channel", "replay", "verify_error"];
|
|
4
|
+
export type OwnerVerifyFailure = Exclude<(typeof OWNER_VERIFY_STATES)[number], "ok">;
|
|
5
|
+
export type OwnerTurnVerdict = {
|
|
6
|
+
readonly kind: "unchecked";
|
|
7
|
+
} | {
|
|
8
|
+
readonly kind: "verified";
|
|
9
|
+
readonly replay: {
|
|
10
|
+
readonly channelId: string;
|
|
11
|
+
readonly groupId: string;
|
|
12
|
+
readonly epoch: number;
|
|
13
|
+
};
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: "constants_only";
|
|
16
|
+
readonly replay: {
|
|
17
|
+
readonly channelId: string;
|
|
18
|
+
readonly groupId: string;
|
|
19
|
+
readonly epoch: number;
|
|
20
|
+
};
|
|
21
|
+
} | {
|
|
22
|
+
readonly kind: "failed";
|
|
23
|
+
readonly reason: OwnerVerifyFailure;
|
|
24
|
+
};
|
|
25
|
+
export type OwnerSignatureResult = {
|
|
26
|
+
readonly state: "ok";
|
|
27
|
+
readonly envelope: AgentVaultEnvelope;
|
|
28
|
+
} | {
|
|
29
|
+
readonly state: "no_signature" | "bad_signature" | "malformed" | "unknown_algo";
|
|
30
|
+
};
|
|
31
|
+
export declare function verifyOwnerSignature(entry: {
|
|
32
|
+
readonly payloadHex?: string;
|
|
33
|
+
readonly identitySignature?: string;
|
|
34
|
+
}, pin: OwnerGrantPin): OwnerSignatureResult;
|
|
35
|
+
export type OwnerDmLeafClass = "ok" | "unsigned_relay" | "foreign";
|
|
36
|
+
/**
|
|
37
|
+
* §7.2 check 2, decision D-4. Any number of owner leaves passes (multi-device:
|
|
38
|
+
* leaf credentials encode identity, not device). Fails closed on an empty tree,
|
|
39
|
+
* a non-basic leaf, or no owner leaf. A signed relay is admitted by
|
|
40
|
+
* `relayIdentityId` alone; the roster flag only chooses between
|
|
41
|
+
* `unsigned_relay` (today's constants) and `foreign` (zero), so it can never
|
|
42
|
+
* widen past today's constants.
|
|
43
|
+
*/
|
|
44
|
+
export declare function classifyOwnerDmLeaves(leaves: readonly {
|
|
45
|
+
basic: boolean;
|
|
46
|
+
identity: string | null;
|
|
47
|
+
}[], allowed: {
|
|
48
|
+
ownerIdentityId: string;
|
|
49
|
+
agentIdentityId: string;
|
|
50
|
+
relayIdentityId?: string;
|
|
51
|
+
isServerRelay: (identityId: string) => boolean;
|
|
52
|
+
}): OwnerDmLeafClass;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bridge's persona renderer (owner-signed tool grants, spec §7.12; plan 4 Task 8).
|
|
3
|
+
*
|
|
4
|
+
* Under a manifest the bridge writes IDENTITY.md, SOUL.md and AGENTS.md from the
|
|
5
|
+
* raw persona whose hash the owner signed, never the server's text. It is a port
|
|
6
|
+
* of render_persona (backend/app/services/runtime_config_render.py) and is pinned
|
|
7
|
+
* to it byte for byte by persona-render.vectors.json, which the backend test
|
|
8
|
+
* test_persona_render_vectors.py re-renders with the Python function.
|
|
9
|
+
*
|
|
10
|
+
* Python str.strip() semantics, not JavaScript trim (trim also removes U+FEFF).
|
|
11
|
+
* `tone` is rendered in a fixed field order (TONE_FIELD_ORDER, the JSONB key
|
|
12
|
+
* order the backend reads), never the payload's key order (rulings item 8(a)).
|
|
13
|
+
*/
|
|
14
|
+
export declare const PERSONA_MAX_BYTES = 8192;
|
|
15
|
+
export type PersonaRender = {
|
|
16
|
+
readonly ok: true;
|
|
17
|
+
readonly files: {
|
|
18
|
+
"IDENTITY.md": string;
|
|
19
|
+
"SOUL.md": string;
|
|
20
|
+
"AGENTS.md": string;
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
readonly ok: false;
|
|
24
|
+
readonly reason: "shape" | "over_cap";
|
|
25
|
+
};
|
|
26
|
+
/** Postgres JSONB returns object keys shorter-first, then bytewise; render_persona iterates that order. */
|
|
27
|
+
export declare const TONE_FIELD_ORDER: readonly ["humor", "length", "warmth", "formality"];
|
|
28
|
+
export declare function pyStrip(s: string): string;
|
|
29
|
+
/** A port of render_persona(persona, tool_names). Never throws. */
|
|
30
|
+
export declare function renderPersonaFiles(persona: unknown): PersonaRender;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type PinCommandStep = "pin_tmp_written" | "cred_tmp_written" | "pin_renamed" | "cred_renamed";
|
|
2
|
+
export type PinCommandResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
changedKey: boolean;
|
|
5
|
+
} | {
|
|
6
|
+
ok: false;
|
|
7
|
+
reason: "pin_invalid" | "credential_invalid" | "symlink_refused" | "agent_mismatch" | "directory_mismatch" | "io_error";
|
|
8
|
+
};
|
|
9
|
+
export declare const PIN_COPY: {
|
|
10
|
+
readonly saved: string;
|
|
11
|
+
readonly pinInvalid: string;
|
|
12
|
+
readonly credentialInvalid: string;
|
|
13
|
+
readonly symlink: string;
|
|
14
|
+
readonly ioError: string;
|
|
15
|
+
readonly agentMismatch: string;
|
|
16
|
+
readonly directoryMismatch: string;
|
|
17
|
+
readonly announce: (agentIdentityId: string) => string;
|
|
18
|
+
readonly usage: string;
|
|
19
|
+
};
|
|
20
|
+
export declare function runPinCommand(opts: {
|
|
21
|
+
credentialPath: string;
|
|
22
|
+
pinSourcePath: string;
|
|
23
|
+
agentIdentityId: string;
|
|
24
|
+
/** Required. Called exactly once, after every check passes and before the first write, with the credential's identity_id. Never called on a refusal. */
|
|
25
|
+
announce: (agentIdentityId: string) => void;
|
|
26
|
+
afterStep?: (s: PinCommandStep) => void;
|
|
27
|
+
}): PinCommandResult;
|
|
28
|
+
export declare function runPinCli(argv: readonly string[], io: {
|
|
29
|
+
out: (line: string) => void;
|
|
30
|
+
err: (line: string) => void;
|
|
31
|
+
}): 0 | 1 | 2;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const PIN_FILE_NAME = "owner-signing-key.json";
|
|
2
|
+
export declare const PIN_STATES: readonly ["ok", "no_pin", "pin_conflict", "malformed", "unsupported_algo", "symlink_refused", "unreadable", "server_owner_mismatch"];
|
|
3
|
+
export type PinState = (typeof PIN_STATES)[number];
|
|
4
|
+
export interface OwnerGrantPin {
|
|
5
|
+
readonly ownerIdentityId: string;
|
|
6
|
+
readonly keyAlgo: "ed25519";
|
|
7
|
+
readonly publicKey: Uint8Array;
|
|
8
|
+
readonly publicKeyB64: string;
|
|
9
|
+
}
|
|
10
|
+
export interface OwnerGrantPinResult {
|
|
11
|
+
readonly state: Exclude<PinState, "server_owner_mismatch">;
|
|
12
|
+
readonly pin: OwnerGrantPin | null;
|
|
13
|
+
}
|
|
14
|
+
/** Canonical base64 of exactly `len` bytes: decode, re-encode, compare. */
|
|
15
|
+
export declare function canonicalBase64Bytes(v: unknown, len: number): Uint8Array | null;
|
|
16
|
+
export declare function parsePinObject(v: unknown): {
|
|
17
|
+
ok: true;
|
|
18
|
+
pin: OwnerGrantPin;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
state: "malformed" | "unsupported_algo";
|
|
22
|
+
};
|
|
23
|
+
export interface PinFs {
|
|
24
|
+
lstat(path: string): {
|
|
25
|
+
isSymbolicLink(): boolean;
|
|
26
|
+
mode: number;
|
|
27
|
+
};
|
|
28
|
+
readFile(path: string): string;
|
|
29
|
+
}
|
|
30
|
+
/** Spec §6.5. A symlinked pin file is `symlink_refused` whatever the credential holds. */
|
|
31
|
+
export declare function loadOwnerGrantPin(opts: {
|
|
32
|
+
credentialPath: string;
|
|
33
|
+
credentialJson: Record<string, unknown>;
|
|
34
|
+
fs?: PinFs;
|
|
35
|
+
}): OwnerGrantPinResult;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner grants plan 4, Task 12 (spec §7.5, OQ-2, audit r1 L-3): the bridge's copy of the runtime's
|
|
3
|
+
* operator-cap parser (connect-agent-runtime/src/room-search-cap.ts). The two packages share no
|
|
4
|
+
* dependency; bridge-agent.room-search-cap.test.ts pins them equal on every listed input.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ROOM_SEARCH_DAILY_CAP = 50;
|
|
7
|
+
export declare const ROOM_SEARCH_CAP_ENV = "SECURECOMS_ROOM_SEARCH_DAILY_CAP";
|
|
8
|
+
/** /^\d+$/ and <= 50 is used (0 = no room search); anything else is 50 with one warning. Never a widening. */
|
|
9
|
+
export declare function parseRoomSearchCap(v: string | undefined, warn?: () => void): number;
|
|
10
|
+
/** Review M-3 (ERIC APPROVALS 3): the per-agent total across rooms, the runtime's parser copied (parity-tested). */
|
|
11
|
+
export declare const AGENT_ROOM_SEARCH_DAILY_CAP = 200;
|
|
12
|
+
export declare const AGENT_ROOM_SEARCH_CAP_ENV = "SECURECOMS_AGENT_ROOM_SEARCH_DAILY_CAP";
|
|
13
|
+
export declare function parseAgentRoomSearchCap(v: string | undefined, warn?: () => void): number;
|
|
14
|
+
/** The store row that holds the per-agent total (never a channel id: channel ids are UUIDs). */
|
|
15
|
+
export declare const AGENT_TOTAL_KEY = "agent-total";
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** "untrusted" (review H-3): an agent reply whose turn saw a relay-class line, or that could not be attributed. */
|
|
2
|
+
export type LineOrigin = "fast_path" | "reply" | "untrusted";
|
|
3
|
+
/** 2026-09-07T00:00:00Z: before this the deterministic fast-reply id did not exist (`date -u -r 1788739200`). */
|
|
4
|
+
export declare const FAST_ID_EPOCH_MS = 1788739200000;
|
|
5
|
+
/**
|
|
6
|
+
* The unkeyed fastReplyId of e1369a7c (2026-09-06) to 60365953 (2026-09-25): SHA-256 of `fast:<messageId>`, first 16
|
|
7
|
+
* bytes, RFC 4122 version and variant nibbles, as a hyphenated UUID. Rows of that period carry this form.
|
|
8
|
+
*/
|
|
9
|
+
export declare function legacyFastReplyId(messageId: string): string;
|
|
10
|
+
export interface AgentLine {
|
|
11
|
+
readonly origin?: LineOrigin;
|
|
12
|
+
readonly modality?: string;
|
|
13
|
+
readonly messageId?: string;
|
|
14
|
+
readonly ts?: number;
|
|
15
|
+
}
|
|
16
|
+
/** `fastIds`: the keyed AND the legacy fast-reply id of every peer row of the conversation (not the 50-row window). */
|
|
17
|
+
export interface FenceContext {
|
|
18
|
+
readonly fastIds: ReadonlySet<string>;
|
|
19
|
+
}
|
|
20
|
+
/** Rule 4, history. */
|
|
21
|
+
export declare function classifyAgentLine(line: AgentLine, ctx: FenceContext): LineOrigin;
|
|
22
|
+
export interface UnifiedItemFacts {
|
|
23
|
+
readonly channelId: string;
|
|
24
|
+
readonly source: string;
|
|
25
|
+
readonly originId: string;
|
|
26
|
+
readonly role?: "peer" | "agent";
|
|
27
|
+
readonly origin?: LineOrigin;
|
|
28
|
+
readonly sourceRef?: string;
|
|
29
|
+
}
|
|
30
|
+
export type TraceRow = ({
|
|
31
|
+
readonly role: "peer" | "agent";
|
|
32
|
+
readonly peerId?: string;
|
|
33
|
+
} & AgentLine) | null;
|
|
34
|
+
/**
|
|
35
|
+
* Review H-3 (ruling): a peer line is relay-class when its authenticated sender is not the pinned owner identity
|
|
36
|
+
* (the relay, incl. owner-speaker voice transcripts) or its modality is voice.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isRelayClassPeer(line: {
|
|
39
|
+
readonly peerId?: string;
|
|
40
|
+
readonly modality?: string;
|
|
41
|
+
}, ownerId: string): boolean;
|
|
42
|
+
export type Trace = (conversationId: string, at: {
|
|
43
|
+
seq?: number;
|
|
44
|
+
messageId?: string;
|
|
45
|
+
}) => TraceRow;
|
|
46
|
+
/**
|
|
47
|
+
* Rule 4, unified. `strict` (review H-3): a turn that holds a manifest grant, which also excludes owner-voice items
|
|
48
|
+
* and imported peer lines that trace to a relay-class row (or cannot be traced).
|
|
49
|
+
*/
|
|
50
|
+
export declare function classifyUnifiedItem(item: UnifiedItemFacts, trace: Trace, ctx: FenceContext, strict?: {
|
|
51
|
+
readonly ownerId: string;
|
|
52
|
+
readonly turnChannelId?: string;
|
|
53
|
+
}, keepUntrusted?: boolean): "keep" | "exclude";
|
package/dist/http-server.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare function writeSse(res: {
|
|
|
25
25
|
export declare function deliverSseBatch(res: Parameters<typeof writeSse>[0], batch: BridgeMessage[], agent: {
|
|
26
26
|
ackRuntimeMessages(ids: string[]): void;
|
|
27
27
|
requeueFront(msgs: BridgeMessage[]): void;
|
|
28
|
+
harnessGrantsWithheld(): boolean;
|
|
28
29
|
}, onWriteError?: (error: unknown) => void): void;
|
|
29
30
|
/**
|
|
30
31
|
* Once-per-connection write-failure reporter (#335). A throwing `res.write`
|