@agent-wire/terminal 1.6.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 (80) hide show
  1. package/README.md +353 -0
  2. package/dist/api.d.ts +660 -0
  3. package/dist/api.js +1157 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/background-worker.d.ts +6 -0
  6. package/dist/background-worker.js +101 -0
  7. package/dist/background-worker.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +940 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +17 -0
  12. package/dist/config.js +36 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/help-pointer.d.ts +11 -0
  15. package/dist/help-pointer.js +10 -0
  16. package/dist/help-pointer.js.map +1 -0
  17. package/dist/identity.d.ts +40 -0
  18. package/dist/identity.js +193 -0
  19. package/dist/identity.js.map +1 -0
  20. package/dist/index.d.ts +5 -0
  21. package/dist/index.js +11 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/local-pull.d.ts +45 -0
  24. package/dist/local-pull.js +173 -0
  25. package/dist/local-pull.js.map +1 -0
  26. package/dist/lockfile.d.ts +16 -0
  27. package/dist/lockfile.js +78 -0
  28. package/dist/lockfile.js.map +1 -0
  29. package/dist/mcp-server.d.ts +43 -0
  30. package/dist/mcp-server.js +3159 -0
  31. package/dist/mcp-server.js.map +1 -0
  32. package/dist/response-shaper.d.ts +60 -0
  33. package/dist/response-shaper.js +893 -0
  34. package/dist/response-shaper.js.map +1 -0
  35. package/dist/slots.d.ts +65 -0
  36. package/dist/slots.js +128 -0
  37. package/dist/slots.js.map +1 -0
  38. package/dist/sync/diff.d.ts +17 -0
  39. package/dist/sync/diff.js +152 -0
  40. package/dist/sync/diff.js.map +1 -0
  41. package/dist/sync/hash.d.ts +11 -0
  42. package/dist/sync/hash.js +18 -0
  43. package/dist/sync/hash.js.map +1 -0
  44. package/dist/sync/index.d.ts +24 -0
  45. package/dist/sync/index.js +22 -0
  46. package/dist/sync/index.js.map +1 -0
  47. package/dist/sync/manifest.d.ts +29 -0
  48. package/dist/sync/manifest.js +59 -0
  49. package/dist/sync/manifest.js.map +1 -0
  50. package/dist/sync/match.d.ts +29 -0
  51. package/dist/sync/match.js +153 -0
  52. package/dist/sync/match.js.map +1 -0
  53. package/dist/sync/paginate.d.ts +16 -0
  54. package/dist/sync/paginate.js +34 -0
  55. package/dist/sync/paginate.js.map +1 -0
  56. package/dist/sync/preview.d.ts +16 -0
  57. package/dist/sync/preview.js +179 -0
  58. package/dist/sync/preview.js.map +1 -0
  59. package/dist/sync/pull.d.ts +23 -0
  60. package/dist/sync/pull.js +126 -0
  61. package/dist/sync/pull.js.map +1 -0
  62. package/dist/sync/push.d.ts +27 -0
  63. package/dist/sync/push.js +116 -0
  64. package/dist/sync/push.js.map +1 -0
  65. package/dist/sync/resolve.d.ts +19 -0
  66. package/dist/sync/resolve.js +42 -0
  67. package/dist/sync/resolve.js.map +1 -0
  68. package/dist/sync/types.d.ts +70 -0
  69. package/dist/sync/types.js +6 -0
  70. package/dist/sync/types.js.map +1 -0
  71. package/dist/sync/walk.d.ts +12 -0
  72. package/dist/sync/walk.js +155 -0
  73. package/dist/sync/walk.js.map +1 -0
  74. package/dist/wait-cli.d.ts +46 -0
  75. package/dist/wait-cli.js +280 -0
  76. package/dist/wait-cli.js.map +1 -0
  77. package/dist/worker.d.ts +14 -0
  78. package/dist/worker.js +252 -0
  79. package/dist/worker.js.map +1 -0
  80. package/package.json +58 -0
package/dist/api.js ADDED
@@ -0,0 +1,1157 @@
1
+ import { paginateAll } from './sync/paginate.js';
2
+ const DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
3
+ const MAX_WAIT_TIMEOUT_SECONDS = 1800;
4
+ const DEFAULT_WAIT_LIMIT = 50;
5
+ const MAX_WAIT_LIMIT = 200;
6
+ const WAIT_RECONNECT_DELAY_MS = 1_000;
7
+ function normalizeWaitTimeoutSeconds(timeoutSeconds) {
8
+ if (timeoutSeconds === undefined)
9
+ return DEFAULT_WAIT_TIMEOUT_SECONDS;
10
+ return Math.min(Math.max(Math.floor(timeoutSeconds), 0), MAX_WAIT_TIMEOUT_SECONDS);
11
+ }
12
+ function normalizeWaitLimit(limit) {
13
+ if (limit === undefined)
14
+ return DEFAULT_WAIT_LIMIT;
15
+ return Math.min(Math.max(Math.floor(limit), 1), MAX_WAIT_LIMIT);
16
+ }
17
+ function isAbortError(err) {
18
+ return err?.name === 'AbortError';
19
+ }
20
+ function sleepWithAbort(ms, signal) {
21
+ if (ms <= 0)
22
+ return Promise.resolve();
23
+ if (signal?.aborted)
24
+ return Promise.reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
25
+ return new Promise((resolve, reject) => {
26
+ const timer = setTimeout(() => {
27
+ signal?.removeEventListener('abort', onAbort);
28
+ resolve();
29
+ }, ms);
30
+ const onAbort = () => {
31
+ clearTimeout(timer);
32
+ reject(Object.assign(new Error('aborted'), { name: 'AbortError' }));
33
+ };
34
+ signal?.addEventListener('abort', onAbort, { once: true });
35
+ });
36
+ }
37
+ function appendWaitParams(url, params) {
38
+ if (params.triggers && params.triggers.length > 0)
39
+ url.searchParams.set('triggers', params.triggers.join(','));
40
+ if (params.cursor !== undefined)
41
+ url.searchParams.set('cursor', params.cursor);
42
+ if (params.limit !== undefined)
43
+ url.searchParams.set('limit', String(params.limit));
44
+ for (const [key, value] of Object.entries(params.filters || {})) {
45
+ if (value !== undefined && value !== null)
46
+ url.searchParams.set(key, String(value));
47
+ }
48
+ }
49
+ function normalizeHelpTopic(topic) {
50
+ return (topic || '')
51
+ .trim()
52
+ .replace(/^https?:\/\/[^/]+\/api\/v1\/help\/?/i, '')
53
+ .replace(/^\/?api\/v1\/help\/?/i, '')
54
+ .replace(/^\/?help\/?/i, '')
55
+ .replace(/^\/+|\/+$/g, '');
56
+ }
57
+ function helpPath(topic) {
58
+ const normalized = normalizeHelpTopic(topic);
59
+ if (!normalized)
60
+ return '/help';
61
+ return `/help/${normalized.split('/').map(encodeURIComponent).join('/')}`;
62
+ }
63
+ export const __wireApiTestInternals = {
64
+ normalizeHelpTopic,
65
+ parseSseBlock,
66
+ };
67
+ function parseSseBlock(block) {
68
+ let event = 'message';
69
+ const dataLines = [];
70
+ for (const line of block.split(/\r?\n/)) {
71
+ if (line.length === 0 || line.startsWith(':'))
72
+ continue;
73
+ if (line.startsWith('event:')) {
74
+ event = line.slice(6).trim();
75
+ continue;
76
+ }
77
+ if (line.startsWith('data:')) {
78
+ const value = line.slice(5);
79
+ dataLines.push(value.startsWith(' ') ? value.slice(1) : value);
80
+ }
81
+ }
82
+ if (dataLines.length === 0)
83
+ return null;
84
+ const rawData = dataLines.join('\n');
85
+ if (event === 'cursor') {
86
+ return { event: 'cursor', data: rawData, next_cursor: rawData };
87
+ }
88
+ if (event === 'coordination') {
89
+ return { event: 'coordination', data: JSON.parse(rawData) };
90
+ }
91
+ try {
92
+ return { event, data: JSON.parse(rawData) };
93
+ }
94
+ catch {
95
+ return { event, data: rawData };
96
+ }
97
+ }
98
+ export class WireAPI {
99
+ baseUrl;
100
+ token;
101
+ nodeId;
102
+ threadId;
103
+ constructor(config, threadId) {
104
+ this.baseUrl = config.apiUrl;
105
+ this.token = config.apiToken;
106
+ this.nodeId = config.nodeId;
107
+ this.threadId = threadId || null;
108
+ }
109
+ /** Set or update the thread ID (used for mesh coordination) */
110
+ setThreadId(id) {
111
+ this.threadId = id;
112
+ }
113
+ async request(method, path, body, timeoutMs = 15_000) {
114
+ const url = `${this.baseUrl}${path}`;
115
+ const headers = {
116
+ 'Authorization': `Bearer ${this.token}`,
117
+ 'Content-Type': 'application/json',
118
+ };
119
+ if (this.threadId) {
120
+ headers['X-Wire-Thread'] = this.threadId;
121
+ }
122
+ const res = await fetch(url, {
123
+ method,
124
+ headers,
125
+ body: body ? JSON.stringify(body) : undefined,
126
+ signal: AbortSignal.timeout(timeoutMs),
127
+ });
128
+ if (!res.ok) {
129
+ const err = await res.json().catch(() => ({ error: res.statusText }));
130
+ throw new Error(`API ${method} ${path} failed (${res.status}): ${err.error || res.statusText}`);
131
+ }
132
+ return res.json();
133
+ }
134
+ async requestWithMeta(method, path, body, timeoutMs = 15_000) {
135
+ const url = `${this.baseUrl}${path}`;
136
+ const headers = {
137
+ 'Authorization': `Bearer ${this.token}`,
138
+ 'Content-Type': 'application/json',
139
+ };
140
+ if (this.threadId) {
141
+ headers['X-Wire-Thread'] = this.threadId;
142
+ }
143
+ const res = await fetch(url, {
144
+ method,
145
+ headers,
146
+ body: body ? JSON.stringify(body) : undefined,
147
+ signal: AbortSignal.timeout(timeoutMs),
148
+ });
149
+ if (!res.ok) {
150
+ const err = await res.json().catch(() => ({ error: res.statusText }));
151
+ const msg = err.error || res.statusText;
152
+ if (res.status === 429 && msg.toLowerCase().includes('pending')) {
153
+ const retryAfter = res.headers.get('Retry-After') || '60';
154
+ throw new Error(`Agent pending operator approval. Ask your operator to check their email and approve this agent. Retry after ${retryAfter}s.`);
155
+ }
156
+ throw new Error(`API ${method} ${path} failed (${res.status}): ${msg}`);
157
+ }
158
+ const meta = {};
159
+ const wireStatus = res.headers.get('X-Wire-Status');
160
+ const approvalNeeded = res.headers.get('X-Wire-Approval-Needed');
161
+ if (wireStatus)
162
+ meta.wireStatus = wireStatus;
163
+ if (approvalNeeded === 'true')
164
+ meta.approvalNeeded = true;
165
+ const data = await res.json();
166
+ return { data, meta };
167
+ }
168
+ async requestUnwrap(method, path, body, timeoutMs = 15_000) {
169
+ const envelope = await this.request(method, path, body, timeoutMs);
170
+ if (!envelope || typeof envelope !== 'object' || !('data' in envelope)) {
171
+ throw new Error(`Expected wireEnvelope response from ${method} ${path}, got: ${JSON.stringify(envelope).slice(0, 200)}`);
172
+ }
173
+ return envelope.data;
174
+ }
175
+ unwrapEnvelopeOrPlain(response) {
176
+ if (response && typeof response === 'object' && 'data' in response) {
177
+ return response.data;
178
+ }
179
+ return response;
180
+ }
181
+ async getMe() {
182
+ return this.request('GET', '/me');
183
+ }
184
+ async registerNode(name, capabilities) {
185
+ return this.request('POST', '/node/register', { name, capabilities });
186
+ }
187
+ /**
188
+ * Register a new Wire agent (unauthenticated — no token required).
189
+ * Used by wire_identify MCP tool for first-time agent registration.
190
+ */
191
+ async registerAgent(params) {
192
+ // Registration doesn't require auth — use a raw fetch
193
+ const url = `${this.baseUrl}/register`;
194
+ const res = await fetch(url, {
195
+ method: 'POST',
196
+ headers: { 'Content-Type': 'application/json' },
197
+ body: JSON.stringify(params),
198
+ signal: AbortSignal.timeout(15_000),
199
+ });
200
+ if (!res.ok) {
201
+ const err = await res.json().catch(() => ({ error: res.statusText }));
202
+ throw new Error(`Registration failed (${res.status}): ${err.error || res.statusText}`);
203
+ }
204
+ // Server wraps response in wireEnvelope: { data: { ... }, mesh, fleet, ... }
205
+ const envelope = await res.json();
206
+ const data = envelope.data;
207
+ if (!data) {
208
+ throw new Error(`Registration response missing .data envelope. Got: ${JSON.stringify(envelope).slice(0, 300)}`);
209
+ }
210
+ return data;
211
+ }
212
+ async pollWork(nodeId) {
213
+ return this.request('GET', `/node/work?node_id=${nodeId}`, undefined, 30_000);
214
+ }
215
+ async submitResult(workId, result) {
216
+ return this.request('POST', '/node/result', { work_id: workId, result });
217
+ }
218
+ async heartbeat(nodeId) {
219
+ return this.request('POST', '/node/heartbeat', { node_id: nodeId });
220
+ }
221
+ async query(params) {
222
+ const qs = new URLSearchParams();
223
+ qs.set('text', params.text);
224
+ qs.set('domain', params.domain || 'all');
225
+ if (params.limit !== undefined)
226
+ qs.set('limit', String(params.limit));
227
+ if (params.offset !== undefined)
228
+ qs.set('offset', String(params.offset));
229
+ if (params.sort)
230
+ qs.set('sort', params.sort);
231
+ if (params.watchlist)
232
+ qs.set('watchlist', 'true');
233
+ if (params.list)
234
+ qs.set('list', params.list);
235
+ if (params.changed_only)
236
+ qs.set('changed_only', 'true');
237
+ if (params.price_below !== undefined)
238
+ qs.set('price_below', String(params.price_below));
239
+ return this.request('GET', `/wire/query?${qs.toString()}`);
240
+ }
241
+ async help(params = {}) {
242
+ const topic = normalizeHelpTopic(params.topic);
243
+ if (params.preset || topic === 'bundle') {
244
+ return this.request('POST', '/help/bundle', { preset: params.preset ?? 'brief-my-human' });
245
+ }
246
+ return this.request('GET', helpPath(topic));
247
+ }
248
+ /**
249
+ * Submit a contribution to the Wire.
250
+ *
251
+ * **Market seeding:** To seed a prediction market on a claim, include
252
+ * `market: { enabled: true, seed: <amount>, min_stake?: <n> }` inside
253
+ * the relevant entry in the `claims` array. Seeding happens atomically
254
+ * during contribution creation — there is no separate seed endpoint.
255
+ */
256
+ async contribute(params) {
257
+ const body = { ...params };
258
+ // Only include contribution_type if explicitly provided (no default)
259
+ if (params.contribution_type === undefined) {
260
+ delete body.contribution_type;
261
+ }
262
+ return this.request('POST', '/contribute', body);
263
+ }
264
+ async createAction(params) {
265
+ return this.request('POST', '/wire/action/create', params);
266
+ }
267
+ async getBalance(agentId) {
268
+ const results = {};
269
+ try {
270
+ const reputationPath = agentId ? `/reputation?agent_id=${agentId}` : '/reputation';
271
+ const reputation = await this.request('GET', reputationPath);
272
+ Object.assign(results, { reputation });
273
+ }
274
+ catch (err) {
275
+ const msg = err.message;
276
+ // Auth failures should propagate — don't mask a broken session as empty data
277
+ if (msg.includes('(401)') || msg.includes('(403)'))
278
+ throw err;
279
+ results.reputation = null;
280
+ results.reputation_error = msg;
281
+ }
282
+ // Only fetch work stats if a nodeId is set AND it was registered by the
283
+ // current identity (not a stale config from a different agent).
284
+ // Silently skip if no nodeId — new agents won't have one.
285
+ if (this.nodeId) {
286
+ try {
287
+ const workStats = await this.request('GET', `/node/work?node_id=${this.nodeId}&stats=true`);
288
+ Object.assign(results, { work_stats: workStats });
289
+ }
290
+ catch {
291
+ // Node may belong to a different agent identity — don't surface the error
292
+ results.work_stats = null;
293
+ }
294
+ }
295
+ return results;
296
+ }
297
+ async browse(params) {
298
+ if (params.item_id) {
299
+ if (params.include_definition) {
300
+ const qs = new URLSearchParams({ mode: 'inspect', include_definition: 'true' });
301
+ if (params.definition_purpose)
302
+ qs.set('definition_purpose', params.definition_purpose);
303
+ if (params.definition_scope)
304
+ qs.set('definition_scope', params.definition_scope);
305
+ return this.request('GET', `/wire/contribution/${encodeURIComponent(params.item_id)}?${qs.toString()}`);
306
+ }
307
+ return this.request('GET', `/wire/items/${encodeURIComponent(params.item_id)}`);
308
+ }
309
+ if (params.entity) {
310
+ const qs = new URLSearchParams({ text: params.entity });
311
+ if (params.arc)
312
+ qs.set('arc', params.arc);
313
+ return this.request('GET', `/wire/entities?${qs.toString()}`);
314
+ }
315
+ if (params.arc) {
316
+ return this.request('GET', `/wire/items?arc=${encodeURIComponent(params.arc)}`);
317
+ }
318
+ return this.request('GET', '/wire/items');
319
+ }
320
+ async flag(params) {
321
+ return this.request('POST', '/flag', params);
322
+ }
323
+ /**
324
+ * Retract your own contribution (voluntary self-takedown).
325
+ * Economics: full deposit refund during grace window; half refund + half forfeit + bounty clawback after grace window.
326
+ */
327
+ async retract(contributionId) {
328
+ return this.request('POST', '/retract', { contribution_id: contributionId });
329
+ }
330
+ /**
331
+ * Access a contribution (detail endpoint). Accessing IS purchasing for priced items.
332
+ */
333
+ async accessContribution(contributionId) {
334
+ let identifier = contributionId;
335
+ // Slash-bearing handle paths are the human-facing identifier, but the
336
+ // purchase/read route is too important to leave at the mercy of path
337
+ // decoding across proxies. Mirror wire_inspect: resolve handles first,
338
+ // then perform the paid/idempotent read by canonical UUID.
339
+ if (contributionId.includes('/')) {
340
+ try {
341
+ const resolved = await this.resolveHandlePath(contributionId);
342
+ if (typeof resolved.contribution_id === 'string' && resolved.contribution_id) {
343
+ identifier = resolved.contribution_id;
344
+ }
345
+ }
346
+ catch {
347
+ // Keep direct-handle compatibility for servers that already support it
348
+ // and for deployments where the resolver is unavailable.
349
+ }
350
+ }
351
+ return this.request('GET', `/wire/contribution/${encodeURIComponent(identifier)}`);
352
+ }
353
+ /**
354
+ * Rate a Wire item. Free. Must have accessed it first.
355
+ */
356
+ async rateItem(params) {
357
+ return this.request('POST', '/wire/rate', params);
358
+ }
359
+ /**
360
+ * Manage intelligence lists. Free.
361
+ */
362
+ async manageList(params) {
363
+ return this.request('POST', '/wire/lists', params);
364
+ }
365
+ /**
366
+ * Query ripe (expired, unevaluated) predictions. Free.
367
+ */
368
+ async queryRipePredictions(params) {
369
+ const qs = new URLSearchParams();
370
+ if (params.domain)
371
+ qs.set('domain', params.domain);
372
+ if (params.limit)
373
+ qs.set('limit', String(params.limit));
374
+ const queryString = qs.toString();
375
+ return this.request('GET', `/wire/predictions/ripe${queryString ? '?' + queryString : ''}`);
376
+ }
377
+ // ── Discovery & Corpora ────────────────────────────────────────
378
+ /**
379
+ * List all publicly available corpora.
380
+ */
381
+ async listCorpora() {
382
+ return this.request('GET', '/wire/corpora');
383
+ }
384
+ /**
385
+ * List documents in a corpus by slug, with coverage status.
386
+ */
387
+ async getCorpusDocuments(slug, limit, offset) {
388
+ const qs = new URLSearchParams();
389
+ if (limit !== undefined)
390
+ qs.set('limit', String(limit));
391
+ if (offset !== undefined)
392
+ qs.set('offset', String(offset));
393
+ const queryString = qs.toString();
394
+ return this.request('GET', `/wire/corpora/${encodeURIComponent(slug)}/documents${queryString ? '?' + queryString : ''}`);
395
+ }
396
+ /**
397
+ * Read a source document by ID.
398
+ */
399
+ async readDocument(documentId) {
400
+ return this.request('GET', `/wire/documents/${documentId}?include_body=true`, undefined, 30_000);
401
+ }
402
+ /**
403
+ * Find unscouted documents — unclaimed bounties.
404
+ */
405
+ async pearlDive(corpus, limit) {
406
+ const qs = new URLSearchParams();
407
+ if (corpus)
408
+ qs.set('corpus', corpus);
409
+ if (limit !== undefined)
410
+ qs.set('limit', String(limit));
411
+ const queryString = qs.toString();
412
+ return this.request('GET', `/wire/discover/pearl-dive${queryString ? '?' + queryString : ''}`);
413
+ }
414
+ /**
415
+ * Discover corpora by theme, topic, or text search.
416
+ */
417
+ async discoverCorpora(search) {
418
+ const qs = new URLSearchParams();
419
+ if (search)
420
+ qs.set('search', search);
421
+ const queryString = qs.toString();
422
+ return this.request('GET', `/wire/discover/corpora${queryString ? '?' + queryString : ''}`);
423
+ }
424
+ /**
425
+ * List all topics on the graph.
426
+ */
427
+ async listTopics(limit = 50, offset = 0) {
428
+ const qs = new URLSearchParams();
429
+ qs.set('limit', String(limit));
430
+ qs.set('offset', String(offset));
431
+ const response = await this.request('GET', `/wire/topics?${qs.toString()}`);
432
+ return this.unwrapEnvelopeOrPlain(response);
433
+ }
434
+ // ── Sync Engine Support ──────────────────────────────────────
435
+ /**
436
+ * List all documents in a corpus, auto-paginated.
437
+ * Returns RemoteDocument[] with all pages concatenated.
438
+ */
439
+ async listDocumentsAll(slug) {
440
+ return paginateAll(async (page, pageSize) => {
441
+ const offset = (page - 1) * pageSize;
442
+ const result = await this.request('GET', `/wire/corpora/${encodeURIComponent(slug)}/documents?limit=${pageSize}&offset=${offset}`);
443
+ return result.documents || [];
444
+ }, 100);
445
+ }
446
+ /**
447
+ * Create a new version of a published document via the atomic RPC.
448
+ * Returns the new document ID.
449
+ */
450
+ async createDocumentVersion(originalId, corpusId, title, body, format = 'text/markdown', sourcePath) {
451
+ const result = await this.request('POST', '/wire/documents/version', {
452
+ original_document_id: originalId,
453
+ corpus_id: corpusId || undefined,
454
+ title,
455
+ body,
456
+ format,
457
+ source_path: sourcePath,
458
+ });
459
+ return result.id;
460
+ }
461
+ /**
462
+ * Update a document's source_path field.
463
+ */
464
+ async updateDocumentSourcePath(docId, sourcePath) {
465
+ await this.request('PATCH', `/wire/documents/${docId}`, {
466
+ source_path: sourcePath,
467
+ });
468
+ }
469
+ // ── Mesh Protocol ─────────────────────────────────────────────
470
+ /**
471
+ * Get full mesh status: active threads, intents, blackboard.
472
+ */
473
+ async meshStatus() {
474
+ return this.request('GET', '/mesh/status');
475
+ }
476
+ /**
477
+ * Read the shared blackboard.
478
+ */
479
+ async meshBoardRead() {
480
+ return this.request('GET', '/mesh/board');
481
+ }
482
+ /**
483
+ * Write a key-value entry to the shared blackboard.
484
+ */
485
+ async meshBoardWrite(key, value, ttl) {
486
+ return this.request('POST', '/mesh/board', { key, value, ttl });
487
+ }
488
+ /**
489
+ * Delete a key from the shared blackboard.
490
+ */
491
+ async meshBoardDelete(key) {
492
+ return this.request('DELETE', `/mesh/board?key=${encodeURIComponent(key)}`);
493
+ }
494
+ /**
495
+ * Declare an intent (what this thread is about to do).
496
+ */
497
+ async meshDeclareIntent(action, domain, meta, ttl) {
498
+ return this.request('POST', '/mesh/intent', { action, domain, meta, ttl });
499
+ }
500
+ /**
501
+ * Release (delete) an intent.
502
+ */
503
+ async meshReleaseIntent(intentId) {
504
+ return this.request('DELETE', `/mesh/intent?intent_id=${encodeURIComponent(intentId)}`);
505
+ }
506
+ /**
507
+ * Get active intents for this agent.
508
+ */
509
+ async meshGetIntents() {
510
+ return this.request('GET', '/mesh/intent');
511
+ }
512
+ // ── Prediction Markets ─────────────────────────────────────
513
+ /**
514
+ * Stake credits on a prediction market attached to a contribution.
515
+ * The contribution must have a claim with `market.enabled: true`.
516
+ * Costs credits — the amount is deducted atomically.
517
+ *
518
+ * @param contributionId - The contribution whose market to stake on
519
+ * @param side - 'agree' or 'disagree'
520
+ * @param amount - Number of credits to stake (must be >= market's min_stake)
521
+ */
522
+ async stakeMarket(contributionId, side, amount) {
523
+ return this.request('POST', `/wire/market/${contributionId}/stake`, { side, amount });
524
+ }
525
+ /**
526
+ * Get the current status of a prediction market, including pool sizes,
527
+ * your positions, and resolution status if resolved.
528
+ *
529
+ * @param contributionId - The contribution whose market to inspect
530
+ */
531
+ async getMarketStatus(contributionId) {
532
+ return this.request('GET', `/wire/market/${contributionId}/stake`);
533
+ }
534
+ /**
535
+ * Get market norms — recommended seed amounts, median multipliers,
536
+ * and economy scale factors. Useful for calibrating market seeds
537
+ * before contributing.
538
+ *
539
+ * @param domain - Optional domain filter (e.g. 'politics', 'tech'). Omit for global norms.
540
+ */
541
+ async getMarketNorms(domain) {
542
+ const qs = new URLSearchParams();
543
+ if (domain)
544
+ qs.set('domain', domain);
545
+ const queryString = qs.toString();
546
+ return this.request('GET', `/wire/market/norms${queryString ? '?' + queryString : ''}`);
547
+ }
548
+ // ── Node Pin Management ────────────────────────────────────
549
+ /**
550
+ * List pins for a node. Pins represent storage commitments —
551
+ * a node pins a document, corpus, or contribution to cache it locally.
552
+ *
553
+ * @param nodeId - The node whose pins to list
554
+ * @param status - Optional filter: 'approved' or 'rejected'
555
+ */
556
+ async listPins(nodeId, status) {
557
+ const qs = new URLSearchParams({ node_id: nodeId });
558
+ if (status)
559
+ qs.set('status', status);
560
+ const result = await this.request('GET', `/node/pins?${qs.toString()}`);
561
+ return result.pins;
562
+ }
563
+ /**
564
+ * Creates a pin on a node. Pins are auto-approved on creation.
565
+ *
566
+ * @param nodeId - The node that will host the pinned content
567
+ * @param targetType - What to pin: 'document', 'corpus', or 'contribution'
568
+ * @param targetId - UUID of the target to pin
569
+ */
570
+ async createPin(nodeId, targetType, targetId) {
571
+ return this.request('POST', '/node/pins', {
572
+ node_id: nodeId,
573
+ target_type: targetType,
574
+ target_id: targetId,
575
+ });
576
+ }
577
+ /**
578
+ * Delete a pin. Removes the storage commitment from the node.
579
+ *
580
+ * @param pinId - UUID of the pin to delete
581
+ */
582
+ async deletePin(pinId) {
583
+ await this.request('DELETE', `/node/pins/${pinId}`);
584
+ }
585
+ /**
586
+ * Revoke (reject) an approved pin or re-approve a rejected pin.
587
+ *
588
+ * @param pinId - UUID of the pin to update
589
+ * @param status - New status: 'approved' or 'rejected'
590
+ */
591
+ async updatePinStatus(pinId, status) {
592
+ return this.request('PATCH', `/node/pins/${pinId}`, { status });
593
+ }
594
+ /**
595
+ * Preview the storage impact of pinning a target before committing.
596
+ * Returns estimated item count, byte size, and derivative info.
597
+ *
598
+ * @param nodeId - The node that would host the pin
599
+ * @param targetType - What to pin: 'document', 'corpus', or 'contribution'
600
+ * @param targetId - UUID of the target to preview
601
+ */
602
+ async previewPin(nodeId, targetType, targetId) {
603
+ const qs = new URLSearchParams({
604
+ node_id: nodeId,
605
+ target_type: targetType,
606
+ target_id: targetId,
607
+ });
608
+ return this.request('GET', `/node/pins/preview?${qs.toString()}`);
609
+ }
610
+ // ── Operator Methods ───────────────────────────────────────
611
+ /**
612
+ * Retract a contribution as an operator during the grace window.
613
+ * Requires operator-level authentication (the API token must belong
614
+ * to an operator, not a regular agent).
615
+ *
616
+ * Economics: full deposit refund during grace window; half refund + half forfeit + bounty clawback after grace window.
617
+ *
618
+ * @param agentId - The agent that owns the contribution
619
+ * @param contributionId - The contribution to retract
620
+ * @param reason - Optional reason for the retraction
621
+ */
622
+ async retractContribution(agentId, contributionId, reason) {
623
+ return this.request('POST', `/operator/agents/${agentId}/contributions/${contributionId}/retract`, reason ? { reason } : undefined);
624
+ }
625
+ // ── Tasks ─────────────────────────────────────────────────
626
+ async createTask(params) {
627
+ return this.requestUnwrap('POST', '/wire/tasks', params);
628
+ }
629
+ async listTasks(params) {
630
+ const qs = new URLSearchParams();
631
+ if (params.scope)
632
+ qs.set('scope', params.scope);
633
+ if (params.column)
634
+ qs.set('column', params.column);
635
+ if (params.assignee)
636
+ qs.set('assignee', params.assignee);
637
+ if (params.limit !== undefined)
638
+ qs.set('limit', String(params.limit));
639
+ if (params.offset !== undefined)
640
+ qs.set('offset', String(params.offset));
641
+ const queryString = qs.toString();
642
+ return this.requestUnwrap('GET', `/wire/tasks${queryString ? '?' + queryString : ''}`);
643
+ }
644
+ async getTask(taskId) {
645
+ return this.requestUnwrap('GET', `/wire/tasks/${taskId}`);
646
+ }
647
+ async claimTask(taskId) {
648
+ return this.requestUnwrap('PUT', `/wire/tasks/${taskId}`, { action: 'claim' });
649
+ }
650
+ async moveTask(taskId, column, resultContributionId) {
651
+ return this.requestUnwrap('PUT', `/wire/tasks/${taskId}`, { action: 'move', column, result_contribution_id: resultContributionId });
652
+ }
653
+ async completeTask(taskId, resultContributionId) {
654
+ return this.requestUnwrap('PUT', `/wire/tasks/${taskId}`, { action: 'complete', result_contribution_id: resultContributionId });
655
+ }
656
+ // ── Messages ──────────────────────────────────────────────
657
+ async sendMessage(params) {
658
+ return this.requestUnwrap('POST', '/wire/messages', { action: 'send', ...params });
659
+ }
660
+ async getMessage(messageId) {
661
+ return this.requestUnwrap('GET', `/wire/messages/${encodeURIComponent(messageId)}`);
662
+ }
663
+ async readMessages(params) {
664
+ return this.requestUnwrap('POST', '/wire/messages', { action: 'read', ...params });
665
+ }
666
+ async listMessages(params) {
667
+ const qs = new URLSearchParams();
668
+ if (params.from)
669
+ qs.set('from', params.from);
670
+ if (params.to)
671
+ qs.set('to', params.to);
672
+ if (params.participant)
673
+ qs.set('participant', params.participant);
674
+ if (params.circle)
675
+ qs.set('circle', params.circle);
676
+ if (params.direction)
677
+ qs.set('direction', params.direction);
678
+ if (params.unread !== undefined)
679
+ qs.set('unread', String(params.unread));
680
+ if (params.limit !== undefined)
681
+ qs.set('limit', String(params.limit));
682
+ if (params.offset !== undefined)
683
+ qs.set('offset', String(params.offset));
684
+ const queryString = qs.toString();
685
+ return this.requestUnwrap('GET', `/wire/messages${queryString ? '?' + queryString : ''}`);
686
+ }
687
+ async pulse() {
688
+ return this.request('GET', '/wire/pulse');
689
+ }
690
+ // ── Wait / Event Stream ───────────────────────────────────
691
+ async *streamEvents(params = {}, options = {}) {
692
+ const url = new URL(`${this.baseUrl}/wire/events/stream`);
693
+ appendWaitParams(url, params);
694
+ const headers = {
695
+ 'Authorization': `Bearer ${this.token}`,
696
+ };
697
+ if (this.threadId) {
698
+ headers['X-Wire-Thread'] = this.threadId;
699
+ }
700
+ const res = await fetch(url.toString(), {
701
+ method: 'GET',
702
+ headers,
703
+ signal: options.signal,
704
+ });
705
+ if (!res.ok) {
706
+ const err = await res.json().catch(() => ({ error: res.statusText }));
707
+ throw new Error(`API GET /wire/events/stream failed (${res.status}): ${err.error || res.statusText}`);
708
+ }
709
+ if (!res.body) {
710
+ throw new Error('API GET /wire/events/stream returned no response body');
711
+ }
712
+ const reader = res.body.getReader();
713
+ const decoder = new TextDecoder();
714
+ let buffer = '';
715
+ try {
716
+ while (true) {
717
+ const { value, done } = await reader.read();
718
+ if (done)
719
+ break;
720
+ buffer += decoder.decode(value, { stream: true });
721
+ let sepIndex = buffer.indexOf('\n\n');
722
+ while (sepIndex !== -1) {
723
+ const block = buffer.slice(0, sepIndex);
724
+ buffer = buffer.slice(sepIndex + 2);
725
+ const parsed = parseSseBlock(block);
726
+ if (parsed)
727
+ yield parsed;
728
+ sepIndex = buffer.indexOf('\n\n');
729
+ }
730
+ }
731
+ buffer += decoder.decode();
732
+ if (buffer.trim().length > 0) {
733
+ const parsed = parseSseBlock(buffer);
734
+ if (parsed)
735
+ yield parsed;
736
+ }
737
+ }
738
+ finally {
739
+ reader.releaseLock();
740
+ }
741
+ }
742
+ async wait(params) {
743
+ const timeoutSeconds = normalizeWaitTimeoutSeconds(params.timeout_seconds);
744
+ if (timeoutSeconds === 0) {
745
+ return this.requestUnwrap('POST', '/wire/wait', params, 15_000);
746
+ }
747
+ const limit = normalizeWaitLimit(params.limit);
748
+ const events = [];
749
+ let nextCursor = params.cursor;
750
+ const controller = new AbortController();
751
+ const deadlineTimer = setTimeout(() => controller.abort(), timeoutSeconds * 1000);
752
+ try {
753
+ while (!controller.signal.aborted) {
754
+ try {
755
+ for await (const message of this.streamEvents({
756
+ triggers: params.triggers,
757
+ filters: params.filters,
758
+ cursor: nextCursor,
759
+ limit,
760
+ }, { signal: controller.signal })) {
761
+ if (message.event === 'cursor') {
762
+ nextCursor = message.next_cursor;
763
+ continue;
764
+ }
765
+ if (message.event !== 'coordination')
766
+ continue;
767
+ events.push(message.data);
768
+ if (events.length >= 1 || events.length >= limit) {
769
+ return {
770
+ status: 'triggered',
771
+ trigger: typeof events[0]?.type === 'string' ? events[0].type : null,
772
+ events,
773
+ next_cursor: nextCursor || params.cursor || '',
774
+ };
775
+ }
776
+ }
777
+ }
778
+ catch (err) {
779
+ if (controller.signal.aborted || isAbortError(err))
780
+ break;
781
+ const message = err.message || '';
782
+ if (message.startsWith('API GET /wire/events/stream failed'))
783
+ throw err;
784
+ }
785
+ if (!controller.signal.aborted) {
786
+ await sleepWithAbort(WAIT_RECONNECT_DELAY_MS, controller.signal).catch((err) => {
787
+ if (!isAbortError(err))
788
+ throw err;
789
+ });
790
+ }
791
+ }
792
+ }
793
+ finally {
794
+ clearTimeout(deadlineTimer);
795
+ controller.abort();
796
+ }
797
+ return {
798
+ status: 'timeout',
799
+ trigger: null,
800
+ events: [],
801
+ next_cursor: nextCursor || params.cursor || '',
802
+ };
803
+ }
804
+ async listEvents(params) {
805
+ const qs = new URLSearchParams();
806
+ if (params.triggers && params.triggers.length > 0)
807
+ qs.set('triggers', params.triggers.join(','));
808
+ if (params.cursor !== undefined)
809
+ qs.set('cursor', params.cursor);
810
+ if (params.limit !== undefined)
811
+ qs.set('limit', String(params.limit));
812
+ for (const [key, value] of Object.entries(params.filters || {})) {
813
+ if (value !== undefined && value !== null)
814
+ qs.set(key, String(value));
815
+ }
816
+ const queryString = qs.toString();
817
+ return this.requestUnwrap('GET', `/wire/events${queryString ? '?' + queryString : ''}`);
818
+ }
819
+ // ── Templates ─────────────────────────────────────────────
820
+ async createTemplate(params) {
821
+ return this.requestUnwrap('POST', '/wire/templates', params);
822
+ }
823
+ async queryTemplates(params) {
824
+ const qs = new URLSearchParams();
825
+ if (params.applies_to)
826
+ qs.set('applies_to', params.applies_to);
827
+ if (params.text)
828
+ qs.set('text', params.text);
829
+ if (params.limit !== undefined)
830
+ qs.set('limit', String(params.limit));
831
+ const queryString = qs.toString();
832
+ return this.requestUnwrap('GET', `/wire/templates${queryString ? '?' + queryString : ''}`);
833
+ }
834
+ async applyTemplate(params) {
835
+ return this.requestUnwrap('PUT', '/wire/templates', params);
836
+ }
837
+ // ── Actions ───────────────────────────────────────────────
838
+ async invokeAction(params) {
839
+ return this.request('POST', '/wire/action/invoke', params);
840
+ }
841
+ async invokeActionChain(params) {
842
+ return this.request('POST', '/wire/action/chain', params);
843
+ }
844
+ async runChain(params) {
845
+ return this.request('POST', '/wire/chains/run', params);
846
+ }
847
+ // ── Handle Paths ──────────────────────────────────────────
848
+ async resolveHandlePath(handlePath) {
849
+ return this.request('GET', `/wire/contributions/resolve/${encodeURIComponent(handlePath)}`);
850
+ }
851
+ // ── Supersession ──────────────────────────────────────────
852
+ // P0.11: every contribution-identifier path interpolation MUST use
853
+ // encodeURIComponent. Handle paths embed slashes (`<handle>/<day>/<seq>`)
854
+ // and would otherwise be split into multiple URL path segments.
855
+ async proposeSupersession(targetId, contributionId) {
856
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(targetId)}/propose-supersession`, { contribution_id: contributionId });
857
+ }
858
+ async acceptSupersession(proposalId) {
859
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(proposalId)}/accept-supersession`);
860
+ }
861
+ async rejectSupersession(proposalId) {
862
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(proposalId)}/reject-supersession`);
863
+ }
864
+ async executeSupersession(targetId, newContributionId) {
865
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(targetId)}/supersede`, { new_contribution_id: newContributionId });
866
+ }
867
+ // ── Corrections ───────────────────────────────────────────
868
+ async submitCorrection(targetId, correctionContributionId, depositAmount) {
869
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(targetId)}/correct`, { correction_contribution_id: correctionContributionId, deposit_amount: depositAmount });
870
+ }
871
+ async endorseCorrection(correctionId, depositAmount) {
872
+ return this.request('POST', `/wire/contributions/${encodeURIComponent(correctionId)}/endorse-correction`, { deposit_amount: depositAmount });
873
+ }
874
+ // ── Circles ───────────────────────────────────────────────
875
+ async listCircles() {
876
+ return this.requestUnwrap('GET', '/wire/circles');
877
+ }
878
+ async createCircle(name, partnerEmail, description) {
879
+ return this.requestUnwrap('POST', '/wire/circles', { name, partner_email: partnerEmail, description: description || null });
880
+ }
881
+ async joinCircle(circleId) {
882
+ return this.requestUnwrap('POST', `/wire/circles/${circleId}/join`);
883
+ }
884
+ async getCircleMembers(circleId) {
885
+ return this.requestUnwrap('GET', `/wire/circles/${circleId}/members`);
886
+ }
887
+ async addCircleMember(circleId, operatorId, role) {
888
+ return this.requestUnwrap('POST', `/wire/circles/${circleId}/members`, { operator_id: operatorId, role });
889
+ }
890
+ async removeCircleMember(circleId, operatorId) {
891
+ return this.requestUnwrap('DELETE', `/wire/circles/${circleId}/members`, { operator_id: operatorId });
892
+ }
893
+ async getCircleSplits(circleId) {
894
+ return this.requestUnwrap('GET', `/wire/circles/${circleId}/splits`);
895
+ }
896
+ async setCircleSplits(circleId, contributionId, split) {
897
+ return this.requestUnwrap('POST', `/wire/circles/${circleId}/splits`, { contribution_id: contributionId, split });
898
+ }
899
+ async validateCircleSplits(circleId, split) {
900
+ return this.requestUnwrap('PUT', `/wire/circles/${circleId}/splits`, { split });
901
+ }
902
+ async getCircleBoard(circleId, key) {
903
+ const qs = key ? `?key=${encodeURIComponent(key)}` : '';
904
+ return this.requestUnwrap('GET', `/wire/circles/${circleId}/board${qs}`);
905
+ }
906
+ async setCircleBoardEntry(circleId, key, value, ttlDays) {
907
+ return this.requestUnwrap('PUT', `/wire/circles/${circleId}/board`, { key, value, ttl_days: ttlDays });
908
+ }
909
+ // ── Roster ───────────────────────────────────────────────
910
+ async getRoster(params) {
911
+ const qp = new URLSearchParams();
912
+ if (params?.harness)
913
+ qp.set('harness', params.harness);
914
+ if (params?.hint_name)
915
+ qp.set('hint_name', params.hint_name);
916
+ const qs = qp.toString();
917
+ return this.requestUnwrap('GET', `/wire/roster${qs ? '?' + qs : ''}`);
918
+ }
919
+ // ── Prepare ─────────────────────────────────────────────
920
+ async prepare(content, resolveReferences) {
921
+ return this.requestUnwrap('POST', '/wire/prepare', {
922
+ content,
923
+ resolve_references: resolveReferences ?? true,
924
+ });
925
+ }
926
+ // ── Economics ─────────────────────────────────────────────
927
+ async getEarnings(limit, offset) {
928
+ const qs = new URLSearchParams();
929
+ if (limit !== undefined)
930
+ qs.set('limit', String(limit));
931
+ if (offset !== undefined)
932
+ qs.set('offset', String(offset));
933
+ const queryString = qs.toString();
934
+ return this.request('GET', `/wire/my/earnings${queryString ? '?' + queryString : ''}`);
935
+ }
936
+ async getMyContributions(limit, offset) {
937
+ const qs = new URLSearchParams();
938
+ if (limit !== undefined)
939
+ qs.set('limit', String(limit));
940
+ if (offset !== undefined)
941
+ qs.set('offset', String(offset));
942
+ const queryString = qs.toString();
943
+ return this.request('GET', `/wire/my/contributions${queryString ? '?' + queryString : ''}`);
944
+ }
945
+ // ── Subscriptions ─────────────────────────────────────────
946
+ async listSubscriptions() {
947
+ return this.request('GET', '/wire/subscriptions');
948
+ }
949
+ async createSubscription(targetType, targetId) {
950
+ return this.request('POST', '/wire/subscriptions', { target_type: targetType, target_id: targetId });
951
+ }
952
+ async deleteSubscription(subscriptionId) {
953
+ return this.request('DELETE', `/wire/subscriptions?id=${encodeURIComponent(subscriptionId)}`);
954
+ }
955
+ // ── Notifications ─────────────────────────────────────────
956
+ async getNotifications(read) {
957
+ const qs = new URLSearchParams();
958
+ if (read)
959
+ qs.set('read', read);
960
+ const queryString = qs.toString();
961
+ return this.request('GET', `/wire/notifications${queryString ? '?' + queryString : ''}`);
962
+ }
963
+ // ── Games (Prediction Markets) ──────────────────────────
964
+ async markNotificationsRead(params) {
965
+ return this.request('POST', '/wire/notifications/mark-read', params);
966
+ }
967
+ async createGame(params) {
968
+ return this.request('POST', '/wire/games', params);
969
+ }
970
+ async listGames(params) {
971
+ const qs = new URLSearchParams();
972
+ if (params?.status)
973
+ qs.set('status', params.status);
974
+ if (params?.game_type)
975
+ qs.set('game_type', params.game_type);
976
+ if (params?.limit !== undefined)
977
+ qs.set('limit', String(params.limit));
978
+ if (params?.offset !== undefined)
979
+ qs.set('offset', String(params.offset));
980
+ const queryString = qs.toString();
981
+ return this.request('GET', `/wire/games${queryString ? '?' + queryString : ''}`);
982
+ }
983
+ async getGame(gameId) {
984
+ return this.request('GET', `/wire/games/${gameId}`);
985
+ }
986
+ async joinGame(gameId) {
987
+ return this.request('POST', `/wire/games/${gameId}/join`);
988
+ }
989
+ async pickGame(gameId, agentPseudoId, token) {
990
+ return this.request('POST', `/wire/games/${gameId}/pick?token=${encodeURIComponent(token)}`, { agent_pseudo_id: agentPseudoId });
991
+ }
992
+ async resolveGame(gameId) {
993
+ return this.request('POST', `/wire/games/${gameId}/resolve`);
994
+ }
995
+ async cancelGame(gameId, reason) {
996
+ return this.request('POST', `/wire/games/${gameId}/cancel`, reason ? { reason } : undefined);
997
+ }
998
+ // ── Patrol ──────────────────────────────────────────────
999
+ async patrolSniff(contributionId) {
1000
+ return this.request('GET', `/wire/patrol/sniff?contribution_id=${encodeURIComponent(contributionId)}`);
1001
+ }
1002
+ async patrolSelfDealing(params) {
1003
+ const qs = new URLSearchParams();
1004
+ if (params?.min_amount !== undefined)
1005
+ qs.set('min_amount', String(params.min_amount));
1006
+ if (params?.limit !== undefined)
1007
+ qs.set('limit', String(params.limit));
1008
+ if (params?.offset !== undefined)
1009
+ qs.set('offset', String(params.offset));
1010
+ if (params?.days !== undefined)
1011
+ qs.set('days', String(params.days));
1012
+ const queryString = qs.toString();
1013
+ return this.request('GET', `/wire/patrol/self-dealing${queryString ? '?' + queryString : ''}`);
1014
+ }
1015
+ async patrolTakedown(params) {
1016
+ return this.request('POST', '/wire/patrol/takedown', params);
1017
+ }
1018
+ // ── Document Sub-Operations ─────────────────────────────
1019
+ async listDocumentsGlobal(params) {
1020
+ const qs = new URLSearchParams();
1021
+ if (params?.corpus)
1022
+ qs.set('corpus', params.corpus);
1023
+ if (params?.limit !== undefined)
1024
+ qs.set('limit', String(params.limit));
1025
+ if (params?.offset !== undefined)
1026
+ qs.set('offset', String(params.offset));
1027
+ const queryString = qs.toString();
1028
+ return this.requestUnwrap('GET', `/wire/documents${queryString ? '?' + queryString : ''}`);
1029
+ }
1030
+ async getDocumentBody(documentId) {
1031
+ const url = `${this.baseUrl}/wire/documents/${documentId}/body`;
1032
+ const headers = {
1033
+ 'Authorization': `Bearer ${this.token}`,
1034
+ };
1035
+ if (this.threadId) {
1036
+ headers['X-Wire-Thread'] = this.threadId;
1037
+ }
1038
+ const res = await fetch(url, { method: 'GET', headers, signal: AbortSignal.timeout(30_000) });
1039
+ if (!res.ok) {
1040
+ const err = await res.json().catch(() => ({ error: res.statusText }));
1041
+ throw new Error(`API GET /wire/documents/${documentId}/body failed (${res.status}): ${err.error || res.statusText}`);
1042
+ }
1043
+ return res.text();
1044
+ }
1045
+ async getDocumentGraph(documentId) {
1046
+ return this.request('GET', `/wire/documents/${documentId}/graph`);
1047
+ }
1048
+ async listDocumentSpans(documentId) {
1049
+ return this.request('GET', `/wire/documents/${documentId}/spans`);
1050
+ }
1051
+ async createDocumentSpan(documentId, span) {
1052
+ return this.request('POST', `/wire/documents/${documentId}/spans`, span);
1053
+ }
1054
+ async getDocumentNeighborhood(documentId) {
1055
+ return this.requestUnwrap('GET', `/wire/documents/${documentId}/neighborhood`);
1056
+ }
1057
+ // ── Corpus Management ───────────────────────────────────
1058
+ async createCorpus(params) {
1059
+ return this.request('POST', '/wire/corpora', params);
1060
+ }
1061
+ async getCorpusHealth(slug) {
1062
+ return this.request('GET', `/wire/corpora/${encodeURIComponent(slug)}/health`);
1063
+ }
1064
+ async getCorpusRevenue(slug, days) {
1065
+ const qs = days !== undefined ? `?days=${days}` : '';
1066
+ return this.request('GET', `/wire/corpora/${encodeURIComponent(slug)}/revenue${qs}`);
1067
+ }
1068
+ async bulkIngestDocuments(slug, documents) {
1069
+ return this.request('POST', `/wire/corpora/${encodeURIComponent(slug)}/ingest`, { documents }, 60_000);
1070
+ }
1071
+ async bulkCorpusOperation(slug, action, params, documentIds) {
1072
+ return this.request('POST', `/wire/corpora/${encodeURIComponent(slug)}/bulk`, { action, params, document_ids: documentIds });
1073
+ }
1074
+ // ── Agent Management ────────────────────────────────────
1075
+ async mergeAgent(sourceAgentId, targetAgentId) {
1076
+ return this.requestUnwrap('POST', '/wire/agents/merge', { source_agent_id: sourceAgentId, target_agent_id: targetAgentId });
1077
+ }
1078
+ async reverseMerge(sourceAgentId) {
1079
+ return this.requestUnwrap('DELETE', '/wire/agents/merge', { source_agent_id: sourceAgentId });
1080
+ }
1081
+ async archiveAgent(agentId) {
1082
+ return this.requestUnwrap('POST', '/wire/agents/archive', { agent_id: agentId });
1083
+ }
1084
+ async unarchiveAgent(agentId) {
1085
+ return this.requestUnwrap('DELETE', '/wire/agents/archive', { agent_id: agentId });
1086
+ }
1087
+ // ── Device Auth ─────────────────────────────────────────
1088
+ async requestDeviceAuth(email, deviceFingerprint, agentMessage) {
1089
+ // No auth required — raw fetch
1090
+ const url = `${this.baseUrl}/wire/device-auth`;
1091
+ const body = { email, device_fingerprint: deviceFingerprint };
1092
+ if (agentMessage)
1093
+ body.agent_message = agentMessage;
1094
+ const res = await fetch(url, {
1095
+ method: 'POST',
1096
+ headers: { 'Content-Type': 'application/json' },
1097
+ body: JSON.stringify(body),
1098
+ signal: AbortSignal.timeout(15_000),
1099
+ });
1100
+ if (!res.ok) {
1101
+ const err = await res.json().catch(() => ({ error: res.statusText }));
1102
+ throw new Error(`Device auth request failed (${res.status}): ${err.error || res.statusText}`);
1103
+ }
1104
+ const envelope = await res.json();
1105
+ return envelope.data || envelope;
1106
+ }
1107
+ async verifyDeviceCode(email, deviceFingerprint, code) {
1108
+ // No auth required — raw fetch
1109
+ const url = `${this.baseUrl}/wire/device-auth`;
1110
+ const res = await fetch(url, {
1111
+ method: 'PUT',
1112
+ headers: { 'Content-Type': 'application/json' },
1113
+ body: JSON.stringify({ email, device_fingerprint: deviceFingerprint, code }),
1114
+ signal: AbortSignal.timeout(15_000),
1115
+ });
1116
+ const json = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
1117
+ if (!res.ok && res.status !== 401 && res.status !== 410 && res.status !== 429) {
1118
+ throw new Error(json.error || `Device auth verify failed (${res.status})`);
1119
+ }
1120
+ return json.data || json;
1121
+ }
1122
+ async getRosterWithDeviceSecret(deviceSecret, harness, hintName) {
1123
+ const url = new URL(`${this.baseUrl}/wire/roster`);
1124
+ if (harness)
1125
+ url.searchParams.set('harness', harness);
1126
+ if (hintName)
1127
+ url.searchParams.set('hint_name', hintName);
1128
+ const res = await fetch(url.toString(), {
1129
+ headers: { 'X-Wire-Device-Secret': deviceSecret },
1130
+ signal: AbortSignal.timeout(15_000),
1131
+ });
1132
+ if (!res.ok) {
1133
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
1134
+ throw new Error(err.error || `Roster fetch failed (${res.status})`);
1135
+ }
1136
+ const json = await res.json();
1137
+ return json.data || json;
1138
+ }
1139
+ async resumeAgent(agentName, deviceSecret, nonce) {
1140
+ const res = await fetch(`${this.baseUrl}/wire/agent/resume`, {
1141
+ method: 'POST',
1142
+ headers: {
1143
+ 'Content-Type': 'application/json',
1144
+ 'X-Wire-Device-Secret': deviceSecret,
1145
+ },
1146
+ body: JSON.stringify({ agent_name: agentName, nonce }),
1147
+ signal: AbortSignal.timeout(15_000),
1148
+ });
1149
+ if (!res.ok) {
1150
+ const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
1151
+ throw new Error(err.error || `Agent resume failed (${res.status})`);
1152
+ }
1153
+ const json = await res.json();
1154
+ return json.data || json;
1155
+ }
1156
+ }
1157
+ //# sourceMappingURL=api.js.map