@securecoms/agent-bridge 0.20.0 → 0.23.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 +48 -2
- package/connectors/hermes/adapter.py +38 -8
- package/connectors/hermes/tests/test_adapter_wiring.py +6 -1
- package/connectors/hermes/tests/test_logging_privacy.py +61 -0
- package/connectors/openclaw/openclaw-entry.inbound.test.ts +560 -0
- package/connectors/openclaw/openclaw-entry.js +248 -51
- package/connectors/openclaw/openclaw-entry.smoke.test.ts +23 -1
- package/dist/bin.d.ts +3 -7
- package/dist/bin.js +3300 -476
- package/dist/bridge-agent.d.ts +280 -8
- package/dist/coordination.d.ts +90 -5
- package/dist/decrypt-error-log-limiter.d.ts +6 -30
- package/dist/errors.d.ts +1 -1
- package/dist/http-server.d.ts +20 -1
- package/dist/index.js +3289 -456
- package/dist/reply-receipts.d.ts +30 -0
- package/dist/service/runtime-config-dir.d.ts +34 -0
- package/dist/service-log.d.ts +7 -0
- package/dist/summarize.d.ts +3 -2
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +30 -3
- package/package.json +2 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-free proof that an attached runtime published a reply for a specific
|
|
3
|
+
* inbound message.
|
|
4
|
+
*
|
|
5
|
+
* The ledger is process-local, bearer-gated by the HTTP surface, short-lived,
|
|
6
|
+
* and bounded. It stores identifiers and timestamps only: never prompt or
|
|
7
|
+
* reply plaintext. A receipt is recorded only after BridgeAgent.send()
|
|
8
|
+
* succeeds, so a caller cannot turn a model result that failed to publish into
|
|
9
|
+
* a healthy synthetic verdict.
|
|
10
|
+
*/
|
|
11
|
+
export interface RuntimeReplyReceipt {
|
|
12
|
+
in_reply_to: string;
|
|
13
|
+
reply_message_id: string;
|
|
14
|
+
channel_id: string;
|
|
15
|
+
replied_at: number;
|
|
16
|
+
}
|
|
17
|
+
export declare class RuntimeReplyReceiptLedger {
|
|
18
|
+
private readonly receipts;
|
|
19
|
+
private readonly ttlMs;
|
|
20
|
+
private readonly capacity;
|
|
21
|
+
private readonly now;
|
|
22
|
+
constructor(opts?: {
|
|
23
|
+
ttlMs?: number;
|
|
24
|
+
capacity?: number;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
});
|
|
27
|
+
record(inReplyTo: string, channelId: string, replyMessageId: string): RuntimeReplyReceipt;
|
|
28
|
+
get(inReplyTo: string): RuntimeReplyReceipt | null;
|
|
29
|
+
private prune;
|
|
30
|
+
}
|
|
@@ -34,8 +34,42 @@ export interface LocalReport {
|
|
|
34
34
|
persona: Record<string, string>;
|
|
35
35
|
has_search_key: boolean;
|
|
36
36
|
has_llm_key: boolean;
|
|
37
|
+
legacy_memory: LegacyMemoryReport[];
|
|
38
|
+
}
|
|
39
|
+
export type LegacyMemoryKind = "global-memory" | "conversation-history" | "captured-memory";
|
|
40
|
+
export declare const LEGACY_MEMORY_FACT_MAX_BYTES = 1024;
|
|
41
|
+
export declare const LEGACY_MEMORY_FACT_DECISION_MAX = 500;
|
|
42
|
+
export interface LegacyMemoryReport {
|
|
43
|
+
id: string;
|
|
44
|
+
kind: LegacyMemoryKind;
|
|
45
|
+
channel_id?: string;
|
|
46
|
+
content: string | null;
|
|
47
|
+
bytes: number;
|
|
48
|
+
exportable: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface LegacyMemoryFactDecision {
|
|
51
|
+
source_id: string;
|
|
52
|
+
fact_hash: string;
|
|
53
|
+
action: "assign" | "delete";
|
|
54
|
+
channel_id?: string;
|
|
37
55
|
}
|
|
38
56
|
export declare function readLocalReport(home: string, identityId: string): LocalReport | null;
|
|
57
|
+
/** Delete only sources whose opaque id still matches the current regular-file
|
|
58
|
+
* bytes in the runtime-owned local report. A stale or tampered action deletes
|
|
59
|
+
* nothing and will be retried against the next report. */
|
|
60
|
+
export declare function deleteLegacyMemorySources(home: string, identityId: string, ids: string[]): number;
|
|
61
|
+
/**
|
|
62
|
+
* Apply a COMPLETE owner disposition for a quarantined global-memory source.
|
|
63
|
+
* Every nonblank line in the exact current source must have exactly one
|
|
64
|
+
* decision. Assigned facts are recorded first through the caller's protected,
|
|
65
|
+
* idempotent store; only after every write succeeds is the exact source
|
|
66
|
+
* revalidated and unlinked. Partial, stale, conflicting, oversized, or
|
|
67
|
+
* malformed batches are no-ops, so they cannot discard unreviewed data.
|
|
68
|
+
*/
|
|
69
|
+
export declare function applyLegacyMemoryFactDecisions(home: string, identityId: string, rawDecisions: unknown, recordFact: (channelId: string, fact: string, assignmentKey: string) => Promise<void>): Promise<{
|
|
70
|
+
sourcesDeleted: number;
|
|
71
|
+
factsAssigned: number;
|
|
72
|
+
}>;
|
|
39
73
|
export declare function readManagedMemory(home: string, identityId: string): string | null;
|
|
40
74
|
/**
|
|
41
75
|
* Remove exactly the named lines. Line-level, never a whole-file replace: the
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type ServiceLogLevel = "debug" | "info" | "warn" | "error";
|
|
2
|
+
export type ServiceLogFields = Readonly<Record<string, number | boolean>>;
|
|
3
|
+
export type ServiceLog = (level: ServiceLogLevel, event: string, fields?: ServiceLogFields, error?: unknown) => void;
|
|
4
|
+
/** Reduce a thrown value without reading its message, stack, or string form. */
|
|
5
|
+
export declare function errorClass(error: unknown): string;
|
|
6
|
+
export declare function formatServiceLog(level: ServiceLogLevel, event: string, fields?: ServiceLogFields, error?: unknown): string;
|
|
7
|
+
export declare function writeServiceLog(level: ServiceLogLevel, event: string, fields?: ServiceLogFields, error?: unknown): void;
|
package/dist/summarize.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ServiceLog } from "./service-log.js";
|
|
1
2
|
export declare const SUMMARY_PROMPT: string;
|
|
2
3
|
export interface WireTranscriptTurn {
|
|
3
4
|
t: number;
|
|
@@ -29,7 +30,7 @@ export interface SummarizeRequest {
|
|
|
29
30
|
requestedBy: string;
|
|
30
31
|
transcript: WireTranscript;
|
|
31
32
|
}
|
|
32
|
-
export declare function parseSummarizeBody(parsed: unknown, selfIdentityId: string, log?:
|
|
33
|
+
export declare function parseSummarizeBody(parsed: unknown, selfIdentityId: string, log?: ServiceLog): SummarizeRequest | null;
|
|
33
34
|
export declare function transcriptToPrompt(transcript: WireTranscript): string;
|
|
34
35
|
export declare function parseSummaryOutput(text: string): {
|
|
35
36
|
paragraph: string;
|
|
@@ -50,5 +51,5 @@ export declare function runSummarize(opts: {
|
|
|
50
51
|
}) => Promise<string>;
|
|
51
52
|
timeoutMs?: number;
|
|
52
53
|
now?: () => Date;
|
|
53
|
-
log:
|
|
54
|
+
log: ServiceLog;
|
|
54
55
|
}): Promise<Record<string, unknown> | null>;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const BRIDGE_VERSION = "0.
|
|
1
|
+
export declare const BRIDGE_VERSION = "0.23.0";
|
package/dist/ws-server.d.ts
CHANGED
|
@@ -9,15 +9,26 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Protocol (all frames are JSON text):
|
|
11
11
|
* Daemon → agent:
|
|
12
|
-
* { "type":"message", channel_id, message_id, sender_id, text, ts
|
|
12
|
+
* { "type":"message", channel_id, message_id, sender_id, text, ts,
|
|
13
|
+
* authorization:{version,audience,sender_membership,tool_grants} } // inbound, decrypted
|
|
13
14
|
* { "type":"sent", message_id } // ack of a send
|
|
14
15
|
* { "type":"ok" } // ack of a status report
|
|
15
16
|
* { "type":"error", error, message, detail? } // canonical codes (see errors.ts):
|
|
16
|
-
* // channel_pending |
|
|
17
|
+
* // channel_pending | rate_limited |
|
|
18
|
+
* // invalid_request | agent_paused |
|
|
19
|
+
* // send_forbidden (the last two are
|
|
20
|
+
* // TERMINAL: the in_reply_to message
|
|
21
|
+
* // is acked by the daemon, no retry)
|
|
17
22
|
* Agent → daemon:
|
|
18
|
-
* { "type":"send", channel_id, text?, a2a?, idempotency_key
|
|
23
|
+
* { "type":"send", channel_id, text?, a2a?, idempotency_key?,
|
|
24
|
+
* in_reply_to? } // exactly one of text | a2a; correlation is metadata-only
|
|
19
25
|
* { "type":"status", channel_id, state, detail? }
|
|
20
26
|
* { "type":"ack", message_id } // confirms a delivered `message` was consumed
|
|
27
|
+
* { "type":"nack", message_id, reason? } // final-wave I1(b): THIS message's turn
|
|
28
|
+
* // failed — requeued behind a backoff; the
|
|
29
|
+
* // socket and other channels' turns stay up
|
|
30
|
+
* { "type":"hello", features? } // → { "type":"hello", features:["nack"] }; a
|
|
31
|
+
* // runtime nacks only after seeing "nack" here
|
|
21
32
|
* { "type":"action_begin", channel_id } // → { "type":"action_begun", run_id }
|
|
22
33
|
* { "type":"action_start", run_id, node_type, title, parent_id? } // → { "type":"action_started", node_id }
|
|
23
34
|
* { "type":"action_update", run_id, node_id, status?, detail? } // → { "type":"ok" }
|
|
@@ -29,6 +40,22 @@
|
|
|
29
40
|
* is re-buffered and redelivered to the next consumer. The agent dedups by
|
|
30
41
|
* message_id (a fresh UUID minted per send; server-side dedup is on replay_nonce).
|
|
31
42
|
*
|
|
43
|
+
* Bounded work (Phase 3):
|
|
44
|
+
* - F2 ordering: at most ONE message per channel is in flight on a socket;
|
|
45
|
+
* the next message for that channel is sent only after the previous one is
|
|
46
|
+
* acked (or retired). A backlog replays in order instead of flooding the
|
|
47
|
+
* runtime, which used to answer the overflow with a "busy" reply and ack it.
|
|
48
|
+
* - F1 attempt cap: every delivery is counted durably; a message that
|
|
49
|
+
* reaches the cap is dead-lettered (content-free log + ack + one
|
|
50
|
+
* content-free notice to the channel per hour) rather than redelivered
|
|
51
|
+
* forever, and a channel where the agent is paused is not delivered at
|
|
52
|
+
* all (no model call).
|
|
53
|
+
* - Final-wave I1: a FAILED delivery (a `nack`, or the socket closing with
|
|
54
|
+
* it in flight — older runtimes close on any failed turn) is requeued
|
|
55
|
+
* behind a 2^attempts × 5 s backoff. A nack fails only its own message.
|
|
56
|
+
* Messages never handed over (mid-flush close, a failed frame write) go
|
|
57
|
+
* back without that penalty.
|
|
58
|
+
*
|
|
32
59
|
* Single consumer: the daemon serves one agent per process. A new `/v1/ws`
|
|
33
60
|
* connection replaces the prior one (newest wins, mirroring the SSE path); the
|
|
34
61
|
* replaced socket's un-acked in-flight messages are re-buffered.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@securecoms/agent-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"@types/ws": "^8.18.1",
|
|
50
50
|
"esbuild": "0.24.0",
|
|
51
51
|
"typescript": "^5.4.0",
|
|
52
|
-
"vitest": "^2.
|
|
52
|
+
"vitest": "^3.2.6"
|
|
53
53
|
}
|
|
54
54
|
}
|