@agentvault/claude-bridge 0.5.7 → 0.5.10
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/arming.d.ts +44 -14
- package/dist/bridge.d.ts +39 -3
- package/dist/config.d.ts +3 -2
- package/dist/index.js +422 -131
- package/dist/router.d.ts +40 -0
- package/dist/service/spec.d.ts.map +1 -1
- package/dist/session.d.ts +28 -2
- package/dist/worker-queue.d.ts +46 -0
- package/package.json +1 -1
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ReplySink } from "./session.js";
|
|
2
|
+
import type { WorkerTask } from "./worker-queue.js";
|
|
3
|
+
/** The push interface wireBridge drives (matches PersistentClaudeSession.push). */
|
|
4
|
+
export interface PushLike {
|
|
5
|
+
push(text: string, reply?: ReplySink, opts?: {
|
|
6
|
+
autoReplyOnText?: boolean;
|
|
7
|
+
replyExpected?: boolean;
|
|
8
|
+
armed?: () => boolean;
|
|
9
|
+
}): void;
|
|
10
|
+
}
|
|
11
|
+
export interface RouterDeps {
|
|
12
|
+
/**
|
|
13
|
+
* Task 4 (C-1/H-3): live, fail-closed device-level tool capability ("Work").
|
|
14
|
+
* MUST be read live at each push (never snapshotted) so a mid-connection flip
|
|
15
|
+
* and the fail-closed reconnect window both take effect immediately. Gates
|
|
16
|
+
* BOTH tool-eligible lanes — owner-DM and armed-room: Work off → the turn
|
|
17
|
+
* falls through to the tools-absent listener (structural fail-safe), making
|
|
18
|
+
* `work_allowed` the single on/off control for tool capability. The
|
|
19
|
+
* session-level isToolTurn AND remains the hard backstop for both lanes.
|
|
20
|
+
*/
|
|
21
|
+
workAllowed: () => boolean;
|
|
22
|
+
/** Always-on locked listener (tools:[]) for untrusted/non-tool turns. */
|
|
23
|
+
listener: PushLike;
|
|
24
|
+
/** Serial queue of tool-eligible turns. */
|
|
25
|
+
queue: {
|
|
26
|
+
enqueue(task: WorkerTask): void;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Route each inbound turn to EXACTLY ONE lane (facet B). Tool-eligible turns —
|
|
31
|
+
* owner DMs (autoReplyOnText) and armed room turns — go to an isolated ephemeral
|
|
32
|
+
* worker via the queue; everything else stays on the locked listener. A room
|
|
33
|
+
* turn's `armed` getter is read once here at routing time to pick the lane; the
|
|
34
|
+
* same live getter is threaded to the worker task so a mid-turn disarm still
|
|
35
|
+
* denies the next tool call (Facet A D5). `work_allowed` is the single control
|
|
36
|
+
* gating BOTH lanes: an armed room turn is only routed to the worker queue when
|
|
37
|
+
* workAllowed() is also true, so Work off always falls through to the listener.
|
|
38
|
+
*/
|
|
39
|
+
export declare function makeRouter(deps: RouterDeps): PushLike;
|
|
40
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/service/spec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB,EAAE,OAAO,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../src/service/spec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB,EAAE,OAAO,CAAC;IAClC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,WAAW,CA0BvF"}
|
package/dist/session.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export type QueryFn = (args: {
|
|
|
45
45
|
type: string;
|
|
46
46
|
[k: string]: unknown;
|
|
47
47
|
}>;
|
|
48
|
-
type ReplySink = (text: string) => void | Promise<void>;
|
|
48
|
+
export type ReplySink = (text: string) => void | Promise<void>;
|
|
49
49
|
export interface SessionOpts {
|
|
50
50
|
/** Fallback reply sink when a message carries no per-message reply (e.g. a
|
|
51
51
|
* proactive say). Per-message replies passed to push() take precedence. */
|
|
@@ -73,6 +73,19 @@ export interface SessionOpts {
|
|
|
73
73
|
apiUrl?: string;
|
|
74
74
|
/** Lazy getter so a JWT refreshed after construction is still picked up. */
|
|
75
75
|
deviceJwt?: () => string | null;
|
|
76
|
+
/** Single-shot ephemeral worker: process exactly the pushed message(s) then
|
|
77
|
+
* terminate the input stream so start() resolves. Used by the worker queue so
|
|
78
|
+
* a per-task query() completes and its state is discarded. */
|
|
79
|
+
ephemeral?: boolean;
|
|
80
|
+
/** Turn cap for a worker query() (bounds a runaway tool loop). */
|
|
81
|
+
maxTurns?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Task 4 (C-1/H-3): live, fail-closed device-level tool capability ("Work").
|
|
84
|
+
* Folded into `isToolTurn` as a top-level AND — the HARD backstop that makes
|
|
85
|
+
* the fail-safe real: NO tool runs (owner DM OR armed room) when this returns
|
|
86
|
+
* false, even if the router mis-routed. Read LIVE at each tool decision (never
|
|
87
|
+
* snapshotted). Absent getter ⇒ `?? false` ⇒ fail-closed (no tools). */
|
|
88
|
+
workAllowed?: () => boolean;
|
|
76
89
|
}
|
|
77
90
|
export declare class PersistentClaudeSession {
|
|
78
91
|
private opts;
|
|
@@ -101,10 +114,21 @@ export declare class PersistentClaudeSession {
|
|
|
101
114
|
* disarm denies the very next tool call. wireBridge binds it to the ArmingState
|
|
102
115
|
* for this turn's room; an unarmed room turn's getter returns false. */
|
|
103
116
|
private currentArmedGetter;
|
|
117
|
+
/** Per-turn: does this turn's sender expect a reply? DECOUPLED from
|
|
118
|
+
* `currentAutoReply` on purpose — it drives ONLY the #416 plain-text fallback
|
|
119
|
+
* (deliver assistant text when the model never calls say), NOT the tool gate.
|
|
120
|
+
* True for owner 1:1 DMs (defaults from autoReplyOnText) AND for a HUMAN/owner
|
|
121
|
+
* speaking in a room (wireBridge sets replyExpected from !senderIsAgent). An
|
|
122
|
+
* agent-authored room turn keeps this false → the agent may still stay silent,
|
|
123
|
+
* which also prevents agent↔agent reply loops. Tool access is unaffected: it
|
|
124
|
+
* stays gated on currentAutoReply (owner DM) OR currentArmedGetter (armed room). */
|
|
125
|
+
private currentReplyExpected;
|
|
104
126
|
private saidThisTurn;
|
|
105
127
|
private turnText;
|
|
106
128
|
/** In-process room MCP server — hoisted so buildSdkOptions can reference it. */
|
|
107
129
|
private roomServer;
|
|
130
|
+
/** AbortController for the in-flight query(); abort() triggers it (queue timeout). */
|
|
131
|
+
private _abort?;
|
|
108
132
|
constructor(opts: SessionOpts);
|
|
109
133
|
/**
|
|
110
134
|
* Queue an inbound message for the model.
|
|
@@ -120,12 +144,15 @@ export declare class PersistentClaudeSession {
|
|
|
120
144
|
*/
|
|
121
145
|
push(text: string, reply?: ReplySink, opts?: {
|
|
122
146
|
autoReplyOnText?: boolean;
|
|
147
|
+
replyExpected?: boolean;
|
|
123
148
|
armed?: () => boolean;
|
|
124
149
|
}): void;
|
|
125
150
|
/** Route a say-tool message to the reply bound to the in-flight message, falling
|
|
126
151
|
* back to opts.onSay. This is what closes the DM→room leak: the destination is
|
|
127
152
|
* the one captured for the message being answered, not a live global target. */
|
|
128
153
|
deliver(text: string): Promise<void>;
|
|
154
|
+
/** Abort the in-flight query() — used by the worker queue on a per-task timeout. */
|
|
155
|
+
abort(): void;
|
|
129
156
|
private input;
|
|
130
157
|
/**
|
|
131
158
|
* Build the SDK options for this session. Locked mode (default) is safe for
|
|
@@ -136,5 +163,4 @@ export declare class PersistentClaudeSession {
|
|
|
136
163
|
private buildSdkOptions;
|
|
137
164
|
start(): Promise<void>;
|
|
138
165
|
}
|
|
139
|
-
export {};
|
|
140
166
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { PersistentClaudeSession, ReplySink } from "./session.js";
|
|
2
|
+
/** One tool-eligible turn to run in an isolated ephemeral worker. */
|
|
3
|
+
export type WorkerTask = {
|
|
4
|
+
/** The single instruction that seeds the worker's conversation. */
|
|
5
|
+
instruction: string;
|
|
6
|
+
/** Where this task's reply (room_say / #416 fallback) is sent. Per-task. */
|
|
7
|
+
reply: ReplySink;
|
|
8
|
+
/** Owner-DM turns want a plain-text reply fallback (#416). */
|
|
9
|
+
autoReplyOnText: boolean;
|
|
10
|
+
/** A human/owner sender expects a reply: if the worker answers in plain text
|
|
11
|
+
* without calling say, deliver that text instead of dropping it. Decoupled from
|
|
12
|
+
* autoReplyOnText so it delivers on a tool-capable room turn WITHOUT enabling
|
|
13
|
+
* tools (arming governs tools). Absent/false for agent-authored turns. */
|
|
14
|
+
replyExpected?: boolean;
|
|
15
|
+
/** Armed-room turns: live getter the gate reads per tool decision (mid-turn
|
|
16
|
+
* disarm denies the next call). Absent for owner DMs. */
|
|
17
|
+
armed?: () => boolean;
|
|
18
|
+
};
|
|
19
|
+
export interface WorkerQueueDeps {
|
|
20
|
+
/** Build a fresh ephemeral worker session for this task (not yet started). */
|
|
21
|
+
makeSession: (task: WorkerTask) => PersistentClaudeSession;
|
|
22
|
+
/** Per-task wall-clock cap; on breach the worker is aborted and the queue advances. */
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
log: (m: string) => void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Serial FIFO of tool-eligible turns. One worker runs at a time (concurrent tool
|
|
28
|
+
* calls against one device/workspace can interleave destructively; the owner is a
|
|
29
|
+
* single actor). Each task runs in a fresh ephemeral worker seeded with only its
|
|
30
|
+
* instruction; on error or timeout the worker is torn down, a generic message is
|
|
31
|
+
* sent to the task's reply, and the queue advances — a task can never wedge the
|
|
32
|
+
* queue (which would starve the owner's DM lane).
|
|
33
|
+
*/
|
|
34
|
+
export declare class WorkerQueue {
|
|
35
|
+
private deps;
|
|
36
|
+
private q;
|
|
37
|
+
private loop;
|
|
38
|
+
private running;
|
|
39
|
+
constructor(deps: WorkerQueueDeps);
|
|
40
|
+
enqueue(task: WorkerTask): void;
|
|
41
|
+
/** Resolves when the queue has processed everything enqueued so far. */
|
|
42
|
+
whenDrained(): Promise<void>;
|
|
43
|
+
private drain;
|
|
44
|
+
private runOne;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=worker-queue.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
|
|
6
6
|
"main": "dist/index.js",
|