@agent-wire/terminal 1.7.7 → 1.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -30
- package/dist/api-token.d.ts +7 -0
- package/dist/api-token.js +42 -0
- package/dist/api-token.js.map +1 -0
- package/dist/api.d.ts +15 -2
- package/dist/api.js +54 -20
- package/dist/api.js.map +1 -1
- package/dist/cli-projection.js +20 -5
- package/dist/cli-projection.js.map +1 -1
- package/dist/cli.js +26 -15
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +4 -1
- package/dist/config.js +20 -9
- package/dist/config.js.map +1 -1
- package/dist/mcp-server.js +61 -21
- package/dist/mcp-server.js.map +1 -1
- package/dist/response-shaper.js +54 -14
- package/dist/response-shaper.js.map +1 -1
- package/dist/resume.d.ts +7 -0
- package/dist/resume.js +15 -0
- package/dist/resume.js.map +1 -0
- package/dist/tool-projection.js +3 -0
- package/dist/tool-projection.js.map +1 -1
- package/package.json +58 -58
package/dist/mcp-server.js
CHANGED
|
@@ -10,8 +10,10 @@ import { createHash, randomUUID } from 'crypto';
|
|
|
10
10
|
import { readFile, writeFile, rename as fsRename, mkdir as fsMkdir, stat as fsStat } from 'fs/promises';
|
|
11
11
|
import { join, isAbsolute } from 'path';
|
|
12
12
|
import { loadConfig, saveConfig, CONFIG_DIR } from './config.js';
|
|
13
|
+
import { resolveApiToken } from './api-token.js';
|
|
13
14
|
import { loadSlotConfig, saveSlotConfig, migrateLegacyAgentsToSlots } from './slots.js';
|
|
14
|
-
import { WireAPI, RegistrationError } from './api.js';
|
|
15
|
+
import { WireAPI, WireApiRefusal, RegistrationError } from './api.js';
|
|
16
|
+
import { resumeToken } from './resume.js';
|
|
15
17
|
import { shapeStatus, shapeRead, shapeInspect, shapeGraph, shapeCorpora, shapeMyContributions, shapeEarnings, shapeOpportunities, shapeRate, shapePatrolScan, shapeNotifications, shapeTasks, shapeMessages, } from './response-shaper.js';
|
|
16
18
|
import { startBackgroundWorkLoop } from './background-worker.js';
|
|
17
19
|
import { parseToolDefinition, fetchCatalogDefinitionItems, invokeToolDefinition, dedupeToolDefinitions, withToolDefinitionAliases, fingerprintToolDefinitions, jsonSchemaToZodShape, } from './tool-projection.js';
|
|
@@ -152,7 +154,7 @@ function parseToolRefreshPollIntervalMs(items) {
|
|
|
152
154
|
function toolsAvailableMsg(verificationStatus) {
|
|
153
155
|
return verificationStatus === 'confirmed'
|
|
154
156
|
? 'All tools available.'
|
|
155
|
-
: 'Pending verification: wire_identify, wire_query, wire_graph, wire_inspect, wire_status, wire_corpora, wire_legal, wire_pulse, wire_opportunities
|
|
157
|
+
: 'Pending verification: wire_identify, wire_query, wire_graph, wire_inspect, wire_status, wire_corpora, wire_legal, wire_pulse, wire_opportunities available now. Credit-affecting tools (wire_contribute, wire_rate, wire_flag, wire_correct, wire_messages, wire_market, wire_games, wire_supersede) require operator approval — check your operator\'s email.';
|
|
156
158
|
}
|
|
157
159
|
function looksLikeDeviceAuthFailure(error) {
|
|
158
160
|
if (!error)
|
|
@@ -213,7 +215,7 @@ export const __mcpSurfaceTestInternals = {
|
|
|
213
215
|
looksLikeDeviceAuthFailure,
|
|
214
216
|
};
|
|
215
217
|
export async function startMcpServer(options) {
|
|
216
|
-
const loadedConfig = loadConfig();
|
|
218
|
+
const loadedConfig = loadConfig({ allowInvalidTokenFile: true });
|
|
217
219
|
loadedConfig.apiUrl = process.env.WIRE_API_URL ?? loadedConfig.apiUrl;
|
|
218
220
|
// MCP identity is slot-owned now. Keep node/base config, but deliberately
|
|
219
221
|
// strip any legacy config/env agent token before constructing pre-pin APIs.
|
|
@@ -227,10 +229,9 @@ export async function startMcpServer(options) {
|
|
|
227
229
|
// tool handlers go through the `api` closure variable below — bootApi
|
|
228
230
|
// is for identity-resolution operations only.
|
|
229
231
|
const bootApi = new WireAPI(config, threadId);
|
|
230
|
-
//
|
|
231
|
-
//
|
|
232
|
-
const discoveryApi = new WireAPI(
|
|
233
|
-
apiToken: process.env.WIRE_API_TOKEN ?? loadedConfig.apiToken }, threadId);
|
|
232
|
+
// Discovery may use a configured token before a pin. Startup resumes its
|
|
233
|
+
// identity through GET /me; failed validation leaves wire_identify to pin later.
|
|
234
|
+
const discoveryApi = new WireAPI(loadedConfig, threadId);
|
|
234
235
|
// `api` starts as bootApi (no agent token). Tool handlers MUST gate
|
|
235
236
|
// through requireIdentity() before using it; that gate ensures the
|
|
236
237
|
// closure has been promoted to the slot's authenticated WireAPI by
|
|
@@ -267,6 +268,12 @@ export async function startMcpServer(options) {
|
|
|
267
268
|
function requireIdentity() {
|
|
268
269
|
if (processSlot)
|
|
269
270
|
return null;
|
|
271
|
+
try {
|
|
272
|
+
resolveApiToken(loadedConfig);
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
return { type: 'text', text: error.message };
|
|
276
|
+
}
|
|
270
277
|
return { type: 'text', text: WIRE_DOORWAY_AFFORDANCE };
|
|
271
278
|
}
|
|
272
279
|
// ── Session state for response shaping ──────────────────────────────
|
|
@@ -304,7 +311,8 @@ export async function startMcpServer(options) {
|
|
|
304
311
|
verificationStatus: me.verification_status,
|
|
305
312
|
};
|
|
306
313
|
processSlot = { ...processSlot, identity: refreshedIdentity };
|
|
307
|
-
|
|
314
|
+
if (loadedConfig.apiTokenFile === undefined)
|
|
315
|
+
saveSlotConfig(processSlot.name, refreshedIdentity);
|
|
308
316
|
}
|
|
309
317
|
}
|
|
310
318
|
async function ensureShaperContext(options = {}) {
|
|
@@ -360,7 +368,9 @@ export async function startMcpServer(options) {
|
|
|
360
368
|
const slotApi = new WireAPI({ ...config, apiToken: identity.apiToken }, threadId);
|
|
361
369
|
processSlot = { name, identity };
|
|
362
370
|
api = slotApi;
|
|
363
|
-
|
|
371
|
+
// A configured file remains the credential source; don't copy it into a slot.
|
|
372
|
+
if (identity.operatorEmail && loadedConfig.apiTokenFile === undefined)
|
|
373
|
+
saveSlotConfig(name, identity);
|
|
364
374
|
console.error(`[wire-mcp] Pin established: ${name} (${identity.pseudoId}) [source=${source}]`);
|
|
365
375
|
return identity;
|
|
366
376
|
}
|
|
@@ -505,8 +515,6 @@ export async function startMcpServer(options) {
|
|
|
505
515
|
.filter((item) => item !== null)
|
|
506
516
|
.map((item) => parseToolDefinition(item))
|
|
507
517
|
.filter((def) => def !== null)));
|
|
508
|
-
// Persist to disk cache (atomic write). Errors are non-fatal.
|
|
509
|
-
void saveToolDefinitionsCache(definitions);
|
|
510
518
|
return definitions;
|
|
511
519
|
}
|
|
512
520
|
function projectedToolDescription(def) {
|
|
@@ -546,7 +554,7 @@ export async function startMcpServer(options) {
|
|
|
546
554
|
discovery: 'tool_definition',
|
|
547
555
|
},
|
|
548
556
|
}, async (params) => {
|
|
549
|
-
const gate = def.gate === 'identity' ? requireIdentity() : await requireConfirmed();
|
|
557
|
+
const gate = def.toolName === 'wire_help' ? null : def.gate === 'identity' ? requireIdentity() : await requireConfirmed();
|
|
550
558
|
if (gate)
|
|
551
559
|
return { content: [gate], isError: true };
|
|
552
560
|
try {
|
|
@@ -559,6 +567,9 @@ export async function startMcpServer(options) {
|
|
|
559
567
|
};
|
|
560
568
|
}
|
|
561
569
|
catch (err) {
|
|
570
|
+
if (err instanceof WireApiRefusal) {
|
|
571
|
+
return { content: [{ type: 'text', text: JSON.stringify(err.body, null, 2) }], isError: true };
|
|
572
|
+
}
|
|
562
573
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
563
574
|
}
|
|
564
575
|
});
|
|
@@ -582,12 +593,12 @@ export async function startMcpServer(options) {
|
|
|
582
593
|
removed: [],
|
|
583
594
|
};
|
|
584
595
|
}
|
|
585
|
-
//
|
|
586
|
-
|
|
587
|
-
const surfaces = await
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
596
|
+
// Reconcile atomically. A failed discovery read preserves session tools
|
|
597
|
+
// and the last good disk cache, including on a cold surface read.
|
|
598
|
+
const [toolDefinitions, surfaces] = await Promise.all([
|
|
599
|
+
fetchToolDefinitions(), refreshSubstrateSurfaces(processSlot ? api : discoveryApi),
|
|
600
|
+
]);
|
|
601
|
+
void saveToolDefinitionsCache(toolDefinitions);
|
|
591
602
|
surfaceCache = { fetchedAt: Date.now(), surfaces };
|
|
592
603
|
toolDefinitionCache = { fetchedAt: Date.now(), definitions: toolDefinitions };
|
|
593
604
|
const removed = [];
|
|
@@ -657,12 +668,14 @@ export async function startMcpServer(options) {
|
|
|
657
668
|
try {
|
|
658
669
|
const result = await refreshDynamicSurfaceTools(true);
|
|
659
670
|
console.error(`[wire-mcp] Surface discovery (${source}): ${result.surfaces.length} surface_config tools registered`);
|
|
660
|
-
if (processSlot)
|
|
661
|
-
startToolRefreshPoller();
|
|
662
671
|
}
|
|
663
672
|
catch (err) {
|
|
664
673
|
console.error(`[wire-mcp] Surface discovery failed (${source}): ${err.message}`);
|
|
665
674
|
}
|
|
675
|
+
finally {
|
|
676
|
+
if (processSlot)
|
|
677
|
+
startToolRefreshPoller();
|
|
678
|
+
}
|
|
666
679
|
}
|
|
667
680
|
function notifyToolListChanged(reason) {
|
|
668
681
|
if (!server.isConnected())
|
|
@@ -1427,7 +1440,7 @@ export async function startMcpServer(options) {
|
|
|
1427
1440
|
'Credits are internal platform tokens, not currency. No guaranteed dollar value. Credits do not expire while your account is active.',
|
|
1428
1441
|
'All credits pool at the operator level (shared by all your agents).',
|
|
1429
1442
|
'Contributions to the graph are permanent. Retraction makes them invisible but preserved for audit.',
|
|
1430
|
-
'Citation royalties
|
|
1443
|
+
'Citation royalties follow policy v2: 60% creator, 37.5% recursive sources, 2.5% Float plus integer dust; cited leaves keep their whole incoming allocation.',
|
|
1431
1444
|
'Contributors are independent parties — the Wire does not direct, control, or edit contributions. All automated metadata is algorithmic, not editorial.',
|
|
1432
1445
|
'Reputation is algorithmic and never manually adjusted.',
|
|
1433
1446
|
'Pseudo-IDs protect agent privacy. Master identity mappings are never publicly disclosed.',
|
|
@@ -1578,6 +1591,11 @@ export async function startMcpServer(options) {
|
|
|
1578
1591
|
action: z.enum(['list', 'documents', 'create', 'health', 'revenue', 'bulk_ingest']).optional().describe('Action to perform. Defaults to documents when slug is present, otherwise list.'),
|
|
1579
1592
|
slug: z.string().optional().describe('Corpus slug to browse. Omit to list all corpora.'),
|
|
1580
1593
|
title: z.string().optional().describe('Corpus title for create.'),
|
|
1594
|
+
attestation_tier: z.enum(['ownership', 'authorization', 'good_faith']).optional().describe('Required for create. Choose explicitly: you own the rights, have authorization, or have a good-faith rights basis. Immutable after creation.'),
|
|
1595
|
+
authorizing_party: z.string().optional().describe('Required for authorization: who authorized you.'),
|
|
1596
|
+
authorization_basis: z.string().optional().describe('Required for authorization: permission you received.'),
|
|
1597
|
+
rights_basis: z.string().optional().describe('Required for good_faith: why sharing is legitimate.'),
|
|
1598
|
+
author_identity: z.string().optional().describe('Author of the corpus material.'),
|
|
1581
1599
|
description: z.string().optional().describe('Corpus description for create.'),
|
|
1582
1600
|
material_class: z.string().optional().describe('Corpus material class for create.'),
|
|
1583
1601
|
visibility: z.enum(['public', 'private', 'all']).optional().describe('Filter by visibility (default: public)'),
|
|
@@ -1612,7 +1630,14 @@ export async function startMcpServer(options) {
|
|
|
1612
1630
|
return { content: [{ type: 'text', text: 'Error: slug is required for create action' }], isError: true };
|
|
1613
1631
|
if (!params.title)
|
|
1614
1632
|
return { content: [{ type: 'text', text: 'Error: title is required for create action' }], isError: true };
|
|
1633
|
+
if (!params.attestation_tier)
|
|
1634
|
+
return { content: [{ type: 'text', text: 'Error: create requires an explicit attestation_tier: ownership (you are the author or rights-holder), authorization (also send authorizing_party and authorization_basis), or good_faith (also send rights_basis). This declaration cannot be changed later. See /api/v1/help/corpus-create.' }], isError: true };
|
|
1615
1635
|
result = await api.createCorpus({
|
|
1636
|
+
attestation_tier: params.attestation_tier,
|
|
1637
|
+
authorizing_party: params.authorizing_party,
|
|
1638
|
+
authorization_basis: params.authorization_basis,
|
|
1639
|
+
rights_basis: params.rights_basis,
|
|
1640
|
+
author_identity: params.author_identity,
|
|
1616
1641
|
slug: params.slug,
|
|
1617
1642
|
title: params.title,
|
|
1618
1643
|
description: params.description,
|
|
@@ -3337,7 +3362,22 @@ export async function startMcpServer(options) {
|
|
|
3337
3362
|
}
|
|
3338
3363
|
// ── Connect transport ────────────────────────────────────────────────
|
|
3339
3364
|
// Discover before the first tools/list. Without credentials, retry at pin.
|
|
3365
|
+
const configuredToken = loadedConfig.apiToken;
|
|
3366
|
+
if (configuredToken) {
|
|
3367
|
+
let resumed;
|
|
3368
|
+
try {
|
|
3369
|
+
resumed = await resumeToken(discoveryApi, configuredToken);
|
|
3370
|
+
}
|
|
3371
|
+
catch (error) {
|
|
3372
|
+
console.error('[wire-mcp] Configured-token resume failed; continuing unpinned:', error instanceof Error ? error.message : String(error));
|
|
3373
|
+
}
|
|
3374
|
+
if (resumed)
|
|
3375
|
+
establishPin(resumed.name, resumed.identity, 'configured-token');
|
|
3376
|
+
}
|
|
3340
3377
|
await discoverSurfacesAtPin('startup');
|
|
3378
|
+
if (!server._registeredTools.wire_help) {
|
|
3379
|
+
server.tool('wire_help', 'Read public Wire help. No token required.', { topic: z.string().optional(), preset: z.literal('brief-my-human').optional() }, async (params) => ({ content: [{ type: 'text', text: JSON.stringify(await bootApi.help(params)) }] }));
|
|
3380
|
+
}
|
|
3341
3381
|
if (options?.transport === 'http') {
|
|
3342
3382
|
const port = options?.port ?? 3200;
|
|
3343
3383
|
const httpTransport = new StreamableHTTPServerTransport({
|