@gizmo-ai/client 0.2.2
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/__tests__/client.test.d.ts +2 -0
- package/dist/__tests__/client.test.d.ts.map +1 -0
- package/dist/__tests__/subscribe.test.d.ts +2 -0
- package/dist/__tests__/subscribe.test.d.ts.map +1 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +835 -0
- package/dist/subscribe.d.ts +18 -0
- package/dist/subscribe.d.ts.map +1 -0
- package/dist/types.d.ts +218 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +33 -0
- package/src/__tests__/client.test.ts +412 -0
- package/src/__tests__/subscribe.test.ts +333 -0
- package/src/client.ts +214 -0
- package/src/errors.ts +16 -0
- package/src/index.ts +63 -0
- package/src/subscribe.ts +87 -0
- package/src/types.ts +271 -0
package/src/subscribe.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Subscription
|
|
3
|
+
*
|
|
4
|
+
* Opens an EventSource to the agent's stream endpoint, maintains local state,
|
|
5
|
+
* applies JSON Patches (RFC 6902), and calls back with the full patched state
|
|
6
|
+
* plus the triggering event.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { applyPatch } from "fast-json-patch";
|
|
10
|
+
import type {
|
|
11
|
+
SSEEvent,
|
|
12
|
+
InitialStateEvent,
|
|
13
|
+
StateUpdateEvent,
|
|
14
|
+
ExecutionStatusEvent,
|
|
15
|
+
HeartbeatEvent,
|
|
16
|
+
CustomEvent,
|
|
17
|
+
SubscribeCallback,
|
|
18
|
+
SubscribeOptions,
|
|
19
|
+
Unsubscribe,
|
|
20
|
+
} from "./types.ts";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to an agent's SSE stream.
|
|
24
|
+
*
|
|
25
|
+
* Maintains a local state object, applies incoming patches, and fires
|
|
26
|
+
* the callback with a consistent snapshot after every event.
|
|
27
|
+
*
|
|
28
|
+
* @returns An unsubscribe function that closes the EventSource.
|
|
29
|
+
*/
|
|
30
|
+
export function subscribe(
|
|
31
|
+
baseUrl: string,
|
|
32
|
+
callback: SubscribeCallback,
|
|
33
|
+
options?: SubscribeOptions,
|
|
34
|
+
): Unsubscribe {
|
|
35
|
+
const endpoint = options?.endpoint ?? "/events";
|
|
36
|
+
const url = `${baseUrl}${endpoint}`;
|
|
37
|
+
|
|
38
|
+
const slices: Record<string, unknown> = {};
|
|
39
|
+
const es = new EventSource(url);
|
|
40
|
+
|
|
41
|
+
// state.initial — set full local state
|
|
42
|
+
es.addEventListener("state.initial", (e: MessageEvent) => {
|
|
43
|
+
const event: InitialStateEvent = JSON.parse(e.data);
|
|
44
|
+
// Reset and populate slices
|
|
45
|
+
for (const key of Object.keys(slices)) delete slices[key];
|
|
46
|
+
Object.assign(slices, event.slices);
|
|
47
|
+
callback({ slices: { ...slices }, event });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// state.update — apply patches or full value replacement
|
|
51
|
+
es.addEventListener("state.update", (e: MessageEvent) => {
|
|
52
|
+
const event: StateUpdateEvent = JSON.parse(e.data);
|
|
53
|
+
|
|
54
|
+
if (event.patches && event.patches.length > 0) {
|
|
55
|
+
const current = slices[event.slice] ?? {};
|
|
56
|
+
const cloned = structuredClone(current);
|
|
57
|
+
const result = applyPatch(cloned, event.patches);
|
|
58
|
+
slices[event.slice] = result.newDocument;
|
|
59
|
+
} else if ("value" in event) {
|
|
60
|
+
slices[event.slice] = event.value;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
callback({ slices: { ...slices }, event });
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// execution.status — fire callback, no state mutation
|
|
67
|
+
es.addEventListener("execution.status", (e: MessageEvent) => {
|
|
68
|
+
const event: ExecutionStatusEvent = JSON.parse(e.data);
|
|
69
|
+
callback({ slices: { ...slices }, event });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// heartbeat — fire callback, no state mutation
|
|
73
|
+
es.addEventListener("heartbeat", (e: MessageEvent) => {
|
|
74
|
+
const event: HeartbeatEvent = JSON.parse(e.data);
|
|
75
|
+
callback({ slices: { ...slices }, event });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// custom-event — fire callback, no state mutation
|
|
79
|
+
es.addEventListener("custom-event", (e: MessageEvent) => {
|
|
80
|
+
const event: CustomEvent = JSON.parse(e.data);
|
|
81
|
+
callback({ slices: { ...slices }, event });
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return () => {
|
|
85
|
+
es.close();
|
|
86
|
+
};
|
|
87
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @gizmo-ai/client — Standalone type definitions
|
|
3
|
+
*
|
|
4
|
+
* All types are redeclared here — no imports from @gizmo-ai/server or @gizmo-ai/runtime.
|
|
5
|
+
* This keeps the client zero-coupling with the server packages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Operation } from "fast-json-patch";
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Manifest
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
export interface GizmoManifest {
|
|
15
|
+
manifestVersion: string;
|
|
16
|
+
identity: {
|
|
17
|
+
agentId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
version: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
documentationUrl?: string;
|
|
22
|
+
};
|
|
23
|
+
auth?: {
|
|
24
|
+
type?: string;
|
|
25
|
+
scopes?: string[];
|
|
26
|
+
};
|
|
27
|
+
endpoints: {
|
|
28
|
+
invoke: string;
|
|
29
|
+
state: string;
|
|
30
|
+
runs: string;
|
|
31
|
+
streams: {
|
|
32
|
+
state: string;
|
|
33
|
+
actions: string;
|
|
34
|
+
events: string;
|
|
35
|
+
};
|
|
36
|
+
control?: {
|
|
37
|
+
cancel: string;
|
|
38
|
+
continue: string;
|
|
39
|
+
};
|
|
40
|
+
plugins?: Record<string, string>;
|
|
41
|
+
};
|
|
42
|
+
state: {
|
|
43
|
+
slices: string[];
|
|
44
|
+
schemaVersion: string;
|
|
45
|
+
schemaRef?: string;
|
|
46
|
+
schema?: {
|
|
47
|
+
type: "object";
|
|
48
|
+
properties: Record<string, unknown>;
|
|
49
|
+
$defs?: Record<string, unknown>;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
actions?: {
|
|
53
|
+
publicContractsRef: string;
|
|
54
|
+
};
|
|
55
|
+
capabilities: {
|
|
56
|
+
tools?: string[];
|
|
57
|
+
skills?: Array<{
|
|
58
|
+
name: string;
|
|
59
|
+
description?: string;
|
|
60
|
+
source?: string;
|
|
61
|
+
}>;
|
|
62
|
+
models?: string[];
|
|
63
|
+
features?: string[];
|
|
64
|
+
plugins?: string[];
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// SSE Events
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
export interface InitialStateEvent {
|
|
73
|
+
type: "state.initial";
|
|
74
|
+
slices: Record<string, unknown>;
|
|
75
|
+
timestamp: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface StateUpdateEvent {
|
|
79
|
+
type: "state.update";
|
|
80
|
+
slice: string;
|
|
81
|
+
patches?: Operation[];
|
|
82
|
+
value?: unknown;
|
|
83
|
+
cause?: {
|
|
84
|
+
type: string;
|
|
85
|
+
id?: string;
|
|
86
|
+
payload?: unknown;
|
|
87
|
+
};
|
|
88
|
+
timestamp: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ExecutionStatusEvent {
|
|
92
|
+
type: "execution.status";
|
|
93
|
+
status: "started" | "completed" | "failed" | "aborted" | "interrupted";
|
|
94
|
+
executionId?: string;
|
|
95
|
+
turnId?: string;
|
|
96
|
+
error?: string;
|
|
97
|
+
timestamp: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface HeartbeatEvent {
|
|
101
|
+
type: "heartbeat";
|
|
102
|
+
timestamp: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface CustomEvent {
|
|
106
|
+
type: "custom-event";
|
|
107
|
+
data: unknown;
|
|
108
|
+
timestamp: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type SSEEvent =
|
|
112
|
+
| InitialStateEvent
|
|
113
|
+
| StateUpdateEvent
|
|
114
|
+
| ExecutionStatusEvent
|
|
115
|
+
| HeartbeatEvent
|
|
116
|
+
| CustomEvent;
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// API Request/Response Types
|
|
120
|
+
// ============================================================================
|
|
121
|
+
|
|
122
|
+
export interface InvokeResponse {
|
|
123
|
+
executionId: string;
|
|
124
|
+
turnId: string;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface AbortResponse {
|
|
128
|
+
status: "aborted";
|
|
129
|
+
executionId: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface RunSummary {
|
|
133
|
+
executionId: string;
|
|
134
|
+
startTime: number;
|
|
135
|
+
endTime?: number;
|
|
136
|
+
status: "running" | "completed" | "failed" | "aborted";
|
|
137
|
+
actionCount: number;
|
|
138
|
+
error?: string;
|
|
139
|
+
digest?: string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface RunDetails extends RunSummary {
|
|
143
|
+
actions: Array<{ type: string; payload?: unknown }>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface RunsResponse {
|
|
147
|
+
runs: RunSummary[];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface RunListParams {
|
|
151
|
+
after?: number;
|
|
152
|
+
before?: number;
|
|
153
|
+
status?: "running" | "completed" | "failed" | "aborted";
|
|
154
|
+
limit?: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface HydrateOptions {
|
|
158
|
+
upToSeq?: number;
|
|
159
|
+
upToTurn?: number;
|
|
160
|
+
thenExecute?: string;
|
|
161
|
+
hydrateMode?: "full" | "slim";
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export interface HydrateResponse {
|
|
165
|
+
status: "hydrated";
|
|
166
|
+
actionCount: number;
|
|
167
|
+
state: {
|
|
168
|
+
conversationLength: number;
|
|
169
|
+
loopCount: number;
|
|
170
|
+
};
|
|
171
|
+
execution?: {
|
|
172
|
+
executionId: string;
|
|
173
|
+
turnId: string;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface HealthResponse {
|
|
178
|
+
status: "ok";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export interface DispatchRequest {
|
|
182
|
+
type: string;
|
|
183
|
+
payload?: unknown;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============================================================================
|
|
187
|
+
// Approval Types
|
|
188
|
+
// ============================================================================
|
|
189
|
+
|
|
190
|
+
export type ApprovalStatus = "pending" | "approved" | "rejected" | "expired";
|
|
191
|
+
|
|
192
|
+
export interface Approval {
|
|
193
|
+
id: string;
|
|
194
|
+
executionId: string;
|
|
195
|
+
toolCall: {
|
|
196
|
+
name: string;
|
|
197
|
+
args: Record<string, unknown>;
|
|
198
|
+
};
|
|
199
|
+
status: ApprovalStatus;
|
|
200
|
+
requestedAt: number;
|
|
201
|
+
expiresAt?: number;
|
|
202
|
+
decidedAt?: number;
|
|
203
|
+
reason?: string;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export interface ApprovalsListResponse {
|
|
207
|
+
approvals: Approval[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface ApprovalResponse {
|
|
211
|
+
approval: Approval;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface HistoryResponse {
|
|
215
|
+
history: Approval[];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ============================================================================
|
|
219
|
+
// Subscribe Types
|
|
220
|
+
// ============================================================================
|
|
221
|
+
|
|
222
|
+
export interface StateSnapshot {
|
|
223
|
+
slices: Record<string, unknown>;
|
|
224
|
+
event: SSEEvent;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type SubscribeCallback = (snapshot: StateSnapshot) => void;
|
|
228
|
+
|
|
229
|
+
export type Unsubscribe = () => void;
|
|
230
|
+
|
|
231
|
+
export interface SubscribeOptions {
|
|
232
|
+
/** SSE endpoint path (default: "/events") */
|
|
233
|
+
endpoint?: string;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Client Types
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
export interface ClientOptions {
|
|
241
|
+
/** Custom fetch implementation (for testing or environments without global fetch) */
|
|
242
|
+
fetch?: typeof globalThis.fetch;
|
|
243
|
+
/** Default headers to include on every request (e.g., auth tokens) */
|
|
244
|
+
headers?: Record<string, string>;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface GizmoClient {
|
|
248
|
+
discover(): Promise<GizmoManifest>;
|
|
249
|
+
invoke(input: string): Promise<InvokeResponse>;
|
|
250
|
+
abort(): Promise<AbortResponse>;
|
|
251
|
+
state(slice?: string): Promise<unknown>;
|
|
252
|
+
subscribe(callback: SubscribeCallback, options?: SubscribeOptions): Unsubscribe;
|
|
253
|
+
health(): Promise<HealthResponse>;
|
|
254
|
+
dispatch(action: DispatchRequest): Promise<unknown>;
|
|
255
|
+
|
|
256
|
+
runs: {
|
|
257
|
+
list(params?: RunListParams): Promise<RunSummary[]>;
|
|
258
|
+
get(executionId: string): Promise<RunDetails>;
|
|
259
|
+
actions(executionId: string): Promise<RunDetails["actions"]>;
|
|
260
|
+
cancel(executionId: string): Promise<{ status: string; executionId: string }>;
|
|
261
|
+
hydrate(executionId: string, options?: HydrateOptions): Promise<HydrateResponse>;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
approvals: {
|
|
265
|
+
list(): Promise<Approval[]>;
|
|
266
|
+
history(): Promise<Approval[]>;
|
|
267
|
+
get(id: string): Promise<Approval>;
|
|
268
|
+
approve(id: string, reason?: string): Promise<Approval>;
|
|
269
|
+
reject(id: string, reason?: string): Promise<Approval>;
|
|
270
|
+
};
|
|
271
|
+
}
|