@beeos-ai/bma-executor-sdk 0.0.0-stage → 0.4.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.
@@ -0,0 +1,261 @@
1
+ import { type IncomingMessage, type Server } from 'node:http';
2
+ import type { Duplex } from 'node:stream';
3
+ import { ChannelScope, type Ack, type AgentCapability, type Capabilities, type ExecutorDrainResult, type ExecutorInfo, type InteractionCancelParams, type InteractionRequestParams, type InteractionResponseParams, type InteractionResponseResult, type SessionAssignParams, type SessionCloseParams, type SessionCloseResult, type SessionEventDeltaParams, type SessionEventParams, type SessionEventResult, type SessionStatusParams, type ToolCallParams, type ToolProgressParams, type ToolResultParams, type TurnCancelParams, type TurnCancelResult, type TurnInputParams, type TurnStartParams, type TurnStartResult } from './generated/executor.js';
4
+ import { type Dialer } from './client.js';
5
+ import { RpcPeer, type LogLevel, type Logger } from './rpc.js';
6
+ import { FrameChannel, type Conn } from './transport.js';
7
+ export declare const DEFAULT_HARNESS_HEARTBEAT_INTERVAL_MS = 15000;
8
+ export declare const DEFAULT_HARNESS_LEASE_TTL_MS = 60000;
9
+ /** What authenticate learns from a connection. A non-empty executorId pins the executor identity. */
10
+ export interface Principal {
11
+ executorId?: string;
12
+ }
13
+ /** One durable agent event, validated. */
14
+ export interface AgentEventRecord {
15
+ sessionId: string;
16
+ turnId: string;
17
+ eventId: string;
18
+ sequence: number;
19
+ type: string;
20
+ /** The event object as sent, for example an agent.message event. */
21
+ event: Record<string, unknown>;
22
+ }
23
+ /** The platform half of the agent capability. */
24
+ export interface AgentPlatform {
25
+ /** Highest contiguous sequence already persisted for the session (0 when none). Asked once per session. */
26
+ ackedSequence(sessionId: string): Promise<number>;
27
+ /** Persists one durable event; resolves once durable. Called once per event, in sequence order per session. */
28
+ agentEvent(executor: HarnessExecutor, event: AgentEventRecord): Promise<void>;
29
+ /** Relays a transient preview. Never persisted. */
30
+ agentDelta(executor: HarnessExecutor, delta: SessionEventDeltaParams): void;
31
+ /** Opens (or for a repeated interaction_id returns) the platform interaction; returns its id. */
32
+ interactionRequested(executor: HarnessExecutor, request: InteractionRequestParams): Promise<string>;
33
+ /** Closes the platform interaction without an answer. */
34
+ interactionCanceled(executor: HarnessExecutor, cancel: InteractionCancelParams): Promise<void>;
35
+ }
36
+ export interface HarnessOptions {
37
+ /**
38
+ * Verifies every WebSocket upgrade (control and MCP). Throwing answers 401.
39
+ * Omitted accepts every connection: use that only on loopback.
40
+ */
41
+ authenticate?: (request: IncomingMessage) => Principal | Promise<Principal>;
42
+ /** Returned by executor.register. A disconnected executor keeps its sessions and calls for leaseTtlMs. */
43
+ heartbeatIntervalMs?: number;
44
+ leaseTtlMs?: number;
45
+ requestTimeoutMs?: number;
46
+ /** Accepted subprotocols in preference order; defaults to v1.1 then v1. */
47
+ protocols?: string[];
48
+ onExecutor?: (executor: HarnessExecutor) => void;
49
+ /** Persists a tool executor's session.event; omitted accepts with a generated id. */
50
+ onSessionEvent?: (executor: HarnessExecutor, params: SessionEventParams) => Promise<SessionEventResult>;
51
+ onSessionStatus?: (executor: HarnessExecutor, params: SessionStatusParams) => void;
52
+ onToolProgress?: (executor: HarnessExecutor, params: ToolProgressParams) => void;
53
+ onLog?: Logger;
54
+ /** Receives agent-capability traffic; omitted rejects it as unknown methods. */
55
+ agent?: AgentPlatform;
56
+ }
57
+ /** Where a connection came from: its principal and, for MCP, its session and server key. */
58
+ export interface AcceptOptions {
59
+ principal?: Principal;
60
+ channel?: ChannelScope;
61
+ sessionId?: string;
62
+ serverKey?: string;
63
+ /** @internal Set when the harness dialed the executor: where to dial its MCP channels. */
64
+ dialBack?: DialBack;
65
+ }
66
+ /** @internal */
67
+ export interface DialBack {
68
+ url: string;
69
+ token?: string;
70
+ }
71
+ export declare class Harness {
72
+ readonly options: Required<Pick<HarnessOptions, 'heartbeatIntervalMs' | 'leaseTtlMs' | 'requestTimeoutMs' | 'protocols'>> & HarnessOptions;
73
+ private readonly executorsById;
74
+ /** @internal */
75
+ readonly sessionsById: Map<string, HarnessSession>;
76
+ private changed;
77
+ private nextConn;
78
+ private readonly wss;
79
+ /** @internal */
80
+ readonly ledger: AgentLedger;
81
+ constructor(options?: HarnessOptions);
82
+ /** Executors currently registered (connected or within their lease). */
83
+ executors(): HarnessExecutor[];
84
+ /** Resolves with a connected executor, waiting for one to register. */
85
+ waitExecutor(signal?: AbortSignal): Promise<HarnessExecutor>;
86
+ /** @internal */
87
+ signal(): void;
88
+ /**
89
+ * Handles an HTTP upgrade for /v1/executors/channel style URLs: the control
90
+ * channel by default, an MCP channel with ?channel=mcp&session_id=&server_key=.
91
+ */
92
+ handleUpgrade(request: IncomingMessage, socket: Duplex, head: Buffer): Promise<void>;
93
+ /** Serves executor channels on an HTTP server's upgrade event. */
94
+ attach(server: Server): void;
95
+ /** Listens on host:port (defaults 127.0.0.1, an ephemeral port) and returns the ws:// URL. */
96
+ listen(port?: number, host?: string): Promise<{
97
+ url: string;
98
+ close(): Promise<void>;
99
+ }>;
100
+ /**
101
+ * Listens on a Unix domain socket or Windows named pipe (\\.\pipe\<name>)
102
+ * and returns its unix:// or npipe:// URL. Same handler, subprotocols,
103
+ * authenticate and close codes as listen; set authenticate (for example
104
+ * bearerAuthenticator with the local token).
105
+ */
106
+ listenLocal(path: string): Promise<{
107
+ url: string;
108
+ close(): Promise<void>;
109
+ }>;
110
+ /**
111
+ * Dials an executor that listens (listenExecutor) at a unix://, npipe:// or
112
+ * ws:// URL: the local "who dials" inversion. The harness also dials the
113
+ * MCP channels of that executor's sessions (HarnessSession.mcp).
114
+ */
115
+ dialExecutor(url: string, options?: {
116
+ token?: string;
117
+ }): Promise<void>;
118
+ /** @internal Opens the MCP channel of a session whose executor this harness dialed. */
119
+ dialMcp(executor: HarnessExecutor, sessionId: string, serverKey: string): Promise<void>;
120
+ /** A Dialer for an ExecutorClient in the same process. */
121
+ inProcessDialer(principal?: Principal): Dialer;
122
+ /** Takes over an established connection (any transport). */
123
+ accept(conn: Conn, options?: AcceptOptions): void;
124
+ /** Only the executor holding the session may serve its MCP servers. */
125
+ private mcpAllowed;
126
+ /** @internal */
127
+ executorFor(id: string): HarnessExecutor;
128
+ /** @internal */
129
+ forgetExecutor(executor: HarnessExecutor): void;
130
+ /** @internal */
131
+ log(level: LogLevel, message: string, error?: unknown): void;
132
+ }
133
+ /** One registered executor; it outlives a connection within its lease. */
134
+ export declare class HarnessExecutor {
135
+ private readonly harness;
136
+ readonly id: string;
137
+ /** @internal */ conn?: HarnessConn;
138
+ info: ExecutorInfo;
139
+ /** The negotiated protocol of the current connection. */
140
+ protocol: string;
141
+ environmentId: string;
142
+ capabilities: Capabilities;
143
+ /** @internal */ readonly sessionsById: Map<string, HarnessSession>;
144
+ /** @internal */ leaseTimer?: ReturnType<typeof setTimeout>;
145
+ /** @internal */ draining: boolean;
146
+ /** @internal */ gone: boolean;
147
+ /** @internal */ readonly eventIds: Map<string, SessionEventResult>;
148
+ /** @internal */ dialBack?: DialBack;
149
+ constructor(harness: Harness, id: string);
150
+ get connected(): boolean;
151
+ sessions(): HarnessSession[];
152
+ /** @internal */
153
+ call<T>(method: string, params: unknown): Promise<T>;
154
+ /** Asks the executor to stop accepting sessions; heartbeats report drain=true from now on. */
155
+ drain(reason: string): Promise<ExecutorDrainResult>;
156
+ /**
157
+ * Binds a session and sends session.assign. work_id and the lease token are
158
+ * generated when empty. The session is bound before the request is sent, so
159
+ * session.ready and MCP channels racing the answer are accepted.
160
+ */
161
+ assignSession(params: Omit<SessionAssignParams, 'work_id' | 'environment_id' | 'lease'> & Partial<SessionAssignParams>): Promise<HarnessSession>;
162
+ /** @internal Ends every session; pending tool calls fail with ToolOutcomeUnknownError. */
163
+ forget(reason: string): void;
164
+ /** The advertised agent capability, undefined when absent. */
165
+ get agentCapability(): AgentCapability | undefined;
166
+ private requireAgent;
167
+ /** turn.start; safe to repeat with the same turn_id after a timeout or reconnect. */
168
+ startTurn(params: TurnStartParams): Promise<TurnStartResult>;
169
+ /** turn.input; a conflict means the turn is over and the input belongs in a new turn.start. */
170
+ sendTurnInput(params: TurnInputParams): Promise<Ack>;
171
+ /** turn.cancel; the turn's end is still reported by its session.status_idle event. */
172
+ cancelTurn(params: TurnCancelParams): Promise<TurnCancelResult>;
173
+ closeAgentSession(params: SessionCloseParams): Promise<SessionCloseResult>;
174
+ /** Delivers the platform's answer; accepted=false means the executor no longer waits for it. */
175
+ respondInteraction(params: InteractionResponseParams): Promise<InteractionResponseResult>;
176
+ }
177
+ /** One session bound to an executor. */
178
+ export declare class HarnessSession {
179
+ private readonly harness;
180
+ readonly executor: HarnessExecutor;
181
+ readonly id: string;
182
+ readonly workId: string;
183
+ /** @internal */ readonly leaseToken: string;
184
+ private readonly readyDeferred;
185
+ private readonly doneDeferred;
186
+ private isReady;
187
+ /** Set once the session ended. */
188
+ endReason: string;
189
+ private readonly calls;
190
+ private readonly mcpChannels;
191
+ private mcpChanged;
192
+ constructor(harness: Harness, executor: HarnessExecutor, id: string, workId: string,
193
+ /** @internal */ leaseToken: string);
194
+ /** Resolves when the session ended. */
195
+ get done(): Promise<void>;
196
+ /** Waits for session.ready. */
197
+ ready(signal?: AbortSignal): Promise<void>;
198
+ /** @internal */
199
+ markReady(): void;
200
+ /**
201
+ * Sends tool.call and waits for its tool.result. Idempotent by tool_call_id
202
+ * (generated when empty): a repeat joins the in-flight call or returns the
203
+ * recorded result without re-dispatching. Aborting signal stops waiting
204
+ * only; use cancelTool to abort the tool.
205
+ */
206
+ callTool(params: Omit<ToolCallParams, 'session_id' | 'tool_call_id'> & {
207
+ tool_call_id?: string;
208
+ }, signal?: AbortSignal): Promise<ToolResultParams>;
209
+ /** Sends tool.cancel; the waiting callTool still receives the executor's tool.result. */
210
+ cancelTool(toolCallId: string, reason: string): Promise<boolean>;
211
+ /** Sends session.stop; the session ends locally on success. */
212
+ stop(reason: string): Promise<void>;
213
+ /**
214
+ * Waits for the executor to open the MCP channel for serverKey (one of the
215
+ * session.assign mcp_servers). A newer channel replaces the old one.
216
+ */
217
+ mcp(serverKey: string, signal?: AbortSignal): Promise<HarnessMcpChannel>;
218
+ /** @internal */
219
+ attachMcp(serverKey: string, channel: HarnessMcpChannel): void;
220
+ private notifyMcp;
221
+ /** @internal Records a tool.result; false when this harness never dispatched the call. */
222
+ complete(result: ToolResultParams): boolean;
223
+ /** @internal Unbinds the session, fails waiting calls and closes MCP channels. Idempotent. */
224
+ end(reason: string, pendingError: () => Error): void;
225
+ private race;
226
+ }
227
+ /**
228
+ * The harness end of one MCP channel, isolated by (session_id, server_key).
229
+ * Frames are raw MCP JSON-RPC: the harness is the MCP client. After a
230
+ * transport error, call session.mcp again and re-initialize.
231
+ */
232
+ export declare class HarnessMcpChannel extends FrameChannel {
233
+ }
234
+ /** Serializes agent events per session and remembers the acknowledged sequence. */
235
+ declare class AgentLedger {
236
+ readonly acked: Map<string, number>;
237
+ private readonly tails;
238
+ /** Runs fn after every earlier fn of the same session. */
239
+ serialize<T>(sessionId: string, fn: () => Promise<T>): Promise<T>;
240
+ }
241
+ /** The harness side of one connection. */
242
+ declare class HarnessConn {
243
+ private readonly harness;
244
+ readonly conn: Conn;
245
+ private readonly principal;
246
+ private readonly id;
247
+ private readonly dialBack?;
248
+ readonly peer: RpcPeer;
249
+ private executor?;
250
+ constructor(harness: Harness, conn: Conn, principal: Principal, id: string, dialBack?: DialBack | undefined);
251
+ private onRequest;
252
+ private register;
253
+ private disconnected;
254
+ private dispatch;
255
+ private sessionEvent;
256
+ /** Agent methods; undefined for everything else, including session.event without event_id. */
257
+ private dispatchAgent;
258
+ private agentEvent;
259
+ private onNotification;
260
+ }
261
+ export {};