@neus/sdk 1.1.7 → 1.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.
- package/README.md +17 -4
- package/cjs/cli-commands.cjs +101 -0
- package/cjs/index.cjs +612 -0
- package/cjs/mcp-hosts.cjs +91 -8
- package/cjs/runtime-adapters.cjs +218 -0
- package/cjs/runtime-mount.cjs +452 -0
- package/cli/neus.mjs +337 -86
- package/cli-commands.js +75 -0
- package/index.js +60 -2
- package/mcp-hosts.js +54 -12
- package/package.json +17 -2
- package/runtime-adapters.js +214 -0
- package/runtime-mount.js +522 -0
- package/types.d.ts +89 -0
package/runtime-mount.js
ADDED
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Mount — proof-backed agent context bundle (neus.runtime-mount.v1).
|
|
3
|
+
* SSOT for CLI, integrators, and protocol neus_agent_mount response shape.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const RUNTIME_MOUNT_SCHEMA = 'neus.runtime-mount.v1';
|
|
7
|
+
|
|
8
|
+
const PROOF_URL_BASE = 'https://neus.network/proof/';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string | null | undefined} value
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeWallet(value) {
|
|
14
|
+
const wallet = String(value || '').trim().toLowerCase();
|
|
15
|
+
return /^0x[a-f0-9]{40}$/.test(wallet) ? wallet : '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown} value
|
|
20
|
+
*/
|
|
21
|
+
function asString(value) {
|
|
22
|
+
const trimmed = String(value ?? '').trim();
|
|
23
|
+
return trimmed.length > 0 ? trimmed : '';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {unknown} value
|
|
28
|
+
* @returns {string[]}
|
|
29
|
+
*/
|
|
30
|
+
function asStringArray(value) {
|
|
31
|
+
if (!Array.isArray(value)) return [];
|
|
32
|
+
return value.map(item => String(item || '').trim()).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @param {Record<string, unknown> | null | undefined} caps
|
|
37
|
+
*/
|
|
38
|
+
function capabilitiesToArray(caps) {
|
|
39
|
+
if (Array.isArray(caps)) return asStringArray(caps);
|
|
40
|
+
if (!caps || typeof caps !== 'object') return [];
|
|
41
|
+
return Object.entries(caps)
|
|
42
|
+
.filter(([, enabled]) => enabled === true)
|
|
43
|
+
.map(([key]) => String(key).trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {number | null | undefined} expiresAt
|
|
49
|
+
*/
|
|
50
|
+
export function isDelegationExpired(expiresAt) {
|
|
51
|
+
if (expiresAt === null || expiresAt === 0) return false;
|
|
52
|
+
const ms = Number(expiresAt);
|
|
53
|
+
return Number.isFinite(ms) && ms > 0 && ms <= Date.now();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Array<Record<string, unknown>>} identities
|
|
58
|
+
* @param {{ agentId?: string, agentWallet?: string, identityQHash?: string }} selector
|
|
59
|
+
*/
|
|
60
|
+
export function pickIdentity(identities, selector) {
|
|
61
|
+
const list = Array.isArray(identities) ? identities : [];
|
|
62
|
+
const qHash = asString(selector.identityQHash).toLowerCase();
|
|
63
|
+
if (qHash) {
|
|
64
|
+
return list.find(row => asString(row.qHash).toLowerCase() === qHash) || null;
|
|
65
|
+
}
|
|
66
|
+
const agentId = asString(selector.agentId).toLowerCase();
|
|
67
|
+
const agentWallet = normalizeWallet(selector.agentWallet);
|
|
68
|
+
if (agentId) {
|
|
69
|
+
const byId = list.filter(row => asString(row.agentId).toLowerCase() === agentId);
|
|
70
|
+
if (agentWallet) {
|
|
71
|
+
return byId.find(row => normalizeWallet(row.agentWallet) === agentWallet) || byId[0] || null;
|
|
72
|
+
}
|
|
73
|
+
return byId[0] || null;
|
|
74
|
+
}
|
|
75
|
+
if (agentWallet) {
|
|
76
|
+
return list.find(row => normalizeWallet(row.agentWallet) === agentWallet) || null;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {Array<Record<string, unknown>>} delegations
|
|
83
|
+
* @param {string} controllerWallet
|
|
84
|
+
* @param {string} agentWallet
|
|
85
|
+
* @param {string} agentId
|
|
86
|
+
*/
|
|
87
|
+
export function pickActiveDelegation(delegations, controllerWallet, agentWallet, agentId) {
|
|
88
|
+
const list = Array.isArray(delegations) ? delegations : [];
|
|
89
|
+
const controller = normalizeWallet(controllerWallet);
|
|
90
|
+
const agent = normalizeWallet(agentWallet);
|
|
91
|
+
const id = asString(agentId).toLowerCase();
|
|
92
|
+
const candidates = list.filter(row => {
|
|
93
|
+
if (isDelegationExpired(row.expiresAt)) return false;
|
|
94
|
+
const rowAgent = normalizeWallet(row.agentWallet);
|
|
95
|
+
const rowController = normalizeWallet(row.controllerWallet);
|
|
96
|
+
if (agent && rowAgent && rowAgent !== agent) return false;
|
|
97
|
+
if (controller && rowController && rowController !== controller) return false;
|
|
98
|
+
if (id && row.agentId && asString(row.agentId).toLowerCase() !== id) return false;
|
|
99
|
+
return true;
|
|
100
|
+
});
|
|
101
|
+
return candidates[0] || null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {Record<string, unknown> | null | undefined} identity
|
|
106
|
+
* @param {Record<string, unknown> | null | undefined} delegation
|
|
107
|
+
*/
|
|
108
|
+
export function resolveEffectiveRuntime(identity, delegation) {
|
|
109
|
+
const delProvider = asString(delegation?.provider);
|
|
110
|
+
const delModel = asString(delegation?.model);
|
|
111
|
+
if (delProvider || delModel) {
|
|
112
|
+
return {
|
|
113
|
+
provider: delProvider || 'openai',
|
|
114
|
+
model: delModel || ''
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const defaultRuntime =
|
|
118
|
+
identity?.defaultRuntime && typeof identity.defaultRuntime === 'object'
|
|
119
|
+
? identity.defaultRuntime
|
|
120
|
+
: null;
|
|
121
|
+
const idProvider = asString(defaultRuntime?.provider);
|
|
122
|
+
const idModel = asString(defaultRuntime?.model);
|
|
123
|
+
if (idProvider || idModel) {
|
|
124
|
+
return {
|
|
125
|
+
provider: idProvider || 'openai',
|
|
126
|
+
model: idModel || ''
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {unknown} proof
|
|
134
|
+
*/
|
|
135
|
+
export function extractAgentContextFromProofs(proofs) {
|
|
136
|
+
const identities = [];
|
|
137
|
+
const delegations = [];
|
|
138
|
+
const list = Array.isArray(proofs) ? proofs : [];
|
|
139
|
+
|
|
140
|
+
for (const proof of list) {
|
|
141
|
+
const qHash = asString(proof?.qHash);
|
|
142
|
+
const verifiedVerifiers = Array.isArray(proof?.verifiedVerifiers) ? proof.verifiedVerifiers : [];
|
|
143
|
+
for (const vv of verifiedVerifiers) {
|
|
144
|
+
const verifierId = asString(vv?.verifierId);
|
|
145
|
+
const vvData = vv?.data && typeof vv.data === 'object' ? vv.data : {};
|
|
146
|
+
if (verifierId === 'agent-identity') {
|
|
147
|
+
identities.push({
|
|
148
|
+
qHash,
|
|
149
|
+
agentId: vvData.agentId || null,
|
|
150
|
+
agentWallet: vvData.agentWallet || null,
|
|
151
|
+
agentLabel: vvData.agentLabel || vvData.agentId || 'Agent',
|
|
152
|
+
agentType: vvData.agentType || 'agent',
|
|
153
|
+
description: vvData.description || null,
|
|
154
|
+
capabilities: capabilitiesToArray(vvData.capabilities),
|
|
155
|
+
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
156
|
+
instructions: vvData.instructions || null,
|
|
157
|
+
services: Array.isArray(vvData.services) ? vvData.services : [],
|
|
158
|
+
defaultRuntime:
|
|
159
|
+
vvData.defaultRuntime && typeof vvData.defaultRuntime === 'object'
|
|
160
|
+
? vvData.defaultRuntime
|
|
161
|
+
: undefined
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (verifierId === 'agent-delegation') {
|
|
165
|
+
delegations.push({
|
|
166
|
+
qHash,
|
|
167
|
+
controllerWallet: vvData.controllerWallet || null,
|
|
168
|
+
agentWallet: vvData.agentWallet || null,
|
|
169
|
+
agentId: vvData.agentId || null,
|
|
170
|
+
scope: vvData.scope || 'global',
|
|
171
|
+
allowedActions: asStringArray(vvData.allowedActions),
|
|
172
|
+
deniedActions: asStringArray(vvData.deniedActions),
|
|
173
|
+
runtimePolicy:
|
|
174
|
+
vvData.runtimePolicy && typeof vvData.runtimePolicy === 'object'
|
|
175
|
+
? vvData.runtimePolicy
|
|
176
|
+
: undefined,
|
|
177
|
+
expiresAt: vvData.expiresAt ?? null,
|
|
178
|
+
isExpired: isDelegationExpired(vvData.expiresAt),
|
|
179
|
+
maxSpend: vvData.maxSpend !== null ? String(vvData.maxSpend) : undefined,
|
|
180
|
+
instructions: vvData.instructions || null,
|
|
181
|
+
skills: Array.isArray(vvData.skills) ? vvData.skills : [],
|
|
182
|
+
provider: vvData.provider || vvData.modelProvider || null,
|
|
183
|
+
model: vvData.model || null
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { identities, delegations };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* @param {Record<string, unknown>} input
|
|
194
|
+
*/
|
|
195
|
+
export function buildRuntimeBundle(input) {
|
|
196
|
+
const identity = input.identity || {};
|
|
197
|
+
const delegation = input.delegation || null;
|
|
198
|
+
const identityQHash = asString(input.identityQHash || identity.qHash);
|
|
199
|
+
const delegationQHash = delegation ? asString(input.delegationQHash || delegation.qHash) : null;
|
|
200
|
+
const agentId = asString(identity.agentId);
|
|
201
|
+
const agentWallet = normalizeWallet(identity.agentWallet);
|
|
202
|
+
|
|
203
|
+
if (!identityQHash || !agentId || !agentWallet) {
|
|
204
|
+
throw new Error('Runtime mount requires verified agent identity (agentId, agentWallet, identityQHash).');
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const effectiveRuntime = resolveEffectiveRuntime(identity, delegation);
|
|
208
|
+
const deniedActions = delegation ? asStringArray(delegation.deniedActions) : [];
|
|
209
|
+
const allowedActions = delegation ? asStringArray(delegation.allowedActions) : undefined;
|
|
210
|
+
const requiresHumanApproval =
|
|
211
|
+
delegation?.runtimePolicy &&
|
|
212
|
+
typeof delegation.runtimePolicy === 'object' &&
|
|
213
|
+
delegation.runtimePolicy.requiresHumanApproval === true;
|
|
214
|
+
|
|
215
|
+
const capabilities = asStringArray(identity.capabilities);
|
|
216
|
+
const skills = Array.isArray(identity.skills) ? identity.skills : [];
|
|
217
|
+
const skillIds = skills
|
|
218
|
+
.map(skill =>
|
|
219
|
+
typeof skill === 'string' ? skill : asString(skill?.id || skill?.label)
|
|
220
|
+
)
|
|
221
|
+
.filter(Boolean);
|
|
222
|
+
|
|
223
|
+
const delegations = delegation ? [delegation] : [];
|
|
224
|
+
const activeDelegations = delegations.filter(row => !row.isExpired).length;
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
schema: RUNTIME_MOUNT_SCHEMA,
|
|
228
|
+
mountedAt: new Date().toISOString(),
|
|
229
|
+
trust: {
|
|
230
|
+
identityQHash,
|
|
231
|
+
delegationQHash: delegationQHash || null,
|
|
232
|
+
identityProofUrl: `${PROOF_URL_BASE}${identityQHash}`,
|
|
233
|
+
delegationProofUrl: delegationQHash ? `${PROOF_URL_BASE}${delegationQHash}` : null
|
|
234
|
+
},
|
|
235
|
+
identity: {
|
|
236
|
+
agentId,
|
|
237
|
+
agentWallet,
|
|
238
|
+
agentLabel: asString(identity.agentLabel) || agentId,
|
|
239
|
+
agentType: asString(identity.agentType) || 'agent',
|
|
240
|
+
description: asString(identity.description) || undefined,
|
|
241
|
+
instructions: asString(identity.instructions) || undefined,
|
|
242
|
+
capabilities,
|
|
243
|
+
skills,
|
|
244
|
+
services: Array.isArray(identity.services) ? identity.services : undefined,
|
|
245
|
+
defaultRuntime:
|
|
246
|
+
identity.defaultRuntime && typeof identity.defaultRuntime === 'object'
|
|
247
|
+
? identity.defaultRuntime
|
|
248
|
+
: undefined
|
|
249
|
+
},
|
|
250
|
+
delegation: delegation
|
|
251
|
+
? {
|
|
252
|
+
controllerWallet: normalizeWallet(delegation.controllerWallet) || asString(delegation.controllerWallet),
|
|
253
|
+
scope: asString(delegation.scope) || undefined,
|
|
254
|
+
allowedActions,
|
|
255
|
+
deniedActions,
|
|
256
|
+
runtimePolicy: delegation.runtimePolicy,
|
|
257
|
+
expiresAt: delegation.expiresAt ?? null,
|
|
258
|
+
isExpired: Boolean(delegation.isExpired),
|
|
259
|
+
maxSpend: delegation.maxSpend,
|
|
260
|
+
instructions: asString(delegation.instructions) || undefined,
|
|
261
|
+
skills: Array.isArray(delegation.skills) ? delegation.skills : undefined,
|
|
262
|
+
provider: asString(delegation.provider) || undefined,
|
|
263
|
+
model: asString(delegation.model) || undefined
|
|
264
|
+
}
|
|
265
|
+
: null,
|
|
266
|
+
effectiveRuntime,
|
|
267
|
+
tools: Array.isArray(input.tools) ? input.tools : [],
|
|
268
|
+
secretBindings: Array.isArray(input.secretBindings) ? input.secretBindings : [],
|
|
269
|
+
memoryRefs: Array.isArray(input.memoryRefs) ? input.memoryRefs : undefined,
|
|
270
|
+
enforce: {
|
|
271
|
+
deniedActions,
|
|
272
|
+
...(allowedActions?.length ? { allowedActions } : {}),
|
|
273
|
+
...(requiresHumanApproval ? { requiresHumanApproval: true } : {})
|
|
274
|
+
},
|
|
275
|
+
contextPack: {
|
|
276
|
+
identityCount: 1,
|
|
277
|
+
delegationCount: delegations.length,
|
|
278
|
+
activeDelegations,
|
|
279
|
+
capabilitiesSummary: capabilities.slice(0, 32),
|
|
280
|
+
skillsSummary: skillIds.slice(0, 32)
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @param {Record<string, unknown>} profileAgent
|
|
287
|
+
*/
|
|
288
|
+
export function profileAgentToIdentitySeed(profileAgent) {
|
|
289
|
+
return {
|
|
290
|
+
agentId: profileAgent.agentId,
|
|
291
|
+
agentWallet: profileAgent.agentWallet,
|
|
292
|
+
agentLabel: profileAgent.agentLabel || profileAgent.name,
|
|
293
|
+
agentType: profileAgent.agentType || profileAgent.typeLabel,
|
|
294
|
+
description: profileAgent.description,
|
|
295
|
+
instructions: profileAgent.instructions,
|
|
296
|
+
capabilities: capabilitiesToArray(profileAgent.capabilities),
|
|
297
|
+
skills: Array.isArray(profileAgent.skills) ? profileAgent.skills : [],
|
|
298
|
+
services: Array.isArray(profileAgent.services) ? profileAgent.services : [],
|
|
299
|
+
identityQHash: profileAgent.identityQHash || profileAgent.qHash
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @param {import('./runtime-mount.js').RuntimeBundle | Record<string, unknown>} value
|
|
305
|
+
*/
|
|
306
|
+
export function isRuntimeBundle(value) {
|
|
307
|
+
return Boolean(value && typeof value === 'object' && value.schema === RUNTIME_MOUNT_SCHEMA);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Resolve mount via MCP (server tool first, then client assembly).
|
|
312
|
+
*
|
|
313
|
+
* @param {{
|
|
314
|
+
* callMcpTool: (args: { name: string, args?: Record<string, unknown>, accessKey?: string, sessionId?: string, signal?: AbortSignal }) => Promise<{ ok: boolean, payload?: unknown, error?: string }>,
|
|
315
|
+
* initializeMcp?: () => Promise<{ sessionId: string }>,
|
|
316
|
+
* accessKey: string,
|
|
317
|
+
* agentId?: string,
|
|
318
|
+
* agentWallet?: string,
|
|
319
|
+
* identityQHash?: string,
|
|
320
|
+
* signal?: AbortSignal
|
|
321
|
+
* }} input
|
|
322
|
+
*/
|
|
323
|
+
export async function resolveRuntimeBundleFromMcp(input) {
|
|
324
|
+
const accessKey = asString(input.accessKey);
|
|
325
|
+
if (!accessKey) {
|
|
326
|
+
throw new Error('NEUS access key or authenticated MCP session is required for runtime mount.');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const selector = {
|
|
330
|
+
agentId: input.agentId,
|
|
331
|
+
agentWallet: input.agentWallet,
|
|
332
|
+
identityQHash: input.identityQHash
|
|
333
|
+
};
|
|
334
|
+
if (!selector.agentId && !selector.agentWallet && !selector.identityQHash) {
|
|
335
|
+
throw new Error('Provide agentId, agentWallet, or identityQHash.');
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let sessionId = '';
|
|
339
|
+
if (input.initializeMcp) {
|
|
340
|
+
const init = await input.initializeMcp();
|
|
341
|
+
sessionId = init.sessionId || '';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const mountArgs = {
|
|
345
|
+
...(selector.agentId ? { agentId: selector.agentId } : {}),
|
|
346
|
+
...(selector.agentWallet ? { agentWallet: selector.agentWallet } : {}),
|
|
347
|
+
...(selector.identityQHash ? { identityQHash: selector.identityQHash } : {})
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const serverMount = await input.callMcpTool({
|
|
351
|
+
name: 'neus_agent_mount',
|
|
352
|
+
args: mountArgs,
|
|
353
|
+
accessKey,
|
|
354
|
+
sessionId,
|
|
355
|
+
signal: input.signal
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (serverMount.ok) {
|
|
359
|
+
const payload = serverMount.payload;
|
|
360
|
+
if (isRuntimeBundle(payload)) {
|
|
361
|
+
return /** @type {import('./runtime-mount.js').RuntimeBundle} */ (payload);
|
|
362
|
+
}
|
|
363
|
+
if (payload && typeof payload === 'object' && isRuntimeBundle(payload.data)) {
|
|
364
|
+
return /** @type {import('./runtime-mount.js').RuntimeBundle} */ (payload.data);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const me = await input.callMcpTool({
|
|
369
|
+
name: 'neus_me',
|
|
370
|
+
args: {},
|
|
371
|
+
accessKey,
|
|
372
|
+
sessionId,
|
|
373
|
+
signal: input.signal
|
|
374
|
+
});
|
|
375
|
+
if (!me.ok) {
|
|
376
|
+
throw new Error(me.error || 'Could not load profile context. Run `neus auth` and retry.');
|
|
377
|
+
}
|
|
378
|
+
const mePayload = /** @type {Record<string, unknown>} */ (me.payload || {});
|
|
379
|
+
if (mePayload.status === 'auth_required') {
|
|
380
|
+
throw new Error('Profile authentication required. Run `neus auth` or set NEUS_ACCESS_KEY.');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const principal = /** @type {Record<string, unknown>} */ (mePayload.principal || {});
|
|
384
|
+
const controllerWallet = normalizeWallet(principal.primaryAccount);
|
|
385
|
+
const profileAgents = Array.isArray(mePayload.agents) ? mePayload.agents : [];
|
|
386
|
+
|
|
387
|
+
let agentWallet = normalizeWallet(selector.agentWallet);
|
|
388
|
+
let agentId = asString(selector.agentId);
|
|
389
|
+
|
|
390
|
+
if (!agentWallet && agentId) {
|
|
391
|
+
const row = profileAgents.find(
|
|
392
|
+
row => asString(row.agentId).toLowerCase() === agentId.toLowerCase()
|
|
393
|
+
);
|
|
394
|
+
if (row) {
|
|
395
|
+
agentWallet = normalizeWallet(row.agentWallet);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (!agentId && agentWallet) {
|
|
399
|
+
const row = profileAgents.find(row => normalizeWallet(row.agentWallet) === agentWallet);
|
|
400
|
+
if (row) agentId = asString(row.agentId);
|
|
401
|
+
}
|
|
402
|
+
if (!agentWallet && selector.identityQHash) {
|
|
403
|
+
const idProof = await input.callMcpTool({
|
|
404
|
+
name: 'neus_proofs_get',
|
|
405
|
+
args: { qHash: selector.identityQHash, verifierId: 'agent-identity' },
|
|
406
|
+
accessKey,
|
|
407
|
+
sessionId,
|
|
408
|
+
signal: input.signal
|
|
409
|
+
});
|
|
410
|
+
if (idProof.ok) {
|
|
411
|
+
const data = /** @type {Record<string, unknown>} */ (idProof.payload?.data || idProof.payload || {});
|
|
412
|
+
const proofs = Array.isArray(data.proofs) ? data.proofs : [];
|
|
413
|
+
const extracted = extractAgentContextFromProofs(proofs);
|
|
414
|
+
const identity = pickIdentity(extracted.identities, selector);
|
|
415
|
+
if (identity) {
|
|
416
|
+
agentWallet = normalizeWallet(identity.agentWallet);
|
|
417
|
+
agentId = asString(identity.agentId);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (!agentWallet) {
|
|
423
|
+
throw new Error('Could not resolve agent wallet. Check agentId or link the agent on your profile.');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const [identityPage, delegationPage] = await Promise.all([
|
|
427
|
+
input.callMcpTool({
|
|
428
|
+
name: 'neus_proofs_get',
|
|
429
|
+
args: { identifier: agentWallet, verifierId: 'agent-identity', limit: 25 },
|
|
430
|
+
accessKey,
|
|
431
|
+
sessionId,
|
|
432
|
+
signal: input.signal
|
|
433
|
+
}),
|
|
434
|
+
controllerWallet
|
|
435
|
+
? input.callMcpTool({
|
|
436
|
+
name: 'neus_proofs_get',
|
|
437
|
+
args: { identifier: controllerWallet, verifierId: 'agent-delegation', limit: 50 },
|
|
438
|
+
accessKey,
|
|
439
|
+
sessionId,
|
|
440
|
+
signal: input.signal
|
|
441
|
+
})
|
|
442
|
+
: Promise.resolve({ ok: false })
|
|
443
|
+
]);
|
|
444
|
+
|
|
445
|
+
const identityProofs = identityPage.ok
|
|
446
|
+
? /** @type {unknown[]} */ (
|
|
447
|
+
identityPage.payload?.data?.proofs || identityPage.payload?.proofs || []
|
|
448
|
+
)
|
|
449
|
+
: [];
|
|
450
|
+
const delegationProofs = delegationPage.ok
|
|
451
|
+
? /** @type {unknown[]} */ (
|
|
452
|
+
delegationPage.payload?.data?.proofs || delegationPage.payload?.proofs || []
|
|
453
|
+
)
|
|
454
|
+
: [];
|
|
455
|
+
|
|
456
|
+
const idCtx = extractAgentContextFromProofs(identityProofs);
|
|
457
|
+
const delCtx = extractAgentContextFromProofs(delegationProofs);
|
|
458
|
+
|
|
459
|
+
let identity = pickIdentity(idCtx.identities, { ...selector, agentId, agentWallet });
|
|
460
|
+
if (!identity && profileAgents.length > 0) {
|
|
461
|
+
const row = profileAgents.find(
|
|
462
|
+
a =>
|
|
463
|
+
asString(a.agentId).toLowerCase() === agentId.toLowerCase() ||
|
|
464
|
+
normalizeWallet(a.agentWallet) === agentWallet
|
|
465
|
+
);
|
|
466
|
+
if (row) {
|
|
467
|
+
identity = { ...profileAgentToIdentitySeed(row), agentWallet, agentId: agentId || asString(row.agentId) };
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (!identity) {
|
|
471
|
+
throw new Error('Agent identity proof not found. Complete agent setup on neus.network first.');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const delegation = pickActiveDelegation(
|
|
475
|
+
delCtx.delegations,
|
|
476
|
+
controllerWallet,
|
|
477
|
+
agentWallet,
|
|
478
|
+
agentId || asString(identity.agentId)
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
return buildRuntimeBundle({
|
|
482
|
+
identity,
|
|
483
|
+
delegation,
|
|
484
|
+
identityQHash: asString(identity.qHash || selector.identityQHash),
|
|
485
|
+
delegationQHash: delegation ? asString(delegation.qHash) : null,
|
|
486
|
+
tools: [],
|
|
487
|
+
secretBindings: []
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* @param {import('./runtime-mount.js').RuntimeBundle | Record<string, unknown> | null | undefined} manifest
|
|
493
|
+
*/
|
|
494
|
+
export function evaluateMountFileHealth(manifest) {
|
|
495
|
+
if (!manifest || manifest.schema !== RUNTIME_MOUNT_SCHEMA) {
|
|
496
|
+
return {
|
|
497
|
+
mountFileValid: false,
|
|
498
|
+
missingDelegation: true,
|
|
499
|
+
delegationExpired: false,
|
|
500
|
+
needsRefresh: true,
|
|
501
|
+
reason: 'missing_or_invalid'
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const delegationQHash = asString(manifest.trust?.delegationQHash);
|
|
506
|
+
const missingDelegation = !delegationQHash;
|
|
507
|
+
const expiresAt = manifest.delegation?.expiresAt;
|
|
508
|
+
const delegationExpired =
|
|
509
|
+
Boolean(manifest.delegation?.isExpired) || isDelegationExpired(expiresAt);
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
mountFileValid: true,
|
|
513
|
+
missingDelegation,
|
|
514
|
+
delegationExpired,
|
|
515
|
+
needsRefresh: missingDelegation || delegationExpired,
|
|
516
|
+
reason: delegationExpired
|
|
517
|
+
? 'delegation_expired'
|
|
518
|
+
: missingDelegation
|
|
519
|
+
? 'delegation_missing'
|
|
520
|
+
: null
|
|
521
|
+
};
|
|
522
|
+
}
|
package/types.d.ts
CHANGED
|
@@ -1170,3 +1170,92 @@ declare module '@neus/sdk/mcp-hosts' {
|
|
|
1170
1170
|
export function buildSetupCommandForHost(host: McpInstallHost, accessKey?: string | null): string;
|
|
1171
1171
|
export function supportsMcpInstallDeeplink(host: McpInstallHost): boolean;
|
|
1172
1172
|
}
|
|
1173
|
+
|
|
1174
|
+
declare module '@neus/sdk/runtime-mount' {
|
|
1175
|
+
export const RUNTIME_MOUNT_SCHEMA: 'neus.runtime-mount.v1';
|
|
1176
|
+
|
|
1177
|
+
export interface RuntimeBundleTrust {
|
|
1178
|
+
identityQHash?: string;
|
|
1179
|
+
delegationQHash?: string;
|
|
1180
|
+
identityUrl?: string;
|
|
1181
|
+
delegationUrl?: string;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
export interface RuntimeBundleIdentity {
|
|
1185
|
+
agentId: string;
|
|
1186
|
+
agentWallet: string;
|
|
1187
|
+
displayName?: string;
|
|
1188
|
+
capabilities?: string[];
|
|
1189
|
+
identityQHash?: string;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
export interface RuntimeBundleDelegation {
|
|
1193
|
+
delegationQHash?: string;
|
|
1194
|
+
controllerWallet?: string;
|
|
1195
|
+
allowedActions?: string[];
|
|
1196
|
+
deniedActions?: string[];
|
|
1197
|
+
expiresAt?: number | null;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
export interface RuntimeBundle {
|
|
1201
|
+
schema: typeof RUNTIME_MOUNT_SCHEMA;
|
|
1202
|
+
mountedAt: string;
|
|
1203
|
+
trust: RuntimeBundleTrust;
|
|
1204
|
+
identity: RuntimeBundleIdentity;
|
|
1205
|
+
delegation: RuntimeBundleDelegation;
|
|
1206
|
+
effectiveRuntime?: Record<string, unknown>;
|
|
1207
|
+
tools?: string[];
|
|
1208
|
+
secretBindings?: Array<Record<string, unknown>>;
|
|
1209
|
+
enforce?: Record<string, unknown>;
|
|
1210
|
+
contextPack?: Record<string, unknown>;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
export function normalizeWallet(value: string | null | undefined): string;
|
|
1214
|
+
export function isDelegationExpired(expiresAt: number | null | undefined): boolean;
|
|
1215
|
+
export function pickIdentity(
|
|
1216
|
+
identities: Array<Record<string, unknown>>,
|
|
1217
|
+
selector: { agentId?: string; agentWallet?: string; identityQHash?: string },
|
|
1218
|
+
): Record<string, unknown> | null;
|
|
1219
|
+
export function pickActiveDelegation(
|
|
1220
|
+
delegations: Array<Record<string, unknown>>,
|
|
1221
|
+
controllerWallet: string,
|
|
1222
|
+
agentWallet: string,
|
|
1223
|
+
agentId: string,
|
|
1224
|
+
): Record<string, unknown> | null;
|
|
1225
|
+
export function extractAgentContextFromProofs(proofs: unknown): {
|
|
1226
|
+
identities: Array<Record<string, unknown>>;
|
|
1227
|
+
delegations: Array<Record<string, unknown>>;
|
|
1228
|
+
};
|
|
1229
|
+
export function buildRuntimeBundle(input: Record<string, unknown>): RuntimeBundle;
|
|
1230
|
+
export function resolveRuntimeBundleFromMcp(
|
|
1231
|
+
mcpClient: { callTool: (name: string, args?: Record<string, unknown>) => Promise<unknown> },
|
|
1232
|
+
selector: { agentId?: string; agentWallet?: string; identityQHash?: string }
|
|
1233
|
+
): Promise<RuntimeBundle>;
|
|
1234
|
+
|
|
1235
|
+
export function evaluateMountFileHealth(
|
|
1236
|
+
manifest: RuntimeBundle | Record<string, unknown> | null | undefined
|
|
1237
|
+
): {
|
|
1238
|
+
mountFileValid: boolean;
|
|
1239
|
+
missingDelegation: boolean;
|
|
1240
|
+
delegationExpired: boolean;
|
|
1241
|
+
needsRefresh: boolean;
|
|
1242
|
+
reason: string | null;
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
declare module '@neus/sdk/runtime-adapters' {
|
|
1247
|
+
import type { RuntimeBundle } from '@neus/sdk/runtime-mount';
|
|
1248
|
+
|
|
1249
|
+
export type RuntimeAdapterHost = 'cursor' | 'claude' | 'codex';
|
|
1250
|
+
|
|
1251
|
+
export interface ApplyRuntimeBundleResult {
|
|
1252
|
+
mountPath: string;
|
|
1253
|
+
adapterFiles: string[];
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
export function applyRuntimeBundle(
|
|
1257
|
+
host: RuntimeAdapterHost,
|
|
1258
|
+
bundle: RuntimeBundle,
|
|
1259
|
+
projectRoot: string
|
|
1260
|
+
): Promise<ApplyRuntimeBundleResult>;
|
|
1261
|
+
}
|