@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.
Files changed (69) hide show
  1. package/README.md +1 -1
  2. package/dist/auto.d.ts +1010 -0
  3. package/dist/auto.js +125 -0
  4. package/dist/beacons.d.ts +42 -0
  5. package/dist/beacons.js +71 -0
  6. package/dist/blinds-owner-enrollment.d.ts +109 -0
  7. package/dist/blinds-owner-enrollment.js +21 -2
  8. package/dist/client.d.ts +3 -19947
  9. package/dist/client.js +124 -17
  10. package/dist/errors.js +1 -1
  11. package/dist/flares.d.ts +32 -0
  12. package/dist/flares.js +5 -0
  13. package/dist/fleets.d.ts +32 -0
  14. package/dist/fleets.js +59 -6
  15. package/dist/flocks.d.ts +285 -1
  16. package/dist/flocks.js +59 -0
  17. package/dist/foundry.d.ts +424 -0
  18. package/dist/foundry.js +133 -0
  19. package/dist/garden.d.ts +754 -0
  20. package/dist/garden.js +76 -0
  21. package/dist/gateway.d.ts +365 -0
  22. package/dist/gateway.js +82 -0
  23. package/dist/gears.d.ts +121 -2
  24. package/dist/gears.js +24 -2
  25. package/dist/generated/operations.d.ts +3 -19948
  26. package/dist/generated/operations.js +54590 -13302
  27. package/dist/generated/schema.d.ts +69793 -17548
  28. package/dist/genesis.d.ts +7908 -1260
  29. package/dist/genesis.js +90 -25
  30. package/dist/genesis.openapi.json +109121 -29920
  31. package/dist/henry.d.ts +202 -1
  32. package/dist/henry.js +223 -0
  33. package/dist/hives-environment.d.ts +9 -0
  34. package/dist/hives-environment.js +22 -0
  35. package/dist/hives-node.js +3 -0
  36. package/dist/hives-operations.d.ts +114 -0
  37. package/dist/hives-operations.js +2 -0
  38. package/dist/hives-platform.d.ts +149 -0
  39. package/dist/hives-platform.js +183 -0
  40. package/dist/hives-sse.d.ts +24 -0
  41. package/dist/hives-sse.js +111 -0
  42. package/dist/iam-directory.d.ts +8 -0
  43. package/dist/iam-directory.js +23 -0
  44. package/dist/iam.d.ts +391 -0
  45. package/dist/iam.js +92 -0
  46. package/dist/idempotency.d.ts +3 -0
  47. package/dist/idempotency.js +22 -0
  48. package/dist/index.d.ts +18 -4
  49. package/dist/index.js +16 -4
  50. package/dist/instances.d.ts +177 -2
  51. package/dist/instances.js +37 -5
  52. package/dist/minds-agent.d.ts +28 -0
  53. package/dist/minds-agent.js +53 -10
  54. package/dist/minds-engine.d.ts +394 -0
  55. package/dist/minds-engine.js +555 -0
  56. package/dist/minds.d.ts +109 -1
  57. package/dist/minds.js +49 -14
  58. package/dist/orgs.d.ts +405 -0
  59. package/dist/orgs.js +63 -0
  60. package/dist/runner.d.ts +4 -0
  61. package/dist/runner.js +1 -0
  62. package/dist/scopes.d.ts +310 -0
  63. package/dist/scopes.js +40 -0
  64. package/dist/taps.d.ts +118 -0
  65. package/dist/taps.js +45 -0
  66. package/dist/traces.d.ts +574 -0
  67. package/dist/traces.js +97 -0
  68. package/dist/types.d.ts +12 -1
  69. package/package.json +1 -1
package/dist/client.js CHANGED
@@ -9,12 +9,18 @@ import { isHivesObjectUpload, hivesObjectBody, hivesContentType, isHivesResource
9
9
  import { operationTable, services } from './generated/operations.js';
10
10
  import { GenesisError } from './errors.js';
11
11
  import { requireSecureUrl } from './urls.js';
12
+ import { iamDirectoryFailure, isIamDirectoryOperation } from './iam-directory.js';
12
13
  import { validateHivesInvocation, hivesFunctionResponseHeaders, hivesFunctionMetadata, hivesOperationError } from './hives-functions.js';
13
- import { validateHivesEnvironment, hivesEnvironmentMetadata, hivesEnvironmentReveal } from './hives-environment.js';
14
+ import { validateHivesEnvironment, validateHivesEnvironmentNames, hivesEnvironmentSetNames, hivesEnvironmentMetadata, hivesEnvironmentReveal } from './hives-environment.js';
14
15
  import { RUNNER_SELF_GRANT_OPERATION, snapshotRunnerSelfGrant, runnerGrantResponse, runnerGrantTransportError } from './runner-grant.js';
15
16
  /** The Genesis API gateway. `https://console.life.one/api` remains an alias for the same gateway. */
16
17
  export const DEFAULT_BASE_URL = 'https://os.one.ml/api';
17
18
  const env = globalThis.process?.env ?? {};
19
+ /** Endless Server-Sent Events streams and the bounded reader for each; request() refuses these and any operation generated with `stream: true`. */
20
+ const ENDLESS_STREAMS = {
21
+ hives_stream_logs: 'genesis.hives.logs.stream() or genesis.hives.logs.window()',
22
+ hives_stream_deployment_logs: 'hivesSseWindow(await genesis.raw(...))',
23
+ };
18
24
  export class GenesisClient {
19
25
  baseUrl;
20
26
  routing;
@@ -45,11 +51,19 @@ export class GenesisClient {
45
51
  const v = input[k];
46
52
  if (v === undefined || v === null || v === '')
47
53
  throw new GenesisError({ status: 0, type: 'invalid_request', message: `Missing required path parameter "${k}" for ${operationId}`, operationId });
48
- return encodeURIComponent(String(v));
54
+ if (!op.tailParams?.includes(k))
55
+ return encodeURIComponent(String(v));
56
+ const segments = String(v).split('/');
57
+ if (segments.some(s => s === '' || s === '.' || s === '..' || s.includes('\\')))
58
+ throw new GenesisError({ status: 0, type: 'invalid_request', operationId,
59
+ message: `Path parameter "${k}" for ${operationId} must be "/"-separated segments with no empty, "." or ".." segment and no backslash` });
60
+ return segments.map(encodeURIComponent).join('/');
49
61
  });
50
62
  let base;
51
63
  let p;
52
64
  if (this.routing === 'direct') {
65
+ if (op.service === 'gateway')
66
+ throw new GenesisError({ status: 0, type: 'invalid_request', operationId, message: 'Gateway operations are answered by the Genesis gateway itself and have no direct upstream.' });
53
67
  base = (this.opts.upstreams?.[op.service] ?? op.upstream.base).replace(/\/+$/, '');
54
68
  p = fill(op.upstream.path);
55
69
  }
@@ -72,9 +86,21 @@ export class GenesisClient {
72
86
  }
73
87
  /** Call any Genesis operation by id, fully typed from the merged spec. */
74
88
  async request(operationId, input, init = {}) {
89
+ // An endless SSE body never completes, so buffering it would hang: refuse before any request is sent.
90
+ // Finite SSE reads (for example traces_streamRun) are not marked and still use request().
91
+ const endless = ENDLESS_STREAMS[operationId]
92
+ ?? (operationTable[operationId]?.stream === true ? 'hivesSseWindow(await genesis.raw(...))' : undefined);
93
+ if (endless)
94
+ throw new GenesisError({ status: 0, type: 'invalid_request', operationId,
95
+ message: `${String(operationId)} is an endless Server-Sent Events stream. Read a bounded window with ${endless} instead of request().` });
75
96
  const res = await this.raw(operationId, (input ?? {}), init);
76
97
  if (operationTable[operationId].rawResponse)
77
98
  return res;
99
+ // DELETE /env answers 200 with an empty body.
100
+ if (operationId === 'hives_delete_project_env') {
101
+ void res.body?.cancel().catch(() => { });
102
+ return undefined;
103
+ }
78
104
  if (operationId === 'hives_stop_deployment' && res.status !== 200) {
79
105
  void res.body?.cancel().catch(() => { });
80
106
  throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Hives did not confirm this deployment stopped. Read its current state before explicitly reconciling stop.' });
@@ -87,7 +113,7 @@ export class GenesisClient {
87
113
  value = ct.includes('json') ? await res.json() : await res.text();
88
114
  }
89
115
  catch (error) {
90
- if (String(operationId).startsWith('hives_') && String(operationId).endsWith('_project_environment'))
116
+ if (String(operationId).startsWith('hives_') && /_project_env(?:ironment)?$/.test(operationId))
91
117
  throw new Error('Hives returned invalid environment data.');
92
118
  if (String(operationId).startsWith('hives_') && /_functions?$/.test(operationId))
93
119
  throw new Error('Hives returned invalid function metadata.');
@@ -111,6 +137,9 @@ export class GenesisClient {
111
137
  if (operationId === 'hives_list_project_environment' || operationId === 'hives_update_project_environment') {
112
138
  return hivesEnvironmentMetadata(value, input?.project_id, operationId === 'hives_update_project_environment');
113
139
  }
140
+ if (operationId === 'hives_set_project_env') {
141
+ return hivesEnvironmentSetNames(value, input?.project_id, input?.body);
142
+ }
114
143
  if (operationId === 'hives_reveal_project_environment') {
115
144
  return hivesEnvironmentReveal(value, input?.key);
116
145
  }
@@ -118,6 +147,17 @@ export class GenesisClient {
118
147
  }
119
148
  /** Raw Response. Completed Hives guest responses retain their actual status, including non-2xx. */
120
149
  async raw(operationId, input = {}, init = {}) {
150
+ if (!isIamDirectoryOperation(operationId))
151
+ return this.dispatch(operationId, input, init);
152
+ // The gateway binds the organization context itself. A refusal is only explained here; the client never changes a session or its context.
153
+ try {
154
+ return await this.dispatch(operationId, input, init);
155
+ }
156
+ catch (e) {
157
+ throw iamDirectoryFailure(e, typeof input.org_id === 'string' ? input.org_id : '', operationId);
158
+ }
159
+ }
160
+ async dispatch(operationId, input, init) {
121
161
  if (isMindsOwnerOperation(operationId))
122
162
  return mindsOwnerTransport({ operationId, input, headers: init.headers, routing: this.routing, url: this.url(operationId, input), credential: () => this.credential(), fetch: this.fetchImpl, signal: init.signal });
123
163
  if (isMindsAgentOperation(operationId))
@@ -139,13 +179,15 @@ export class GenesisClient {
139
179
  const flocksNative = ['flocks_exchangeJoinToken', 'flocks_activateNode', 'flocks_rotateNodeCredential'].includes(operationId);
140
180
  if (flocksNative && this.routing === 'direct')
141
181
  throw new GenesisError({ status: 0, type: 'invalid_request', message: 'Native Flocks credentials are brokered only through the Genesis gateway.', operationId });
142
- if (operationId === 'hives_update_project_environment')
182
+ if (operationId === 'hives_update_project_environment' || operationId === 'hives_set_project_env')
143
183
  input = { ...input, body: validateHivesEnvironment(input.body) };
184
+ if (operationId === 'hives_delete_project_env')
185
+ input = { ...input, body: validateHivesEnvironmentNames(input.body) };
144
186
  const objectBody = isHivesObjectUpload(operationId) ? await hivesObjectBody(input.body) : undefined;
145
187
  const url = this.url(operationId, input);
146
188
  const headers = {
147
189
  accept: 'application/json',
148
- 'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.1.13',
190
+ 'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.2.0',
149
191
  ...this.opts.headers, ...init.headers,
150
192
  };
151
193
  if (op.service === 'blinds' && !['GET', 'HEAD', 'OPTIONS'].includes(op.method))
@@ -203,7 +245,7 @@ export class GenesisClient {
203
245
  body = JSON.stringify(input.body);
204
246
  }
205
247
  }
206
- const environmentOperation = op.service === 'hives' && operationId.endsWith('_project_environment');
248
+ const environmentOperation = op.service === 'hives' && /_project_env(?:ironment)?$/.test(operationId);
207
249
  const secretOperation = op.service === 'hives' && (/_secrets?$/.test(operationId) || environmentOperation);
208
250
  const functionOperation = op.service === 'hives' && (op.rawResponse || /_function(?:s|_async)?$/.test(operationId));
209
251
  let res;
@@ -224,13 +266,28 @@ export class GenesisClient {
224
266
  }
225
267
  if (runnerGrant)
226
268
  return runnerGrantResponse(res, runnerGrant);
269
+ // A raw (binary) answer outside Hives, e.g. a Panes screenshot: a success is the bytes; a
270
+ // failure takes the ordinary error path below.
271
+ if (op.rawResponse && op.service !== 'hives' && res.ok)
272
+ return res;
227
273
  if (op.rawResponse && /^exec_[a-f0-9]{32}$/.test(res.headers.get('x-execution-id') ?? '')) {
228
274
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers: hivesFunctionResponseHeaders(res.headers) });
229
275
  }
230
276
  if (op.rawResponse && op.service === 'hives' && res.headers.has('x-hives-operation-id')) {
277
+ // 5xx stays invalid_response below: its outcome is uncertain, so keep the key and reconcile.
278
+ const copy = res.status >= 400 && res.status < 500 ? res.clone() : undefined;
231
279
  const error = await hivesOperationError(res);
232
- if (error)
280
+ if (error) {
281
+ void copy?.body?.cancel().catch(() => { });
233
282
  throw error;
283
+ }
284
+ // A refusal envelope that is not an operation receipt keeps its own status, code and request id (finding G2).
285
+ const detail = copy ? await hivesErrorDetail(copy) : {};
286
+ if (detail.code !== undefined) {
287
+ throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, code: detail.code,
288
+ message: detail.message ?? `Hives refused this invocation (${res.status}). Keep the original idempotency key and read its operation status before retrying.`,
289
+ requestId: res.headers.get('x-request-id') ?? detail.requestId });
290
+ }
234
291
  throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
235
292
  message: 'Hives operation status was not confirmed. Keep the original idempotency key and reconcile its operation.' });
236
293
  }
@@ -238,16 +295,21 @@ export class GenesisClient {
238
295
  throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
239
296
  message: 'Hives did not confirm a completed guest execution. Keep the original invocation key and reconcile its operation.' });
240
297
  if (functionOperation && !res.ok) {
241
- const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
242
- : res.status === 403 ? 'Missing the authorized Hives scope for this function action.'
243
- : res.status === 404 ? 'Function/deployment, project or artifact not found in the authenticated organization and project.'
244
- : res.status === 409 ? 'Function mutation conflicted. Refresh its metadata before retrying.'
245
- : res.status === 400 || res.status === 422 ? 'Invalid function request. Check artifact, resource bounds and guest request syntax.'
246
- : res.status === 413 ? 'Function request exceeds a body or serialized guest envelope limit.'
247
- : res.status >= 300 && res.status < 400 ? 'Unexpected function service redirect. No redirect was followed.'
248
- : `Hives function operation failed (${res.status}). Refresh and retry.`;
249
- throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, message,
250
- requestId: res.headers.get('x-request-id') ?? undefined });
298
+ // Keep the service's own refusal code and text (findings G3, G4); the fixed texts are fallbacks only.
299
+ const detail = await hivesErrorDetail(res);
300
+ const invocation = op.rawResponse === true;
301
+ const scope = detail.code === undefined || /scope/i.test(detail.code);
302
+ const message = detail.code !== undefined && detail.message !== undefined && !(res.status === 403 && scope) ? detail.message
303
+ : res.status === 401 ? 'Genesis credential rejected. Sign in again.'
304
+ : res.status === 403 ? (scope ? 'Missing the authorized Hives scope for this function action.' : `Hives refused this ${invocation ? 'invocation' : 'function action'} (${detail.code}).`)
305
+ : res.status === 404 ? 'Function/deployment, project or artifact not found in the authenticated organization and project.'
306
+ : res.status === 409 ? (invocation ? `Hives refused this invocation as a conflict${detail.code ? ` (${detail.code})` : ''}. Read the target's current state before retrying.` : 'Function mutation conflicted. Refresh its metadata before retrying.')
307
+ : res.status === 400 || res.status === 422 ? 'Invalid function request. Check artifact, resource bounds and guest request syntax.'
308
+ : res.status === 413 ? 'Function request exceeds a body or serialized guest envelope limit.'
309
+ : res.status >= 300 && res.status < 400 ? 'Unexpected function service redirect. No redirect was followed.'
310
+ : `Hives function operation failed (${res.status}). Refresh and retry.`;
311
+ throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, message, code: detail.code,
312
+ requestId: res.headers.get('x-request-id') ?? detail.requestId });
251
313
  }
252
314
  if (!res.ok && secretOperation) {
253
315
  const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
@@ -275,3 +337,48 @@ export class GenesisClient {
275
337
  return res;
276
338
  }
277
339
  }
340
+ /**
341
+ * The refusal code, short message and request id of a Hives function/invocation error body. Reads at most
342
+ * 8 KiB and keeps nothing else: a body without a platform code may reflect guest request data.
343
+ */
344
+ async function hivesErrorDetail(res) {
345
+ if (!res.body)
346
+ return {};
347
+ const reader = res.body.getReader();
348
+ const chunks = [];
349
+ let size = 0;
350
+ try {
351
+ for (;;) {
352
+ const { value, done } = await reader.read();
353
+ if (done)
354
+ break;
355
+ size += value.length;
356
+ if (size > 8192) {
357
+ await reader.cancel().catch(() => { });
358
+ return {};
359
+ }
360
+ chunks.push(value);
361
+ }
362
+ const bytes = new Uint8Array(size);
363
+ let offset = 0;
364
+ for (const chunk of chunks) {
365
+ bytes.set(chunk, offset);
366
+ offset += chunk.length;
367
+ }
368
+ const body = JSON.parse(new TextDecoder().decode(bytes));
369
+ const e = body && typeof body === 'object' ? body.error : undefined;
370
+ // Only an error envelope: a top-level `code` belongs to an operation receipt, never to a refusal.
371
+ const raw = e && typeof e === 'object' ? e.code : typeof e === 'string' ? e : undefined;
372
+ const code = typeof raw === 'string' && /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/.test(raw) ? raw : undefined;
373
+ const text = e && typeof e === 'object' ? e.message : undefined;
374
+ const message = code && typeof text === 'string' && text.length <= 300 && !/[\u0000-\u001f\u007f]/.test(text) ? text : undefined;
375
+ const id = (e && typeof e === 'object' ? e.request_id : undefined) ?? body?.request_id;
376
+ return { code, message, requestId: typeof id === 'string' && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : undefined };
377
+ }
378
+ catch {
379
+ return {};
380
+ }
381
+ finally {
382
+ reader.releaseLock();
383
+ }
384
+ }
package/dist/errors.js CHANGED
@@ -29,7 +29,7 @@ export class GenesisError extends Error {
29
29
  (typeof e === 'string' && e) || (typeof body === 'string' && body) || `${res.status} ${res.statusText}`;
30
30
  return new GenesisError({
31
31
  status: res.status, type, message, code: typeof e === 'object' ? e?.code : undefined,
32
- requestId: res.headers.get('x-request-id') ?? (typeof e === 'object' ? e?.request_id : undefined) ?? body?.request_id,
32
+ requestId: [typeof e === 'object' ? e?.request_id : undefined, body?.request_id, res.headers.get('x-request-id')].find((v) => typeof v === 'string' && v !== ''),
33
33
  operationId, body,
34
34
  });
35
35
  }
package/dist/flares.d.ts CHANGED
@@ -92,6 +92,15 @@ export declare function flaresModule(client: GenesisClient): {
92
92
  }>;
93
93
  };
94
94
  presence: {
95
+ list: () => Promise<({
96
+ did?: string;
97
+ status?: string;
98
+ sessionId?: string;
99
+ sessionCount?: number;
100
+ lastSeenAt?: string;
101
+ } & {
102
+ [key: string]: unknown;
103
+ })[]>;
95
104
  heartbeat: (body: In<"flares_presenceHeartbeat">["body"], key: string) => Promise<{
96
105
  did?: string;
97
106
  status?: string;
@@ -112,6 +121,18 @@ export declare function flaresModule(client: GenesisClient): {
112
121
  }>;
113
122
  };
114
123
  trust: {
124
+ list: () => Promise<({
125
+ did?: string;
126
+ score?: number;
127
+ confidence?: number;
128
+ dataPoints?: number;
129
+ computedAt?: string;
130
+ expiresAt?: string;
131
+ trustLevel?: string;
132
+ endorsementCount?: number;
133
+ } & {
134
+ [key: string]: unknown;
135
+ })[]>;
115
136
  score: (did: string) => Promise<{
116
137
  did?: string;
117
138
  score?: number;
@@ -137,6 +158,17 @@ export declare function flaresModule(client: GenesisClient): {
137
158
  [key: string]: unknown;
138
159
  }>;
139
160
  };
161
+ activity: {
162
+ list: (q?: In<"flares_listActivity">) => Promise<({
163
+ id?: string;
164
+ at?: string;
165
+ action?: string;
166
+ resourceId?: string | null;
167
+ actor?: string;
168
+ } & {
169
+ [key: string]: unknown;
170
+ })[]>;
171
+ };
140
172
  choreography: {
141
173
  workflows: {
142
174
  list: () => Promise<{
package/dist/flares.js CHANGED
@@ -28,16 +28,21 @@ export function flaresModule(client) {
28
28
  fail: (id, body, key) => write('flares_failCommitment', { id, body }, key),
29
29
  },
30
30
  presence: {
31
+ list: () => client.request('flares_listPresence'),
31
32
  heartbeat: (body, key) => write('flares_presenceHeartbeat', { body }, key),
32
33
  get: (did) => client.request('flares_getPresence', { did }),
33
34
  },
34
35
  trust: {
36
+ list: () => client.request('flares_listTrust'),
35
37
  score: (did) => client.request('flares_getTrustScore', { did }),
36
38
  check: (body, key) => write('flares_checkTrust', { body }, key),
37
39
  endorsements: (did) => client.request('flares_listEndorsements', { did }),
38
40
  endorse: (body, key) => write('flares_createEndorsement', { body }, key),
39
41
  revoke: (id, key) => write('flares_revokeEndorsement', { id }, key),
40
42
  },
43
+ activity: {
44
+ list: (q = {}) => client.request('flares_listActivity', q),
45
+ },
41
46
  choreography: {
42
47
  workflows: {
43
48
  list: () => client.request('flares_listChoreographyWorkflows'),
package/dist/fleets.d.ts CHANGED
@@ -3,6 +3,9 @@ import type { OperationInput } from './types.js';
3
3
  type TaskInput = NonNullable<OperationInput<'fleets_submitTask'>['body']>;
4
4
  type AgentLaunchInput = NonNullable<OperationInput<'fleets_launchAgent'>['body']>;
5
5
  type AgentCommandInput = NonNullable<OperationInput<'fleets_commandAgent'>['body']>;
6
+ type AgentUpdateInput = NonNullable<OperationInput<'fleets_updateAgent'>['body']>;
7
+ type AgentLogsQuery = Omit<OperationInput<'fleets_getAgentLogs'>, 'agent_id'>;
8
+ type TaskLogsQuery = Omit<OperationInput<'fleets_getTaskLogs'>, 'task_id'>;
6
9
  export interface FleetsInstanceOptions {
7
10
  idempotencyKey?: string;
8
11
  signal?: AbortSignal;
@@ -62,6 +65,27 @@ export declare function fleetsModule(client: GenesisClient): {
62
65
  [key: string]: unknown;
63
66
  };
64
67
  }>;
68
+ /** Change name, model, instructions or memory. A memory change needs a stopped agent. */
69
+ update: (agent_id: string, body: AgentUpdateInput) => Promise<{
70
+ agent: import("./index.js").components["schemas"]["Fleets.Agent"];
71
+ operation_id: string;
72
+ result?: {
73
+ [key: string]: unknown;
74
+ };
75
+ }>;
76
+ /** Delete a stopped (or recovery_required) idle agent and its log. */
77
+ delete: (agent_id: string) => Promise<{
78
+ id: string;
79
+ deleted: true;
80
+ operation_id: string;
81
+ }>;
82
+ /** Operation log, oldest first; page with after = next_after. */
83
+ logs: (agent_id: string, query?: AgentLogsQuery) => Promise<{
84
+ agent_id: string;
85
+ state: "launching" | "running" | "stopping" | "stopped" | "recovering" | "recovery_required";
86
+ entries: import("./index.js").components["schemas"]["Fleets.AgentLogEntry"][];
87
+ next_after: number | null;
88
+ }>;
65
89
  };
66
90
  instances: {
67
91
  list: (query?: OperationInput<"fleets_listInstances">) => Promise<{
@@ -181,6 +205,14 @@ export declare function fleetsModule(client: GenesisClient): {
181
205
  [key: string]: unknown;
182
206
  };
183
207
  }>;
208
+ /** Lifecycle and output log; poll with after = next_after until complete. */
209
+ logs: (task_id: string, query?: TaskLogsQuery) => Promise<{
210
+ task_id: string;
211
+ state: "pending" | "running" | "cancelling" | "completed" | "failed" | "cancelled" | "timed_out";
212
+ complete: boolean;
213
+ entries: import("./index.js").components["schemas"]["Fleets.TaskLogEntry"][];
214
+ next_after: number | null;
215
+ }>;
184
216
  };
185
217
  };
186
218
  export {};
package/dist/fleets.js CHANGED
@@ -46,12 +46,7 @@ function validateAgentLaunch(body) {
46
46
  if (body.model !== undefined && (!body.model || byteLength(body.model) > 128)) {
47
47
  throw invalid('fleets_launchAgent', 'Agent model must contain 1–128 UTF-8 bytes.');
48
48
  }
49
- if (body.memory !== undefined) {
50
- if (!['minds', 'files', 'custom'].includes(body.memory.provider))
51
- throw invalid('fleets_launchAgent', 'Memory provider must be minds, files, or custom.');
52
- if (body.memory.provider === 'custom' && !body.memory.endpoint)
53
- throw invalid('fleets_launchAgent', 'Custom memory requires an endpoint.');
54
- }
49
+ validateMemory('fleets_launchAgent', body.memory);
55
50
  if (body.idempotency_key !== undefined && (typeof body.idempotency_key !== 'string' || !/^[!-~]{1,128}$/.test(body.idempotency_key))) {
56
51
  throw invalid('fleets_launchAgent', 'Idempotency key must contain 1–128 visible ASCII characters.');
57
52
  }
@@ -61,6 +56,39 @@ function validateAgentCommand(body) {
61
56
  throw invalid('fleets_commandAgent', 'Agent input must contain 1–65536 UTF-8 bytes without NUL.');
62
57
  }
63
58
  }
59
+ function validateMemory(operation, memory) {
60
+ if (memory === undefined)
61
+ return;
62
+ if (!memory || !['minds', 'files', 'custom'].includes(memory.provider))
63
+ throw invalid(operation, 'Memory provider must be minds, files, or custom.');
64
+ if (memory.provider === 'custom' && !memory.endpoint)
65
+ throw invalid(operation, 'Custom memory requires an endpoint.');
66
+ }
67
+ function validateAgentUpdate(body) {
68
+ const op = 'fleets_updateAgent';
69
+ if (!body || typeof body !== 'object' || Array.isArray(body))
70
+ throw invalid(op, 'Provide at least one of name, model, instructions or memory.');
71
+ const keys = Object.keys(body);
72
+ if (!keys.length || keys.some(key => !['name', 'model', 'instructions', 'memory'].includes(key))) {
73
+ throw invalid(op, 'Provide at least one of name, model, instructions or memory.');
74
+ }
75
+ if (body.name !== undefined && (typeof body.name !== 'string' || !body.name.trim() || byteLength(body.name) > 128)) {
76
+ throw invalid(op, 'Agent name must contain 1–128 UTF-8 bytes.');
77
+ }
78
+ if (body.model !== undefined && body.model !== null && (typeof body.model !== 'string' || !body.model || byteLength(body.model) > 128)) {
79
+ throw invalid(op, 'Agent model must contain 1–128 UTF-8 bytes, or be null to clear it.');
80
+ }
81
+ if (body.instructions !== undefined && body.instructions !== null
82
+ && (typeof body.instructions !== 'string' || byteLength(body.instructions) > 65536 || body.instructions.includes('\0'))) {
83
+ throw invalid(op, 'Agent instructions must contain at most 65536 UTF-8 bytes without NUL, or be null to clear them.');
84
+ }
85
+ validateMemory(op, body.memory);
86
+ }
87
+ const cursor = (value, operation, max) => {
88
+ if (value !== undefined && (!Number.isSafeInteger(value) || value < 0 || value > max)) {
89
+ throw invalid(operation, 'after must be a non-negative integer cursor (next_after).');
90
+ }
91
+ };
64
92
  /** Managed Henry instances and tasks scoped to the credential's verified org. */
65
93
  export function fleetsModule(client) {
66
94
  return {
@@ -87,6 +115,26 @@ export function fleetsModule(client) {
87
115
  agentId(agent_id, 'fleets_recoverAgent');
88
116
  return client.request('fleets_recoverAgent', { agent_id });
89
117
  },
118
+ /** Change name, model, instructions or memory. A memory change needs a stopped agent. */
119
+ update: async (agent_id, body) => {
120
+ agentId(agent_id, 'fleets_updateAgent');
121
+ validateAgentUpdate(body);
122
+ return client.request('fleets_updateAgent', { agent_id, body });
123
+ },
124
+ /** Delete a stopped (or recovery_required) idle agent and its log. */
125
+ delete: async (agent_id) => {
126
+ agentId(agent_id, 'fleets_deleteAgent');
127
+ return client.request('fleets_deleteAgent', { agent_id });
128
+ },
129
+ /** Operation log, oldest first; page with after = next_after. */
130
+ logs: async (agent_id, query = {}) => {
131
+ agentId(agent_id, 'fleets_getAgentLogs');
132
+ cursor(query.after, 'fleets_getAgentLogs', Number.MAX_SAFE_INTEGER);
133
+ if (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) {
134
+ throw invalid('fleets_getAgentLogs', 'Limit must be 1–200.');
135
+ }
136
+ return client.request('fleets_getAgentLogs', { agent_id, ...query });
137
+ },
90
138
  },
91
139
  instances: {
92
140
  list: async (query = {}) => {
@@ -131,6 +179,11 @@ export function fleetsModule(client) {
131
179
  get: (task_id) => client.request('fleets_getTask', { task_id }),
132
180
  cancel: (task_id) => client.request('fleets_cancelTask', { task_id }),
133
181
  receipt: (task_id) => client.request('fleets_getTaskReceipt', { task_id }),
182
+ /** Lifecycle and output log; poll with after = next_after until complete. */
183
+ logs: async (task_id, query = {}) => {
184
+ cursor(query.after, 'fleets_getTaskLogs', 4294967295);
185
+ return client.request('fleets_getTaskLogs', { task_id, ...query });
186
+ },
134
187
  },
135
188
  };
136
189
  }