@onesi/genesis 0.1.13 → 0.2.1

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 (80) 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 +121 -0
  7. package/dist/blinds-owner-enrollment.js +23 -2
  8. package/dist/boxes.d.ts +1074 -0
  9. package/dist/boxes.js +338 -0
  10. package/dist/client.d.ts +3 -19947
  11. package/dist/client.js +127 -18
  12. package/dist/errors.d.ts +6 -0
  13. package/dist/errors.js +38 -3
  14. package/dist/flares.d.ts +32 -0
  15. package/dist/flares.js +5 -0
  16. package/dist/fleets.d.ts +32 -0
  17. package/dist/fleets.js +59 -6
  18. package/dist/flocks.d.ts +334 -1
  19. package/dist/flocks.js +85 -0
  20. package/dist/foundry.d.ts +429 -0
  21. package/dist/foundry.js +135 -0
  22. package/dist/garden.d.ts +754 -0
  23. package/dist/garden.js +76 -0
  24. package/dist/gateway.d.ts +365 -0
  25. package/dist/gateway.js +82 -0
  26. package/dist/gears.d.ts +121 -2
  27. package/dist/gears.js +24 -2
  28. package/dist/generated/operations.d.ts +3 -19948
  29. package/dist/generated/operations.js +64214 -13423
  30. package/dist/generated/schema.d.ts +85708 -17364
  31. package/dist/genesis.d.ts +9041 -1250
  32. package/dist/genesis.js +92 -25
  33. package/dist/genesis.openapi.json +133172 -31267
  34. package/dist/henry.d.ts +202 -1
  35. package/dist/henry.js +223 -0
  36. package/dist/hives-cells.d.ts +8 -3
  37. package/dist/hives-cells.js +17 -4
  38. package/dist/hives-environment.d.ts +9 -0
  39. package/dist/hives-environment.js +22 -0
  40. package/dist/hives-node.js +4 -1
  41. package/dist/hives-operations.d.ts +121 -3
  42. package/dist/hives-operations.js +2 -0
  43. package/dist/hives-platform.d.ts +149 -0
  44. package/dist/hives-platform.js +183 -0
  45. package/dist/hives-resources-transport.d.ts +7 -1
  46. package/dist/hives-resources-transport.js +10 -2
  47. package/dist/hives-sse.d.ts +24 -0
  48. package/dist/hives-sse.js +111 -0
  49. package/dist/hives-topics.d.ts +4 -3
  50. package/dist/hives-topics.js +9 -4
  51. package/dist/iam-directory.d.ts +8 -0
  52. package/dist/iam-directory.js +23 -0
  53. package/dist/iam.d.ts +397 -0
  54. package/dist/iam.js +92 -0
  55. package/dist/idempotency.d.ts +3 -0
  56. package/dist/idempotency.js +27 -0
  57. package/dist/index.d.ts +22 -6
  58. package/dist/index.js +19 -5
  59. package/dist/instances.d.ts +177 -2
  60. package/dist/instances.js +37 -5
  61. package/dist/minds-agent.d.ts +28 -0
  62. package/dist/minds-agent.js +53 -10
  63. package/dist/minds-engine.d.ts +394 -0
  64. package/dist/minds-engine.js +555 -0
  65. package/dist/minds.d.ts +109 -1
  66. package/dist/minds.js +49 -14
  67. package/dist/orgs.d.ts +405 -0
  68. package/dist/orgs.js +63 -0
  69. package/dist/redact.d.ts +37 -0
  70. package/dist/redact.js +330 -0
  71. package/dist/runner.d.ts +4 -0
  72. package/dist/runner.js +1 -0
  73. package/dist/scopes.d.ts +316 -0
  74. package/dist/scopes.js +40 -0
  75. package/dist/taps.d.ts +118 -0
  76. package/dist/taps.js +45 -0
  77. package/dist/traces.d.ts +574 -0
  78. package/dist/traces.js +97 -0
  79. package/dist/types.d.ts +12 -1
  80. 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' || operationId === 'hives_delete_project_env_key') {
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,17 @@ 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) };
186
+ if (operationId === 'hives_delete_project_env_key')
187
+ validateHivesEnvironmentNames([input.key]);
144
188
  const objectBody = isHivesObjectUpload(operationId) ? await hivesObjectBody(input.body) : undefined;
145
189
  const url = this.url(operationId, input);
146
190
  const headers = {
147
191
  accept: 'application/json',
148
- 'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.1.13',
192
+ 'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.2.1',
149
193
  ...this.opts.headers, ...init.headers,
150
194
  };
151
195
  if (op.service === 'blinds' && !['GET', 'HEAD', 'OPTIONS'].includes(op.method))
@@ -203,7 +247,7 @@ export class GenesisClient {
203
247
  body = JSON.stringify(input.body);
204
248
  }
205
249
  }
206
- const environmentOperation = op.service === 'hives' && operationId.endsWith('_project_environment');
250
+ const environmentOperation = op.service === 'hives' && /_project_env(?:ironment|_key)?$/.test(operationId);
207
251
  const secretOperation = op.service === 'hives' && (/_secrets?$/.test(operationId) || environmentOperation);
208
252
  const functionOperation = op.service === 'hives' && (op.rawResponse || /_function(?:s|_async)?$/.test(operationId));
209
253
  let res;
@@ -224,30 +268,50 @@ export class GenesisClient {
224
268
  }
225
269
  if (runnerGrant)
226
270
  return runnerGrantResponse(res, runnerGrant);
271
+ // A raw (binary) answer outside Hives, e.g. a Panes screenshot: a success is the bytes; a
272
+ // failure takes the ordinary error path below.
273
+ if (op.rawResponse && op.service !== 'hives' && res.ok)
274
+ return res;
227
275
  if (op.rawResponse && /^exec_[a-f0-9]{32}$/.test(res.headers.get('x-execution-id') ?? '')) {
228
276
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers: hivesFunctionResponseHeaders(res.headers) });
229
277
  }
230
278
  if (op.rawResponse && op.service === 'hives' && res.headers.has('x-hives-operation-id')) {
279
+ // 5xx stays invalid_response below: its outcome is uncertain, so keep the key and reconcile.
280
+ const copy = res.status >= 400 && res.status < 500 ? res.clone() : undefined;
231
281
  const error = await hivesOperationError(res);
232
- if (error)
282
+ if (error) {
283
+ void copy?.body?.cancel().catch(() => { });
233
284
  throw error;
285
+ }
286
+ // A refusal envelope that is not an operation receipt keeps its own status, code and request id (finding G2).
287
+ const detail = copy ? await hivesErrorDetail(copy) : {};
288
+ if (detail.code !== undefined) {
289
+ throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, code: detail.code,
290
+ message: detail.message ?? `Hives refused this invocation (${res.status}). Keep the original idempotency key and read its operation status before retrying.`,
291
+ requestId: res.headers.get('x-request-id') ?? detail.requestId });
292
+ }
234
293
  throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
235
294
  message: 'Hives operation status was not confirmed. Keep the original idempotency key and reconcile its operation.' });
236
295
  }
237
- if (op.rawResponse && res.ok)
296
+ if (op.rawResponse && op.service === 'hives' && res.ok)
238
297
  throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
239
298
  message: 'Hives did not confirm a completed guest execution. Keep the original invocation key and reconcile its operation.' });
240
299
  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 });
300
+ // Keep the service's own refusal code and text (findings G3, G4); the fixed texts are fallbacks only.
301
+ const detail = await hivesErrorDetail(res);
302
+ const invocation = op.rawResponse === true;
303
+ const scope = detail.code === undefined || /scope/i.test(detail.code);
304
+ const message = detail.code !== undefined && detail.message !== undefined && !(res.status === 403 && scope) ? detail.message
305
+ : res.status === 401 ? 'Genesis credential rejected. Sign in again.'
306
+ : res.status === 403 ? (scope ? 'Missing the authorized Hives scope for this function action.' : `Hives refused this ${invocation ? 'invocation' : 'function action'} (${detail.code}).`)
307
+ : res.status === 404 ? 'Function/deployment, project or artifact not found in the authenticated organization and project.'
308
+ : 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.')
309
+ : res.status === 400 || res.status === 422 ? 'Invalid function request. Check artifact, resource bounds and guest request syntax.'
310
+ : res.status === 413 ? 'Function request exceeds a body or serialized guest envelope limit.'
311
+ : res.status >= 300 && res.status < 400 ? 'Unexpected function service redirect. No redirect was followed.'
312
+ : `Hives function operation failed (${res.status}). Refresh and retry.`;
313
+ throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, message, code: detail.code,
314
+ requestId: res.headers.get('x-request-id') ?? detail.requestId });
251
315
  }
252
316
  if (!res.ok && secretOperation) {
253
317
  const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
@@ -275,3 +339,48 @@ export class GenesisClient {
275
339
  return res;
276
340
  }
277
341
  }
342
+ /**
343
+ * The refusal code, short message and request id of a Hives function/invocation error body. Reads at most
344
+ * 8 KiB and keeps nothing else: a body without a platform code may reflect guest request data.
345
+ */
346
+ async function hivesErrorDetail(res) {
347
+ if (!res.body)
348
+ return {};
349
+ const reader = res.body.getReader();
350
+ const chunks = [];
351
+ let size = 0;
352
+ try {
353
+ for (;;) {
354
+ const { value, done } = await reader.read();
355
+ if (done)
356
+ break;
357
+ size += value.length;
358
+ if (size > 8192) {
359
+ await reader.cancel().catch(() => { });
360
+ return {};
361
+ }
362
+ chunks.push(value);
363
+ }
364
+ const bytes = new Uint8Array(size);
365
+ let offset = 0;
366
+ for (const chunk of chunks) {
367
+ bytes.set(chunk, offset);
368
+ offset += chunk.length;
369
+ }
370
+ const body = JSON.parse(new TextDecoder().decode(bytes));
371
+ const e = body && typeof body === 'object' ? body.error : undefined;
372
+ // Only an error envelope: a top-level `code` belongs to an operation receipt, never to a refusal.
373
+ const raw = e && typeof e === 'object' ? e.code : typeof e === 'string' ? e : undefined;
374
+ const code = typeof raw === 'string' && /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/.test(raw) ? raw : undefined;
375
+ const text = e && typeof e === 'object' ? e.message : undefined;
376
+ const message = code && typeof text === 'string' && text.length <= 300 && !/[\u0000-\u001f\u007f]/.test(text) ? text : undefined;
377
+ const id = (e && typeof e === 'object' ? e.request_id : undefined) ?? body?.request_id;
378
+ return { code, message, requestId: typeof id === 'string' && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : undefined };
379
+ }
380
+ catch {
381
+ return {};
382
+ }
383
+ finally {
384
+ reader.releaseLock();
385
+ }
386
+ }
package/dist/errors.d.ts CHANGED
@@ -33,3 +33,9 @@ export declare function errorOutcome(body: unknown): string | undefined;
33
33
  /** Optional fields the gateway adds to an envelope; each must be a bounded string when present. */
34
34
  export declare const ENVELOPE_EXTRA_FIELDS: readonly string[];
35
35
  export declare const envelopeExtrasValid: (error: Record<string, unknown>) => boolean;
36
+ /**
37
+ * The service's error body as the CLI (`--json`) and MCP print it beside the envelope, redacted and bounded
38
+ * by `redactErrorBody` (see redact.ts). Undefined when the SDK kept no body: it deliberately drops bodies
39
+ * that may echo submitted secrets or guest data (Hives secret and function refusals).
40
+ */
41
+ export declare function errorBody(e: GenesisError): unknown;
package/dist/errors.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { authorizationCode, redactErrorBody } from './redact.js';
1
2
  /** Normalized error for every Genesis service (gateway `{error:{type,message}}`, IAM `{error,error_description}`, Relays `{error}`, Foundry/OpenAI shape). */
2
3
  export class GenesisError extends Error {
3
4
  status;
@@ -25,15 +26,41 @@ export class GenesisError extends Error {
25
26
  // An OAuth-style token (`{"error":"forbidden"}`) is the type; any other string is a message, and type stays a stable token.
26
27
  const type = typeof e === 'object' && e ? (e.type ?? e.code ?? 'error')
27
28
  : typeof e === 'string' && /^[a-z][a-z0-9_]{0,63}$/.test(e) ? e : statusType(res.status);
29
+ const code = backendCode(body);
28
30
  const message = (typeof e === 'object' && e?.message) || body?.error_description || body?.message ||
29
- (typeof e === 'string' && e) || (typeof body === 'string' && body) || `${res.status} ${res.statusText}`;
31
+ (typeof e === 'string' && e) || (typeof body === 'string' && body) || `${res.status} ${res.statusText}${code ? ` (${code})` : ''}`;
30
32
  return new GenesisError({
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,
33
+ status: res.status, type, message, code,
34
+ requestId: [typeof e === 'object' ? e?.request_id : undefined, body?.request_id, res.headers.get('x-request-id')].find((v) => typeof v === 'string' && v !== ''),
33
35
  operationId, body,
34
36
  });
35
37
  }
36
38
  }
39
+ const CODE_TOKEN = /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/;
40
+ /** A refusal code is one case (`release_not_proven_safe`, `HARNESS_NOT_ON_NODE`), never a mixed-case random string. */
41
+ const ONE_CASE = /^(?:[a-z][a-z0-9_.:-]*|[A-Z][A-Z0-9_.:-]*)$/;
42
+ /**
43
+ * The refusal code of an error body. The envelope's `error.code` wins, unless it is only the gateway's generic
44
+ * `UPSTREAM_<status>`; otherwise the backend's own top-level `code`, which the gateway keeps beside its envelope
45
+ * or passes through unwrapped (Hives `{"code":"release_not_proven_safe",...}` on a 409 release).
46
+ *
47
+ * Accepting a top-level `code` here is deliberate and differs from the Hives function/invocation error path
48
+ * (`hivesErrorDetail` in client.ts), which rejects it because there a top-level `code` belongs to an operation
49
+ * receipt. A top-level `code` can also be an OAuth authorization code echoed by an authorize/token answer, so
50
+ * it is not taken when the body looks like one (`redirect_uri`, `access_token`, `iss`...), beside any string
51
+ * `error`, when the value looks random, or when it is not a one-case code. `errorBody` redacts that `code` in an
52
+ * OAuth-shaped body, beside an OAuth error token (`{error: "invalid_grant", code}`) and when it looks random
53
+ * (16 or more letters and digits). Any other refused `code`, such as one beside a non-OAuth string `error`, is
54
+ * printed in the body but never reported as the error code.
55
+ */
56
+ function backendCode(body) {
57
+ if (!row(body))
58
+ return undefined;
59
+ const envelope = row(body.error) && typeof body.error.code === 'string' && body.error.code !== '' ? body.error.code : undefined;
60
+ const own = typeof body.code === 'string' && CODE_TOKEN.test(body.code) && ONE_CASE.test(body.code)
61
+ && typeof body.error !== 'string' && !authorizationCode(body) ? body.code : undefined;
62
+ return envelope !== undefined && !(own && /^UPSTREAM_\d{3}$/.test(envelope)) ? envelope : own;
63
+ }
37
64
  const statusType = (s) => s === 401 ? 'unauthorized' : s === 403 ? 'forbidden' : s === 404 ? 'not_found' : s === 429 ? 'rate_limited' : s >= 500 ? 'upstream_error' : 'error';
38
65
  /** The gateway catalog fields of an error, for JSON output: `code`, `required_scope` and `docs_url` when the gateway sent them. */
39
66
  export function errorCatalog(e) {
@@ -64,3 +91,11 @@ export function errorOutcome(body) {
64
91
  /** Optional fields the gateway adds to an envelope; each must be a bounded string when present. */
65
92
  export const ENVELOPE_EXTRA_FIELDS = ['type', 'message', 'docs_url', 'layer', 'reason', 'required_scope'];
66
93
  export const envelopeExtrasValid = (error) => ENVELOPE_EXTRA_FIELDS.every(name => error[name] === undefined || typeof error[name] === 'string' && error[name].length <= 2048);
94
+ /**
95
+ * The service's error body as the CLI (`--json`) and MCP print it beside the envelope, redacted and bounded
96
+ * by `redactErrorBody` (see redact.ts). Undefined when the SDK kept no body: it deliberately drops bodies
97
+ * that may echo submitted secrets or guest data (Hives secret and function refusals).
98
+ */
99
+ export function errorBody(e) {
100
+ return redactErrorBody(e.body);
101
+ }
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
  }