@onesi/genesis 0.1.13 → 0.2.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 +1 -1
- package/dist/auto.d.ts +1010 -0
- package/dist/auto.js +125 -0
- package/dist/beacons.d.ts +42 -0
- package/dist/beacons.js +71 -0
- package/dist/blinds-owner-enrollment.d.ts +109 -0
- package/dist/blinds-owner-enrollment.js +21 -2
- package/dist/client.d.ts +3 -19947
- package/dist/client.js +124 -17
- package/dist/errors.js +1 -1
- package/dist/flares.d.ts +32 -0
- package/dist/flares.js +5 -0
- package/dist/fleets.d.ts +32 -0
- package/dist/fleets.js +59 -6
- package/dist/flocks.d.ts +285 -1
- package/dist/flocks.js +59 -0
- package/dist/foundry.d.ts +424 -0
- package/dist/foundry.js +133 -0
- package/dist/garden.d.ts +754 -0
- package/dist/garden.js +76 -0
- package/dist/gateway.d.ts +365 -0
- package/dist/gateway.js +82 -0
- package/dist/gears.d.ts +121 -2
- package/dist/gears.js +24 -2
- package/dist/generated/operations.d.ts +3 -19948
- package/dist/generated/operations.js +54590 -13302
- package/dist/generated/schema.d.ts +69793 -17548
- package/dist/genesis.d.ts +7908 -1260
- package/dist/genesis.js +90 -25
- package/dist/genesis.openapi.json +109121 -29920
- package/dist/henry.d.ts +202 -1
- package/dist/henry.js +223 -0
- package/dist/hives-environment.d.ts +9 -0
- package/dist/hives-environment.js +22 -0
- package/dist/hives-node.js +3 -0
- package/dist/hives-operations.d.ts +114 -0
- package/dist/hives-operations.js +2 -0
- package/dist/hives-platform.d.ts +149 -0
- package/dist/hives-platform.js +183 -0
- package/dist/hives-sse.d.ts +24 -0
- package/dist/hives-sse.js +111 -0
- package/dist/iam-directory.d.ts +8 -0
- package/dist/iam-directory.js +23 -0
- package/dist/iam.d.ts +391 -0
- package/dist/iam.js +92 -0
- package/dist/idempotency.d.ts +3 -0
- package/dist/idempotency.js +22 -0
- package/dist/index.d.ts +18 -4
- package/dist/index.js +16 -4
- package/dist/instances.d.ts +177 -2
- package/dist/instances.js +37 -5
- package/dist/minds-agent.d.ts +28 -0
- package/dist/minds-agent.js +53 -10
- package/dist/minds-engine.d.ts +394 -0
- package/dist/minds-engine.js +555 -0
- package/dist/minds.d.ts +109 -1
- package/dist/minds.js +49 -14
- package/dist/orgs.d.ts +405 -0
- package/dist/orgs.js +63 -0
- package/dist/runner.d.ts +4 -0
- package/dist/runner.js +1 -0
- package/dist/scopes.d.ts +310 -0
- package/dist/scopes.js +40 -0
- package/dist/taps.d.ts +118 -0
- package/dist/taps.js +45 -0
- package/dist/traces.d.ts +574 -0
- package/dist/traces.js +97 -0
- package/dist/types.d.ts +12 -1
- package/package.json +1 -1
package/dist/henry.d.ts
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
import type { GenesisClient } from './client.js';
|
|
2
2
|
import type { OperationInput } from './types.js';
|
|
3
|
+
import type { components } from './generated/schema.js';
|
|
4
|
+
export type HenryRun = components['schemas']['Henry.Run'];
|
|
5
|
+
export type HenryRunEvent = components['schemas']['Henry.RunEvent'];
|
|
6
|
+
export type HenryRunEventMessage = components['schemas']['Henry.RunEventMessage'];
|
|
7
|
+
export type HenryRunStreamStatus = components['schemas']['Henry.RunStreamStatus'];
|
|
8
|
+
export type HenryMessageDelivery = components['schemas']['Henry.RunMessageDelivery'];
|
|
9
|
+
/** One event of a live run stream. `end` comes once the run is completed, failed or cancelled. */
|
|
10
|
+
export type HenryStreamEvent = {
|
|
11
|
+
event: 'activity';
|
|
12
|
+
data: HenryRunEvent;
|
|
13
|
+
} | {
|
|
14
|
+
event: 'status' | 'end';
|
|
15
|
+
data: HenryRunStreamStatus;
|
|
16
|
+
} | {
|
|
17
|
+
event: 'error';
|
|
18
|
+
data: {
|
|
19
|
+
message: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
export interface HenryStreamOptions {
|
|
23
|
+
/** Start after this activity seq (default 0: from the first event). */
|
|
24
|
+
afterSeq?: number;
|
|
25
|
+
/** Stops the stream; the iterator then returns without an error. */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
/** Reconnects allowed after a stream closes without `end` (1-1000, default 100). Henry closes each stream within 120 s. */
|
|
28
|
+
maxReconnects?: number;
|
|
29
|
+
/** Reconnect when no byte (event or 15 s keepalive) arrives for this long (default 45000 ms). */
|
|
30
|
+
idleMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/** A turn: the inputs that led to one model answer, and that answer. Its number is the `turn` to fork from to re-run it. */
|
|
33
|
+
export interface HenryTurn {
|
|
34
|
+
turn: number;
|
|
35
|
+
inputs: HenryRunEventMessage[];
|
|
36
|
+
events: Pick<HenryRunEvent, 'seq' | 'at' | 'type' | 'detail'>[];
|
|
37
|
+
answer: HenryRunEventMessage;
|
|
38
|
+
}
|
|
39
|
+
export interface HenryReplay {
|
|
40
|
+
run_id: string;
|
|
41
|
+
status?: string;
|
|
42
|
+
turns: HenryTurn[];
|
|
43
|
+
pending: Omit<HenryTurn, 'answer'>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fold a run's events into turns. Turn N holds the inputs that led to the run's (N+1)th model answer and that answer, so
|
|
47
|
+
* forking from turn N re-runs that answer. Events after the last answer are returned as `pending`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function foldHenryTurns(events: HenryRunEvent[]): {
|
|
50
|
+
turns: HenryTurn[];
|
|
51
|
+
pending: Omit<HenryTurn, 'answer'>;
|
|
52
|
+
};
|
|
3
53
|
/** Henry always derives its organization from the gateway's verified caller. */
|
|
4
54
|
export declare function henryModule(client: GenesisClient): {
|
|
5
55
|
config: () => Promise<{
|
|
@@ -27,9 +77,20 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
27
77
|
publicKeyBase64: string;
|
|
28
78
|
}>;
|
|
29
79
|
};
|
|
80
|
+
blinds: {
|
|
81
|
+
/** HTTPS request through the organization's bound Blinds machine. */
|
|
82
|
+
proxy: (body: NonNullable<OperationInput<"henry_proxyBlinds">["body"]>) => Promise<{
|
|
83
|
+
request_id?: string;
|
|
84
|
+
status: number;
|
|
85
|
+
headers: {
|
|
86
|
+
[key: string]: string;
|
|
87
|
+
};
|
|
88
|
+
body: string;
|
|
89
|
+
}>;
|
|
90
|
+
};
|
|
30
91
|
runs: {
|
|
31
92
|
list: (page_token?: string) => Promise<{
|
|
32
|
-
runs:
|
|
93
|
+
runs: components["schemas"]["Henry.Run"][];
|
|
33
94
|
next_page_token?: string | null;
|
|
34
95
|
}>;
|
|
35
96
|
create: (body: NonNullable<OperationInput<"henry_createRun">["body"]>) => Promise<{
|
|
@@ -44,6 +105,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
44
105
|
at: string;
|
|
45
106
|
type: string;
|
|
46
107
|
detail: string;
|
|
108
|
+
message_index?: number;
|
|
109
|
+
message_id?: string;
|
|
47
110
|
}[];
|
|
48
111
|
approvals: {
|
|
49
112
|
id: string;
|
|
@@ -57,6 +120,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
57
120
|
code?: number;
|
|
58
121
|
receipt?: Record<string, never>;
|
|
59
122
|
usage?: Record<string, never> | null;
|
|
123
|
+
started_at?: string;
|
|
124
|
+
ended_at?: string;
|
|
60
125
|
}[];
|
|
61
126
|
messages: ({
|
|
62
127
|
role: string;
|
|
@@ -64,6 +129,16 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
64
129
|
} & {
|
|
65
130
|
[key: string]: unknown;
|
|
66
131
|
})[];
|
|
132
|
+
trace_id?: string;
|
|
133
|
+
forked_from?: {
|
|
134
|
+
run_id: string;
|
|
135
|
+
turn: number;
|
|
136
|
+
text_sha256?: string;
|
|
137
|
+
};
|
|
138
|
+
inbox?: components["schemas"]["Henry.RunInboxMessage"][];
|
|
139
|
+
interrupt_execution?: string;
|
|
140
|
+
observatory_version?: number;
|
|
141
|
+
telemetry?: components["schemas"]["Henry.RunTelemetry"];
|
|
67
142
|
}>;
|
|
68
143
|
get: (run_id: string) => Promise<{
|
|
69
144
|
id: string;
|
|
@@ -77,6 +152,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
77
152
|
at: string;
|
|
78
153
|
type: string;
|
|
79
154
|
detail: string;
|
|
155
|
+
message_index?: number;
|
|
156
|
+
message_id?: string;
|
|
80
157
|
}[];
|
|
81
158
|
approvals: {
|
|
82
159
|
id: string;
|
|
@@ -90,6 +167,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
90
167
|
code?: number;
|
|
91
168
|
receipt?: Record<string, never>;
|
|
92
169
|
usage?: Record<string, never> | null;
|
|
170
|
+
started_at?: string;
|
|
171
|
+
ended_at?: string;
|
|
93
172
|
}[];
|
|
94
173
|
messages: ({
|
|
95
174
|
role: string;
|
|
@@ -97,6 +176,32 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
97
176
|
} & {
|
|
98
177
|
[key: string]: unknown;
|
|
99
178
|
})[];
|
|
179
|
+
trace_id?: string;
|
|
180
|
+
forked_from?: {
|
|
181
|
+
run_id: string;
|
|
182
|
+
turn: number;
|
|
183
|
+
text_sha256?: string;
|
|
184
|
+
};
|
|
185
|
+
inbox?: components["schemas"]["Henry.RunInboxMessage"][];
|
|
186
|
+
interrupt_execution?: string;
|
|
187
|
+
observatory_version?: number;
|
|
188
|
+
telemetry?: components["schemas"]["Henry.RunTelemetry"];
|
|
189
|
+
}>;
|
|
190
|
+
/** Page activity events: pass the previous page's next_after_seq as after_seq. */
|
|
191
|
+
events: (run_id: string, query?: {
|
|
192
|
+
after_seq?: number;
|
|
193
|
+
limit?: number;
|
|
194
|
+
}) => Promise<{
|
|
195
|
+
run_id: string;
|
|
196
|
+
status?: string;
|
|
197
|
+
events: components["schemas"]["Henry.RunEvent"][];
|
|
198
|
+
next_after_seq: number;
|
|
199
|
+
has_more: boolean;
|
|
200
|
+
}>;
|
|
201
|
+
/** Delete a finished run; active runs must be cancelled first (409). */
|
|
202
|
+
delete: (run_id: string) => Promise<{
|
|
203
|
+
run_id: string;
|
|
204
|
+
deleted: boolean;
|
|
100
205
|
}>;
|
|
101
206
|
command: (run_id: string, action: "pause" | "resume" | "cancel" | "input", text?: string) => Promise<{
|
|
102
207
|
id: string;
|
|
@@ -110,6 +215,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
110
215
|
at: string;
|
|
111
216
|
type: string;
|
|
112
217
|
detail: string;
|
|
218
|
+
message_index?: number;
|
|
219
|
+
message_id?: string;
|
|
113
220
|
}[];
|
|
114
221
|
approvals: {
|
|
115
222
|
id: string;
|
|
@@ -123,6 +230,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
123
230
|
code?: number;
|
|
124
231
|
receipt?: Record<string, never>;
|
|
125
232
|
usage?: Record<string, never> | null;
|
|
233
|
+
started_at?: string;
|
|
234
|
+
ended_at?: string;
|
|
126
235
|
}[];
|
|
127
236
|
messages: ({
|
|
128
237
|
role: string;
|
|
@@ -130,6 +239,16 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
130
239
|
} & {
|
|
131
240
|
[key: string]: unknown;
|
|
132
241
|
})[];
|
|
242
|
+
trace_id?: string;
|
|
243
|
+
forked_from?: {
|
|
244
|
+
run_id: string;
|
|
245
|
+
turn: number;
|
|
246
|
+
text_sha256?: string;
|
|
247
|
+
};
|
|
248
|
+
inbox?: components["schemas"]["Henry.RunInboxMessage"][];
|
|
249
|
+
interrupt_execution?: string;
|
|
250
|
+
observatory_version?: number;
|
|
251
|
+
telemetry?: components["schemas"]["Henry.RunTelemetry"];
|
|
133
252
|
}>;
|
|
134
253
|
approval: (run_id: string, approval_id: string, decision: "approve" | "reject") => Promise<{
|
|
135
254
|
id: string;
|
|
@@ -143,6 +262,70 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
143
262
|
at: string;
|
|
144
263
|
type: string;
|
|
145
264
|
detail: string;
|
|
265
|
+
message_index?: number;
|
|
266
|
+
message_id?: string;
|
|
267
|
+
}[];
|
|
268
|
+
approvals: {
|
|
269
|
+
id: string;
|
|
270
|
+
action: string;
|
|
271
|
+
url: string;
|
|
272
|
+
status: string;
|
|
273
|
+
requested_at: string;
|
|
274
|
+
}[];
|
|
275
|
+
phases: {
|
|
276
|
+
id?: string;
|
|
277
|
+
code?: number;
|
|
278
|
+
receipt?: Record<string, never>;
|
|
279
|
+
usage?: Record<string, never> | null;
|
|
280
|
+
started_at?: string;
|
|
281
|
+
ended_at?: string;
|
|
282
|
+
}[];
|
|
283
|
+
messages: ({
|
|
284
|
+
role: string;
|
|
285
|
+
content?: string | null;
|
|
286
|
+
} & {
|
|
287
|
+
[key: string]: unknown;
|
|
288
|
+
})[];
|
|
289
|
+
trace_id?: string;
|
|
290
|
+
forked_from?: {
|
|
291
|
+
run_id: string;
|
|
292
|
+
turn: number;
|
|
293
|
+
text_sha256?: string;
|
|
294
|
+
};
|
|
295
|
+
inbox?: components["schemas"]["Henry.RunInboxMessage"][];
|
|
296
|
+
interrupt_execution?: string;
|
|
297
|
+
observatory_version?: number;
|
|
298
|
+
telemetry?: components["schemas"]["Henry.RunTelemetry"];
|
|
299
|
+
}>;
|
|
300
|
+
/**
|
|
301
|
+
* Send a message into a run: `steer` lands before the next model call, `queue` (default) after the current turn,
|
|
302
|
+
* `interrupt` cancels the turn in flight. A completed run takes it at once and runs the next turn in this request.
|
|
303
|
+
*/
|
|
304
|
+
message: (run_id: string, body: {
|
|
305
|
+
text: string;
|
|
306
|
+
delivery?: HenryMessageDelivery;
|
|
307
|
+
}) => Promise<{
|
|
308
|
+
message: components["schemas"]["Henry.RunMessageReceipt"];
|
|
309
|
+
run: components["schemas"]["Henry.Run"];
|
|
310
|
+
}>;
|
|
311
|
+
/**
|
|
312
|
+
* Start a new run from turn N of this one (0 = the original task; default the latest, which needs text). run_id is
|
|
313
|
+
* the new run's idempotency key: one is generated unless supplied, and a retry with it returns the same run.
|
|
314
|
+
*/
|
|
315
|
+
fork: (run_id: string, body?: NonNullable<OperationInput<"henry_forkRun">["body"]>) => Promise<{
|
|
316
|
+
id: string;
|
|
317
|
+
task: string;
|
|
318
|
+
model: string;
|
|
319
|
+
status: "ready" | "running" | "acting" | "paused" | "awaiting_approval" | "completed" | "cancelled" | "failed" | "waiting_input";
|
|
320
|
+
created_at: string;
|
|
321
|
+
updated_at: string;
|
|
322
|
+
activity: {
|
|
323
|
+
id: string;
|
|
324
|
+
at: string;
|
|
325
|
+
type: string;
|
|
326
|
+
detail: string;
|
|
327
|
+
message_index?: number;
|
|
328
|
+
message_id?: string;
|
|
146
329
|
}[];
|
|
147
330
|
approvals: {
|
|
148
331
|
id: string;
|
|
@@ -156,6 +339,8 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
156
339
|
code?: number;
|
|
157
340
|
receipt?: Record<string, never>;
|
|
158
341
|
usage?: Record<string, never> | null;
|
|
342
|
+
started_at?: string;
|
|
343
|
+
ended_at?: string;
|
|
159
344
|
}[];
|
|
160
345
|
messages: ({
|
|
161
346
|
role: string;
|
|
@@ -163,6 +348,22 @@ export declare function henryModule(client: GenesisClient): {
|
|
|
163
348
|
} & {
|
|
164
349
|
[key: string]: unknown;
|
|
165
350
|
})[];
|
|
351
|
+
trace_id?: string;
|
|
352
|
+
forked_from?: {
|
|
353
|
+
run_id: string;
|
|
354
|
+
turn: number;
|
|
355
|
+
text_sha256?: string;
|
|
356
|
+
};
|
|
357
|
+
inbox?: components["schemas"]["Henry.RunInboxMessage"][];
|
|
358
|
+
interrupt_execution?: string;
|
|
359
|
+
observatory_version?: number;
|
|
360
|
+
telemetry?: components["schemas"]["Henry.RunTelemetry"];
|
|
166
361
|
}>;
|
|
362
|
+
/** Follow a run live: an async iterator over its stream, reconnecting with Last-Event-ID until `end`. */
|
|
363
|
+
streamEvents: (run_id: string, options?: HenryStreamOptions) => AsyncGenerator<HenryStreamEvent, any, any>;
|
|
364
|
+
/** Read every event of a run and fold them into turns (see foldHenryTurns). */
|
|
365
|
+
replay: (run_id: string, options?: {
|
|
366
|
+
signal?: AbortSignal;
|
|
367
|
+
}) => Promise<HenryReplay>;
|
|
167
368
|
};
|
|
168
369
|
};
|
package/dist/henry.js
CHANGED
|
@@ -1,4 +1,183 @@
|
|
|
1
1
|
import { GenesisError } from './errors.js';
|
|
2
|
+
const MAX_SSE_EVENT_CHARS = 4 * 1024 * 1024;
|
|
3
|
+
const REPLAY_PAGE = 500;
|
|
4
|
+
const MAX_REPLAY_PAGES = 2000;
|
|
5
|
+
/**
|
|
6
|
+
* Fold a run's events into turns. Turn N holds the inputs that led to the run's (N+1)th model answer and that answer, so
|
|
7
|
+
* forking from turn N re-runs that answer. Events after the last answer are returned as `pending`.
|
|
8
|
+
*/
|
|
9
|
+
export function foldHenryTurns(events) {
|
|
10
|
+
const turns = [];
|
|
11
|
+
let current = { turn: 0, inputs: [], events: [] };
|
|
12
|
+
for (const event of events) {
|
|
13
|
+
current.events.push({ seq: event.seq, at: event.at, type: event.type, ...(event.detail === undefined ? {} : { detail: event.detail }) });
|
|
14
|
+
const message = event.message;
|
|
15
|
+
if (message?.role === 'assistant') {
|
|
16
|
+
turns.push({ ...current, answer: message });
|
|
17
|
+
current = { turn: turns.length, inputs: [], events: [] };
|
|
18
|
+
}
|
|
19
|
+
else if (message)
|
|
20
|
+
current.inputs.push(message);
|
|
21
|
+
}
|
|
22
|
+
return { turns, pending: current };
|
|
23
|
+
}
|
|
24
|
+
const invalid = (operationId, message) => new GenesisError({ status: 0, type: 'invalid_request', operationId, message });
|
|
25
|
+
const STREAM_EVENTS = new Set(['activity', 'status', 'end', 'error']);
|
|
26
|
+
/** Parse one SSE frame's lines into { event, id, data }; undefined for comments, keepalives and data-less frames. */
|
|
27
|
+
function frame(lines) {
|
|
28
|
+
let event = 'message';
|
|
29
|
+
let id;
|
|
30
|
+
const data = [];
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
if (line.startsWith(':'))
|
|
33
|
+
continue;
|
|
34
|
+
const colon = line.indexOf(':');
|
|
35
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
36
|
+
let value = colon < 0 ? '' : line.slice(colon + 1);
|
|
37
|
+
if (value.startsWith(' '))
|
|
38
|
+
value = value.slice(1);
|
|
39
|
+
if (field === 'event')
|
|
40
|
+
event = value;
|
|
41
|
+
else if (field === 'id')
|
|
42
|
+
id = value;
|
|
43
|
+
else if (field === 'data')
|
|
44
|
+
data.push(value);
|
|
45
|
+
}
|
|
46
|
+
return data.length ? { event, ...(id === undefined ? {} : { id }), data: data.join('\n') } : undefined;
|
|
47
|
+
}
|
|
48
|
+
const wait = (ms, signal) => new Promise(resolve => {
|
|
49
|
+
const done = () => { clearTimeout(timer); signal?.removeEventListener('abort', done); resolve(); };
|
|
50
|
+
const timer = setTimeout(done, ms);
|
|
51
|
+
signal?.addEventListener('abort', done, { once: true });
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Follow a run over SSE, reconnecting with Last-Event-ID after each stream closes (Henry closes one within 120 s) until
|
|
55
|
+
* `end`, the signal aborts or maxReconnects is used up. Never buffers a whole stream.
|
|
56
|
+
*/
|
|
57
|
+
async function* streamRunEvents(client, run_id, options = {}) {
|
|
58
|
+
const operationId = 'henry_listRunEvents';
|
|
59
|
+
const { signal } = options;
|
|
60
|
+
let last = options.afterSeq ?? 0;
|
|
61
|
+
let lastStatus;
|
|
62
|
+
if (!Number.isSafeInteger(last) || last < 0 || last > 9_999_999)
|
|
63
|
+
throw invalid(operationId, 'afterSeq must be an integer from 0 to 9999999.');
|
|
64
|
+
const maxReconnects = options.maxReconnects ?? 100;
|
|
65
|
+
if (!Number.isInteger(maxReconnects) || maxReconnects < 1 || maxReconnects > 1000)
|
|
66
|
+
throw invalid(operationId, 'maxReconnects must be an integer from 1 to 1000.');
|
|
67
|
+
const idleMs = options.idleMs ?? 45_000;
|
|
68
|
+
if (!Number.isInteger(idleMs) || idleMs < 1000 || idleMs > 300_000)
|
|
69
|
+
throw invalid(operationId, 'idleMs must be an integer from 1000 to 300000.');
|
|
70
|
+
for (let attempt = 0; attempt <= maxReconnects && !signal?.aborted; attempt++) {
|
|
71
|
+
// One connection: aborted by the caller's signal, or when the stream goes quiet for idleMs.
|
|
72
|
+
const connection = new AbortController();
|
|
73
|
+
const abort = () => connection.abort();
|
|
74
|
+
signal?.addEventListener('abort', abort, { once: true });
|
|
75
|
+
let idle;
|
|
76
|
+
const touch = () => { clearTimeout(idle); idle = setTimeout(abort, idleMs); };
|
|
77
|
+
let progressed = false;
|
|
78
|
+
try {
|
|
79
|
+
touch();
|
|
80
|
+
let response;
|
|
81
|
+
try {
|
|
82
|
+
// Exactly text/event-stream: an Accept that also allows JSON gets the paged read instead of a stream.
|
|
83
|
+
response = await client.raw(operationId, { run_id }, { signal: connection.signal, headers: { accept: 'text/event-stream', 'last-event-id': String(last) } });
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (signal?.aborted)
|
|
87
|
+
return;
|
|
88
|
+
// An HTTP answer (404, 403, 429 streams_busy...) is raised, not retried in a loop. A dropped or quiet connection is retried.
|
|
89
|
+
if (error instanceof GenesisError && error.status !== 0 && ![502, 503, 504].includes(error.status))
|
|
90
|
+
throw error;
|
|
91
|
+
await wait(1000, signal);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const reader = response.body?.getReader();
|
|
95
|
+
if (!reader) {
|
|
96
|
+
await wait(1000, signal);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const decoder = new TextDecoder();
|
|
100
|
+
let buffer = '';
|
|
101
|
+
let lines = [];
|
|
102
|
+
let size = 0;
|
|
103
|
+
try {
|
|
104
|
+
for (;;) {
|
|
105
|
+
let chunk;
|
|
106
|
+
try {
|
|
107
|
+
chunk = await reader.read();
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
if (chunk.done)
|
|
113
|
+
break;
|
|
114
|
+
touch();
|
|
115
|
+
buffer += decoder.decode(chunk.value, { stream: true });
|
|
116
|
+
let index;
|
|
117
|
+
while ((index = buffer.search(/\r\n|\r|\n/)) >= 0) {
|
|
118
|
+
const line = buffer.slice(0, index);
|
|
119
|
+
buffer = buffer.slice(index + (buffer.startsWith('\r\n', index) ? 2 : 1));
|
|
120
|
+
if (line !== '') {
|
|
121
|
+
size += line.length;
|
|
122
|
+
if (size > MAX_SSE_EVENT_CHARS)
|
|
123
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Henry stream event exceeds 4 MiB.' });
|
|
124
|
+
lines.push(line);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const parsed = frame(lines);
|
|
128
|
+
lines = [];
|
|
129
|
+
size = 0;
|
|
130
|
+
if (!parsed || !STREAM_EVENTS.has(parsed.event))
|
|
131
|
+
continue;
|
|
132
|
+
let data;
|
|
133
|
+
try {
|
|
134
|
+
data = JSON.parse(parsed.data);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Henry stream sent invalid JSON.' });
|
|
138
|
+
}
|
|
139
|
+
if (parsed.event === 'status') {
|
|
140
|
+
// Each connection opens with the current status: report a change only.
|
|
141
|
+
const status = data?.status;
|
|
142
|
+
if (status === lastStatus)
|
|
143
|
+
continue;
|
|
144
|
+
lastStatus = status;
|
|
145
|
+
}
|
|
146
|
+
if (parsed.event === 'activity') {
|
|
147
|
+
const seq = data?.seq;
|
|
148
|
+
if (typeof seq !== 'number' || !Number.isSafeInteger(seq))
|
|
149
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Henry stream activity has no seq.' });
|
|
150
|
+
if (seq <= last)
|
|
151
|
+
continue; // already yielded before a reconnect
|
|
152
|
+
last = seq;
|
|
153
|
+
progressed = true;
|
|
154
|
+
}
|
|
155
|
+
yield { event: parsed.event, data };
|
|
156
|
+
if (parsed.event === 'end')
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (buffer.length > MAX_SSE_EVENT_CHARS)
|
|
160
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Henry stream event exceeds 4 MiB.' });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
void reader.cancel().catch(() => { });
|
|
165
|
+
}
|
|
166
|
+
if (signal?.aborted)
|
|
167
|
+
return;
|
|
168
|
+
// A stream that closed with nothing new waits before reconnecting, so a failing upstream is never hammered.
|
|
169
|
+
if (!progressed)
|
|
170
|
+
await wait(1000, signal);
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
clearTimeout(idle);
|
|
174
|
+
signal?.removeEventListener('abort', abort);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (!signal?.aborted)
|
|
178
|
+
throw new GenesisError({ status: 0, type: 'stream_incomplete', operationId,
|
|
179
|
+
message: `Henry run ${run_id} did not end within ${maxReconnects} reconnects; resume with afterSeq ${last}.` });
|
|
180
|
+
}
|
|
2
181
|
function receiptVerificationKey(value) {
|
|
3
182
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
4
183
|
throw new Error('Henry returned an invalid receipt verification key.');
|
|
@@ -34,12 +213,56 @@ export function henryModule(client) {
|
|
|
34
213
|
}
|
|
35
214
|
},
|
|
36
215
|
},
|
|
216
|
+
blinds: {
|
|
217
|
+
/** HTTPS request through the organization's bound Blinds machine. */
|
|
218
|
+
proxy: (body) => client.request('henry_proxyBlinds', { body }),
|
|
219
|
+
},
|
|
37
220
|
runs: {
|
|
38
221
|
list: (page_token) => client.request('henry_listRuns', page_token ? { page_token } : {}),
|
|
39
222
|
create: (body) => client.request('henry_createRun', { body: { ...body, run_id: body.run_id ?? globalThis.crypto.randomUUID() } }),
|
|
40
223
|
get: (run_id) => client.request('henry_getRun', { run_id }),
|
|
224
|
+
/** Page activity events: pass the previous page's next_after_seq as after_seq. */
|
|
225
|
+
events: (run_id, query = {}) => client.request('henry_listRunEvents', { run_id, ...query }),
|
|
226
|
+
/** Delete a finished run; active runs must be cancelled first (409). */
|
|
227
|
+
delete: (run_id) => client.request('henry_deleteRun', { run_id }),
|
|
41
228
|
command: (run_id, action, text) => client.request('henry_commandRun', { run_id, body: { action, ...(text === undefined ? {} : { text }) } }),
|
|
42
229
|
approval: (run_id, approval_id, decision) => client.request('henry_decideApproval', { run_id, approval_id, body: { decision } }),
|
|
230
|
+
/**
|
|
231
|
+
* Send a message into a run: `steer` lands before the next model call, `queue` (default) after the current turn,
|
|
232
|
+
* `interrupt` cancels the turn in flight. A completed run takes it at once and runs the next turn in this request.
|
|
233
|
+
*/
|
|
234
|
+
message: (run_id, body) => {
|
|
235
|
+
if (typeof body?.text !== 'string' || !body.text.trim() || body.text.length > 16000)
|
|
236
|
+
throw invalid('henry_sendRunMessage', 'Message text must contain 1-16000 characters.');
|
|
237
|
+
return client.request('henry_sendRunMessage', { run_id, body: { text: body.text, ...(body.delivery === undefined ? {} : { delivery: body.delivery }) } });
|
|
238
|
+
},
|
|
239
|
+
/**
|
|
240
|
+
* Start a new run from turn N of this one (0 = the original task; default the latest, which needs text). run_id is
|
|
241
|
+
* the new run's idempotency key: one is generated unless supplied, and a retry with it returns the same run.
|
|
242
|
+
*/
|
|
243
|
+
fork: (run_id, body = {}) => {
|
|
244
|
+
if (body.turn !== undefined && (!Number.isSafeInteger(body.turn) || body.turn < 0))
|
|
245
|
+
throw invalid('henry_forkRun', 'turn must be a non-negative integer.');
|
|
246
|
+
return client.request('henry_forkRun', { run_id, body: { ...body, run_id: body.run_id ?? globalThis.crypto.randomUUID() } });
|
|
247
|
+
},
|
|
248
|
+
/** Follow a run live: an async iterator over its stream, reconnecting with Last-Event-ID until `end`. */
|
|
249
|
+
streamEvents: (run_id, options = {}) => streamRunEvents(client, run_id, options),
|
|
250
|
+
/** Read every event of a run and fold them into turns (see foldHenryTurns). */
|
|
251
|
+
replay: async (run_id, options = {}) => {
|
|
252
|
+
const events = [];
|
|
253
|
+
let after = 0;
|
|
254
|
+
for (let pages = 0; pages < MAX_REPLAY_PAGES; pages++) {
|
|
255
|
+
const page = await client.request('henry_listRunEvents', { run_id, after_seq: after, limit: REPLAY_PAGE }, { signal: options.signal });
|
|
256
|
+
events.push(...page.events);
|
|
257
|
+
if (!page.has_more)
|
|
258
|
+
return { run_id: page.run_id, ...(page.status === undefined ? {} : { status: page.status }), ...foldHenryTurns(events) };
|
|
259
|
+
if (!Number.isSafeInteger(page.next_after_seq) || page.next_after_seq <= after) {
|
|
260
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId: 'henry_listRunEvents', message: 'Henry events did not advance.' });
|
|
261
|
+
}
|
|
262
|
+
after = page.next_after_seq;
|
|
263
|
+
}
|
|
264
|
+
throw new GenesisError({ status: 0, type: 'invalid_response', operationId: 'henry_listRunEvents', message: 'Henry run has too many events to replay.' });
|
|
265
|
+
},
|
|
43
266
|
},
|
|
44
267
|
};
|
|
45
268
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Environment metadata never carries stored values. Reveals are explicit. */
|
|
2
2
|
export declare function validateHivesEnvironment(input: unknown): Record<string, string>;
|
|
3
|
+
/** Names for DELETE /env: 1–100 distinct valid environment names. */
|
|
4
|
+
export declare function validateHivesEnvironmentNames(input: unknown): string[];
|
|
3
5
|
export declare function hivesEnvironmentMetadata(value: unknown, project: unknown, updated: boolean): {
|
|
4
6
|
project_id: string;
|
|
5
7
|
updated: {
|
|
@@ -26,3 +28,10 @@ export declare function hivesEnvironmentReveal(value: unknown, key: unknown): {
|
|
|
26
28
|
name: string;
|
|
27
29
|
value: string;
|
|
28
30
|
};
|
|
31
|
+
/** PUT /env answers the whole decrypted map. Keep only the submitted names it confirms; never a value. */
|
|
32
|
+
export declare function hivesEnvironmentSetNames(value: unknown, project: unknown, submitted: unknown): {
|
|
33
|
+
project_id: string;
|
|
34
|
+
updated: {
|
|
35
|
+
name: string;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
@@ -17,6 +17,18 @@ export function validateHivesEnvironment(input) {
|
|
|
17
17
|
throw new Error('Environment input exceeds 2 MiB. Split it into smaller batches.');
|
|
18
18
|
return result;
|
|
19
19
|
}
|
|
20
|
+
/** Names for DELETE /env: 1–100 distinct valid environment names. */
|
|
21
|
+
export function validateHivesEnvironmentNames(input) {
|
|
22
|
+
if (!Array.isArray(input) || input.length < 1 || input.length > 100)
|
|
23
|
+
throw new Error('Environment deletion requires a JSON array of 1–100 names.');
|
|
24
|
+
for (const name of input) {
|
|
25
|
+
if (typeof name !== 'string' || name.length > 255 || !/^[A-Za-z][A-Za-z0-9_]*$/.test(name))
|
|
26
|
+
throw new Error('Environment names must start with a letter and contain only letters, numbers and underscores (up to 255 characters).');
|
|
27
|
+
}
|
|
28
|
+
if (new Set(input).size !== input.length)
|
|
29
|
+
throw new Error('Environment deletion names must be distinct.');
|
|
30
|
+
return [...input];
|
|
31
|
+
}
|
|
20
32
|
function record(value) {
|
|
21
33
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
22
34
|
throw new Error('Hives returned invalid environment metadata.');
|
|
@@ -56,3 +68,13 @@ export function hivesEnvironmentReveal(value, key) {
|
|
|
56
68
|
throw new Error('Hives returned invalid environment reveal data.');
|
|
57
69
|
return { name: data.name, value: data.value };
|
|
58
70
|
}
|
|
71
|
+
/** PUT /env answers the whole decrypted map. Keep only the submitted names it confirms; never a value. */
|
|
72
|
+
export function hivesEnvironmentSetNames(value, project, submitted) {
|
|
73
|
+
const data = record(value);
|
|
74
|
+
if (typeof project !== 'string' || !submitted || typeof submitted !== 'object')
|
|
75
|
+
throw new Error('Hives returned invalid environment data.');
|
|
76
|
+
const updated = Object.keys(submitted);
|
|
77
|
+
if (updated.some((name) => typeof data[name] !== 'string'))
|
|
78
|
+
throw new Error('Hives did not confirm every submitted environment name. Read the names before retrying.');
|
|
79
|
+
return { project_id: project, updated: updated.map((name) => ({ name })) };
|
|
80
|
+
}
|
package/dist/hives-node.js
CHANGED
|
@@ -16,6 +16,9 @@ export function hivesOperationBudget(id) {
|
|
|
16
16
|
return 100_000;
|
|
17
17
|
if (id === 'hives_cancel_deployment' || id === 'hives_delete_deployment' || id === 'hives_stop_deployment')
|
|
18
18
|
return 330_000;
|
|
19
|
+
// A live SSE subscription is bounded by its caller's window or --follow limit (at most 300 s).
|
|
20
|
+
if (id === 'hives_stream_logs')
|
|
21
|
+
return 330_000;
|
|
19
22
|
if (['hives_create_deployment', 'hives_upload_deployment', 'hives_invoke_deployment', 'hives_invoke_function', 'hives_invoke_function_async'].includes(id))
|
|
20
23
|
return 40_000;
|
|
21
24
|
if (['hives_create_function', 'hives_update_function', 'hives_delete_function'].includes(id))
|