@agentvault/agentvault 0.17.3 → 0.17.5

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 (62) hide show
  1. package/dist/cli.js +141 -4
  2. package/dist/cli.js.map +4 -4
  3. package/dist/index.js +159 -16
  4. package/dist/index.js.map +4 -4
  5. package/dist/openclaw-entry.js +598 -142
  6. package/dist/openclaw-entry.js.map +4 -4
  7. package/openclaw.plugin.json +1 -1
  8. package/package.json +1 -1
  9. package/dist/_cp.d.ts +0 -10
  10. package/dist/_cp.d.ts.map +0 -1
  11. package/dist/account-config.d.ts +0 -20
  12. package/dist/account-config.d.ts.map +0 -1
  13. package/dist/channel.d.ts +0 -350
  14. package/dist/channel.d.ts.map +0 -1
  15. package/dist/cli.d.ts +0 -2
  16. package/dist/cli.d.ts.map +0 -1
  17. package/dist/create-agent.d.ts +0 -28
  18. package/dist/create-agent.d.ts.map +0 -1
  19. package/dist/crypto-helpers.d.ts +0 -2
  20. package/dist/crypto-helpers.d.ts.map +0 -1
  21. package/dist/doctor.d.ts +0 -41
  22. package/dist/doctor.d.ts.map +0 -1
  23. package/dist/fetch-interceptor.d.ts +0 -32
  24. package/dist/fetch-interceptor.d.ts.map +0 -1
  25. package/dist/gateway-send.d.ts +0 -98
  26. package/dist/gateway-send.d.ts.map +0 -1
  27. package/dist/http-handlers.d.ts +0 -42
  28. package/dist/http-handlers.d.ts.map +0 -1
  29. package/dist/index.d.ts +0 -25
  30. package/dist/index.d.ts.map +0 -1
  31. package/dist/mcp-handlers.d.ts +0 -26
  32. package/dist/mcp-handlers.d.ts.map +0 -1
  33. package/dist/mcp-server.d.ts +0 -88
  34. package/dist/mcp-server.d.ts.map +0 -1
  35. package/dist/openclaw-compat.d.ts +0 -33
  36. package/dist/openclaw-compat.d.ts.map +0 -1
  37. package/dist/openclaw-entry.d.ts +0 -27
  38. package/dist/openclaw-entry.d.ts.map +0 -1
  39. package/dist/openclaw-plugin.d.ts +0 -102
  40. package/dist/openclaw-plugin.d.ts.map +0 -1
  41. package/dist/openclaw-types.d.ts +0 -155
  42. package/dist/openclaw-types.d.ts.map +0 -1
  43. package/dist/policy-enforcer.d.ts +0 -78
  44. package/dist/policy-enforcer.d.ts.map +0 -1
  45. package/dist/setup.d.ts +0 -27
  46. package/dist/setup.d.ts.map +0 -1
  47. package/dist/skill-invoker.d.ts +0 -30
  48. package/dist/skill-invoker.d.ts.map +0 -1
  49. package/dist/skill-manifest.d.ts +0 -25
  50. package/dist/skill-manifest.d.ts.map +0 -1
  51. package/dist/skill-telemetry.d.ts +0 -36
  52. package/dist/skill-telemetry.d.ts.map +0 -1
  53. package/dist/skills-publish.d.ts +0 -8
  54. package/dist/skills-publish.d.ts.map +0 -1
  55. package/dist/state.d.ts +0 -32
  56. package/dist/state.d.ts.map +0 -1
  57. package/dist/transport.d.ts +0 -24
  58. package/dist/transport.d.ts.map +0 -1
  59. package/dist/types.d.ts +0 -379
  60. package/dist/types.d.ts.map +0 -1
  61. package/dist/workspace-handlers.d.ts +0 -62
  62. package/dist/workspace-handlers.d.ts.map +0 -1
@@ -14,6 +14,178 @@ var __export = (target, all) => {
14
14
  __defProp(target, name, { get: all[name], enumerable: true });
15
15
  };
16
16
 
17
+ // src/http-handlers.ts
18
+ var http_handlers_exports = {};
19
+ __export(http_handlers_exports, {
20
+ handleActionRequest: () => handleActionRequest,
21
+ handleDecisionRequest: () => handleDecisionRequest,
22
+ handleMcpConfigRequest: () => handleMcpConfigRequest,
23
+ handleSendRequest: () => handleSendRequest,
24
+ handleStatusRequest: () => handleStatusRequest,
25
+ handleTargetsRequest: () => handleTargetsRequest
26
+ });
27
+ async function handleSendRequest(parsed, channel) {
28
+ const text = parsed.text;
29
+ if (!text || typeof text !== "string") {
30
+ return { status: 400, body: { ok: false, error: "Missing 'text' field" } };
31
+ }
32
+ try {
33
+ let target;
34
+ let a2aTarget = parsed.hub_address ?? parsed.a2a_address ?? parsed.a2aAddress;
35
+ if (!a2aTarget && parsed.channel_id && typeof parsed.channel_id === "string") {
36
+ const hubAddr = channel.resolveA2AChannelHub(parsed.channel_id);
37
+ if (hubAddr) a2aTarget = hubAddr;
38
+ }
39
+ if (a2aTarget && typeof a2aTarget === "string") {
40
+ target = { kind: "a2a", hubAddress: a2aTarget };
41
+ } else if (typeof parsed.room_id === "string") {
42
+ target = { kind: "room", roomId: parsed.room_id };
43
+ } else if (parsed.target === "context") {
44
+ target = { kind: "context" };
45
+ } else if (parsed.file_path && typeof parsed.file_path === "string") {
46
+ const receipt2 = await channel.deliver(
47
+ { kind: "owner" },
48
+ { type: "attachment", text, filePath: parsed.file_path },
49
+ { topicId: parsed.topicId }
50
+ );
51
+ return {
52
+ status: receipt2.ok ? 200 : 500,
53
+ body: {
54
+ ok: receipt2.ok,
55
+ destination: receipt2.destination,
56
+ ...receipt2.error ? { error: receipt2.error } : {}
57
+ }
58
+ };
59
+ } else {
60
+ target = { kind: "owner" };
61
+ }
62
+ const receipt = await channel.deliver(
63
+ target,
64
+ { type: "text", text },
65
+ {
66
+ topicId: parsed.topicId,
67
+ priority: parsed.priority,
68
+ metadata: {
69
+ ...parsed.metadata,
70
+ ...parsed.message_type ? { message_type: parsed.message_type } : {}
71
+ }
72
+ }
73
+ );
74
+ return {
75
+ status: receipt.ok ? 200 : 500,
76
+ body: {
77
+ ok: receipt.ok,
78
+ destination: receipt.destination,
79
+ ...receipt.error ? { error: receipt.error } : {}
80
+ }
81
+ };
82
+ } catch (err) {
83
+ return { status: 500, body: { ok: false, error: String(err) } };
84
+ }
85
+ }
86
+ async function handleActionRequest(parsed, channel) {
87
+ if (!parsed.action || typeof parsed.action !== "string") {
88
+ return { status: 400, body: { ok: false, error: "Missing 'action' field" } };
89
+ }
90
+ try {
91
+ const confirmation = {
92
+ action: parsed.action,
93
+ status: parsed.status ?? "completed",
94
+ decisionId: parsed.decision_id,
95
+ detail: parsed.detail,
96
+ estimated_cost: parsed.estimated_cost
97
+ };
98
+ const target = parsed.room_id && typeof parsed.room_id === "string" ? { kind: "room", roomId: parsed.room_id } : { kind: "owner" };
99
+ const receipt = await channel.deliver(
100
+ target,
101
+ { type: "action_confirmation", confirmation }
102
+ );
103
+ return {
104
+ status: receipt.ok ? 200 : 500,
105
+ body: {
106
+ ok: receipt.ok,
107
+ destination: receipt.destination,
108
+ ...receipt.error ? { error: receipt.error } : {}
109
+ }
110
+ };
111
+ } catch (err) {
112
+ return { status: 500, body: { ok: false, error: String(err) } };
113
+ }
114
+ }
115
+ async function handleDecisionRequest(parsed, channel) {
116
+ const title = parsed.title;
117
+ if (!title || typeof title !== "string") {
118
+ return { status: 400, body: { ok: false, error: "Missing 'title' field" } };
119
+ }
120
+ const options = parsed.options;
121
+ if (!Array.isArray(options) || options.length < 2) {
122
+ return { status: 400, body: { ok: false, error: "'options' must be an array with at least 2 items" } };
123
+ }
124
+ for (const opt of options) {
125
+ if (!opt || typeof opt !== "object" || !opt.option_id || !opt.label) {
126
+ return { status: 400, body: { ok: false, error: "Each option must have 'option_id' and 'label'" } };
127
+ }
128
+ }
129
+ try {
130
+ const decision_id = await channel.sendDecisionRequest({
131
+ title,
132
+ description: parsed.description,
133
+ options,
134
+ context_refs: parsed.context_refs,
135
+ deadline: parsed.deadline,
136
+ auto_action: parsed.auto_action
137
+ });
138
+ return { status: 200, body: { ok: true, decision_id } };
139
+ } catch (err) {
140
+ return { status: 500, body: { ok: false, error: String(err) } };
141
+ }
142
+ }
143
+ function handleStatusRequest(channel) {
144
+ return {
145
+ status: 200,
146
+ body: {
147
+ ok: true,
148
+ state: channel.state,
149
+ deviceId: channel.deviceId ?? void 0,
150
+ sessions: channel.sessionCount
151
+ }
152
+ };
153
+ }
154
+ function handleTargetsRequest(channel) {
155
+ return {
156
+ status: 200,
157
+ body: {
158
+ ok: true,
159
+ targets: channel.listTargets(),
160
+ context: channel.lastInboundRoomId ? { kind: "room", roomId: channel.lastInboundRoomId } : { kind: "owner" }
161
+ }
162
+ };
163
+ }
164
+ function handleMcpConfigRequest(agentName, port, mcpSkillCount) {
165
+ return {
166
+ status: 200,
167
+ body: {
168
+ mcpServers: {
169
+ [`agentvault-${agentName}`]: {
170
+ url: `http://127.0.0.1:${port}/mcp`
171
+ }
172
+ },
173
+ _meta: {
174
+ agent: agentName,
175
+ skills_count: mcpSkillCount,
176
+ transport: "streamable-http",
177
+ auth: "none (localhost)",
178
+ instructions: "Add the mcpServers block to your MCP configuration file (e.g., .mcp.json or mcp_config.json)"
179
+ }
180
+ }
181
+ };
182
+ }
183
+ var init_http_handlers = __esm({
184
+ "src/http-handlers.ts"() {
185
+ "use strict";
186
+ }
187
+ });
188
+
17
189
  // src/openclaw-compat.ts
18
190
  var openclaw_compat_exports = {};
19
191
  __export(openclaw_compat_exports, {
@@ -90,8 +262,385 @@ var init_openclaw_compat = __esm({
90
262
  }
91
263
  });
92
264
 
265
+ // src/mcp-server.ts
266
+ var mcp_server_exports = {};
267
+ __export(mcp_server_exports, {
268
+ AgentVaultMcpServer: () => AgentVaultMcpServer
269
+ });
270
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
271
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
272
+ var AgentVaultMcpServer;
273
+ var init_mcp_server = __esm({
274
+ "src/mcp-server.ts"() {
275
+ "use strict";
276
+ AgentVaultMcpServer = class {
277
+ server;
278
+ skills = /* @__PURE__ */ new Map();
279
+ opts;
280
+ initialized = false;
281
+ constructor(opts) {
282
+ this.opts = opts;
283
+ this.server = new McpServer({
284
+ name: `agentvault-${opts.agentName}`,
285
+ version: "1.0.0"
286
+ });
287
+ }
288
+ /* ---- Skill registration ------------------------------------------------ */
289
+ /**
290
+ * Register a skill that will be exposed as an MCP tool.
291
+ * Must be called *before* `initialize()`.
292
+ */
293
+ registerSkill(skill) {
294
+ this.skills.set(skill.name, skill);
295
+ }
296
+ /**
297
+ * Register all skills as MCP tools / resources / prompts.
298
+ * Called lazily on first request if not called explicitly.
299
+ */
300
+ initialize() {
301
+ if (this.initialized) return;
302
+ for (const [name, skill] of this.skills) {
303
+ this.registerToolForSkill(name, skill);
304
+ }
305
+ this.server.resource(
306
+ "skills-registry",
307
+ "skills://registry",
308
+ { description: "List of all registered agent skills", mimeType: "application/json" },
309
+ async () => {
310
+ const registry = Array.from(this.skills.values()).map((s) => ({
311
+ name: s.name,
312
+ version: s.version,
313
+ description: s.description,
314
+ tags: s.tags,
315
+ sla: s.slaDefinition,
316
+ hasSchema: !!s.inputSchema,
317
+ hasInstructions: !!s.instructions,
318
+ certificationTier: s.certificationTier,
319
+ modelRouting: s.modelRouting,
320
+ allowedModels: s.allowedModels,
321
+ hasToolPolicy: !!(s.toolsAllowed || s.toolsDenied),
322
+ hasOutputSchema: !!s.outputSchema,
323
+ requiredPolicies: s.requiredPolicies
324
+ }));
325
+ return {
326
+ contents: [{
327
+ uri: "skills://registry",
328
+ mimeType: "application/json",
329
+ text: JSON.stringify(registry, null, 2)
330
+ }]
331
+ };
332
+ }
333
+ );
334
+ for (const [name, skill] of this.skills) {
335
+ if (skill.instructions) {
336
+ this.server.prompt(
337
+ name,
338
+ skill.description ?? `Instructions for ${name}`,
339
+ () => ({
340
+ messages: [{
341
+ role: "user",
342
+ content: { type: "text", text: skill.instructions }
343
+ }]
344
+ })
345
+ );
346
+ }
347
+ }
348
+ this.initialized = true;
349
+ }
350
+ /* ---- HTTP request handler ---------------------------------------------- */
351
+ /**
352
+ * Handle an incoming HTTP request to the /mcp endpoint.
353
+ *
354
+ * Supports the Streamable HTTP transport protocol:
355
+ * - POST for JSON-RPC messages
356
+ * - GET for SSE notification stream
357
+ * - DELETE for session close
358
+ *
359
+ * Each request gets a fresh stateless transport; after the response is
360
+ * flushed the transport + underlying protocol connection are torn down
361
+ * so the single McpServer instance is ready for the next caller.
362
+ *
363
+ * Local requests from 127.0.0.1/::1 bypass SPT validation (owner access).
364
+ */
365
+ async handleRequest(req, res) {
366
+ if (!this.initialized) {
367
+ this.initialize();
368
+ }
369
+ const remote = req.socket?.remoteAddress;
370
+ const isLocal = remote === "127.0.0.1" || remote === "::1" || remote === "::ffff:127.0.0.1";
371
+ if (!isLocal) {
372
+ const authHeader = req.headers.authorization;
373
+ if (!authHeader?.startsWith("Bearer ")) {
374
+ res.writeHead(401, { "Content-Type": "application/json" });
375
+ res.end(JSON.stringify({ error: "Missing or invalid Authorization header" }));
376
+ return;
377
+ }
378
+ const token = authHeader.slice(7);
379
+ const valid = await this.validateSpt(token);
380
+ if (!valid) {
381
+ res.writeHead(403, { "Content-Type": "application/json" });
382
+ res.end(JSON.stringify({ error: "Invalid or expired SPT token" }));
383
+ return;
384
+ }
385
+ }
386
+ const transport = new StreamableHTTPServerTransport({
387
+ sessionIdGenerator: void 0
388
+ // stateless
389
+ });
390
+ await this.server.connect(transport);
391
+ try {
392
+ await transport.handleRequest(req, res);
393
+ } finally {
394
+ await transport.close();
395
+ }
396
+ }
397
+ /* ---- Private helpers --------------------------------------------------- */
398
+ /**
399
+ * Register a single skill as an MCP tool.
400
+ *
401
+ * The MCP SDK's `tool()` overloads that accept a schema expect Zod types.
402
+ * Since our skill definitions use raw JSON Schema we register without
403
+ * schema validation (name + description + handler) and let the handler
404
+ * receive the raw args object.
405
+ */
406
+ registerToolForSkill(name, skill) {
407
+ const description = skill.description ?? `AgentVault skill: ${name}`;
408
+ this.server.tool(
409
+ name,
410
+ description,
411
+ async (args) => {
412
+ if (!this.opts.onInvoke) {
413
+ return {
414
+ content: [{ type: "text", text: `Skill "${name}" invoked but no handler registered` }]
415
+ };
416
+ }
417
+ try {
418
+ const result = await this.opts.onInvoke(name, args);
419
+ const text = typeof result === "string" ? result : JSON.stringify(result, null, 2);
420
+ return {
421
+ content: [{ type: "text", text }]
422
+ };
423
+ } catch (err) {
424
+ const message = err instanceof Error ? err.message : String(err);
425
+ return {
426
+ content: [{ type: "text", text: `Error invoking skill "${name}": ${message}` }],
427
+ isError: true
428
+ };
429
+ }
430
+ }
431
+ );
432
+ }
433
+ /**
434
+ * Validate a Service Provider Token against the AgentVault backend.
435
+ */
436
+ async validateSpt(token) {
437
+ try {
438
+ const url = `${this.opts.apiUrl}/api/v1/capabilities/introspect`;
439
+ const headers = {
440
+ "Content-Type": "application/json"
441
+ };
442
+ if (this.opts.apiKey) {
443
+ headers["Authorization"] = `Bearer ${this.opts.apiKey}`;
444
+ }
445
+ const resp = await fetch(url, {
446
+ method: "POST",
447
+ headers,
448
+ body: JSON.stringify({ token })
449
+ });
450
+ if (!resp.ok) return false;
451
+ const data = await resp.json();
452
+ return data.active === true;
453
+ } catch {
454
+ return false;
455
+ }
456
+ }
457
+ /* ---- Accessors --------------------------------------------------------- */
458
+ get skillCount() {
459
+ return this.skills.size;
460
+ }
461
+ get isInitialized() {
462
+ return this.initialized;
463
+ }
464
+ };
465
+ }
466
+ });
467
+
468
+ // src/skill-manifest.ts
469
+ var skill_manifest_exports = {};
470
+ __export(skill_manifest_exports, {
471
+ loadSkillsFromApi: () => loadSkillsFromApi,
472
+ loadSkillsFromDirectory: () => loadSkillsFromDirectory,
473
+ mergeSkills: () => mergeSkills,
474
+ parseSkillMd: () => parseSkillMd
475
+ });
476
+ import { readFileSync, existsSync, readdirSync } from "node:fs";
477
+ import { resolve, join } from "node:path";
478
+ function parseSkillMd(content) {
479
+ const lines = content.split("\n");
480
+ if (lines[0]?.trim() !== "---") return null;
481
+ let endIdx = -1;
482
+ for (let i = 1; i < lines.length; i++) {
483
+ if (lines[i]?.trim() === "---") {
484
+ endIdx = i;
485
+ break;
486
+ }
487
+ }
488
+ if (endIdx === -1) return null;
489
+ const frontmatterLines = lines.slice(1, endIdx);
490
+ const frontmatter = parseSimpleYaml(frontmatterLines.join("\n"));
491
+ if (!frontmatter.name) return null;
492
+ const instructionLines = lines.slice(endIdx + 1);
493
+ const instructions = instructionLines.join("\n").trim();
494
+ const skill = {
495
+ name: frontmatter.name,
496
+ version: frontmatter.version,
497
+ description: frontmatter.description,
498
+ tags: frontmatter.tags,
499
+ inputSchema: frontmatter.schema,
500
+ slaDefinition: frontmatter.sla,
501
+ instructions: instructions || void 0
502
+ };
503
+ if (frontmatter.agentVault) {
504
+ const av = frontmatter.agentVault;
505
+ if (av.certification) skill.certificationTier = av.certification;
506
+ if (av.runtime?.capabilities) skill.toolsAllowed = av.runtime.capabilities;
507
+ if (av.runtime?.forbidden) skill.toolsDenied = av.runtime.forbidden;
508
+ if (av.runtime?.output_schema) skill.outputSchema = av.runtime.output_schema;
509
+ if (av.model?.routing) skill.modelRouting = av.model.routing;
510
+ if (av.model?.allowed) skill.allowedModels = av.model.allowed;
511
+ if (av.model?.default) skill.defaultModel = av.model.default;
512
+ if (av.integrity) skill.integrity = av.integrity;
513
+ if (av.requiredPolicies) skill.requiredPolicies = av.requiredPolicies;
514
+ }
515
+ return skill;
516
+ }
517
+ function parseSimpleYaml(yaml) {
518
+ const result = {};
519
+ const lines = yaml.split("\n");
520
+ const stack = [];
521
+ let currentObj = result;
522
+ function parseValue(raw) {
523
+ const value = raw.replace(/^["']|["']$/g, "");
524
+ const num = Number(value);
525
+ if (!isNaN(num) && value !== "") return num;
526
+ if (value === "true") return true;
527
+ if (value === "false") return false;
528
+ return value;
529
+ }
530
+ for (const line of lines) {
531
+ const trimmed = line.trim();
532
+ if (!trimmed || trimmed.startsWith("#")) continue;
533
+ const indent = line.length - line.trimStart().length;
534
+ while (stack.length > 0 && indent <= stack[stack.length - 1].indent) {
535
+ const popped = stack.pop();
536
+ currentObj = stack.length > 0 ? stack[stack.length - 1].obj : result;
537
+ currentObj[popped.key] = popped.obj;
538
+ }
539
+ const inlineArrayMatch = trimmed.match(/^(\w[\w_-]*)\s*:\s*\[(.+)\]$/);
540
+ if (inlineArrayMatch) {
541
+ const key = inlineArrayMatch[1];
542
+ const values = inlineArrayMatch[2].split(",").map((v) => v.trim().replace(/^["']|["']$/g, ""));
543
+ if (stack.length > 0) {
544
+ stack[stack.length - 1].obj[key] = values;
545
+ } else {
546
+ currentObj[key] = values;
547
+ }
548
+ continue;
549
+ }
550
+ const kvMatch = trimmed.match(/^(\w[\w_-]*)\s*:\s*(.+)$/);
551
+ if (kvMatch) {
552
+ const key = kvMatch[1];
553
+ const val = parseValue(kvMatch[2]);
554
+ if (stack.length > 0) {
555
+ stack[stack.length - 1].obj[key] = val;
556
+ } else {
557
+ currentObj[key] = val;
558
+ }
559
+ continue;
560
+ }
561
+ const nestedMatch = trimmed.match(/^(\w[\w_-]*)\s*:$/);
562
+ if (nestedMatch) {
563
+ const key = nestedMatch[1];
564
+ const newObj = {};
565
+ stack.push({ key, obj: newObj, indent });
566
+ continue;
567
+ }
568
+ }
569
+ while (stack.length > 0) {
570
+ const popped = stack.pop();
571
+ const parent = stack.length > 0 ? stack[stack.length - 1].obj : result;
572
+ parent[popped.key] = popped.obj;
573
+ }
574
+ return result;
575
+ }
576
+ function loadSkillsFromDirectory(dir) {
577
+ const skills = [];
578
+ const absDir = resolve(dir);
579
+ if (!existsSync(absDir)) return skills;
580
+ const rootSkill = join(absDir, "SKILL.md");
581
+ if (existsSync(rootSkill)) {
582
+ const content = readFileSync(rootSkill, "utf-8");
583
+ const skill = parseSkillMd(content);
584
+ if (skill) skills.push(skill);
585
+ }
586
+ try {
587
+ const entries = readdirSync(absDir, { withFileTypes: true });
588
+ for (const entry of entries) {
589
+ if (entry.isDirectory()) {
590
+ const subSkill = join(absDir, entry.name, "SKILL.md");
591
+ if (existsSync(subSkill)) {
592
+ const content = readFileSync(subSkill, "utf-8");
593
+ const skill = parseSkillMd(content);
594
+ if (skill) skills.push(skill);
595
+ }
596
+ }
597
+ }
598
+ } catch {
599
+ }
600
+ return skills;
601
+ }
602
+ async function loadSkillsFromApi(apiUrl, apiKey, hubId) {
603
+ try {
604
+ const res = await fetch(`${apiUrl}/api/v1/hub/identities/${hubId}/skills`, {
605
+ headers: { Authorization: `Bearer ${apiKey}` }
606
+ });
607
+ if (!res.ok) return [];
608
+ const data = await res.json();
609
+ return data.map((cap) => ({
610
+ name: cap.capability_name,
611
+ version: cap.capability_version,
612
+ description: cap.description,
613
+ inputSchema: cap.schema_definition,
614
+ slaDefinition: cap.sla_definition,
615
+ tags: cap.tags,
616
+ instructions: cap.instructions
617
+ }));
618
+ } catch {
619
+ return [];
620
+ }
621
+ }
622
+ function mergeSkills(local, remote) {
623
+ const byName = /* @__PURE__ */ new Map();
624
+ for (const skill of remote) {
625
+ byName.set(skill.name, skill);
626
+ }
627
+ for (const skill of local) {
628
+ byName.set(skill.name, skill);
629
+ }
630
+ const source = local.length > 0 && remote.length > 0 ? "merged" : local.length > 0 ? "local" : "remote";
631
+ return {
632
+ skills: Array.from(byName.values()),
633
+ source
634
+ };
635
+ }
636
+ var init_skill_manifest = __esm({
637
+ "src/skill-manifest.ts"() {
638
+ "use strict";
639
+ }
640
+ });
641
+
93
642
  // src/openclaw-entry.ts
94
- import { resolve } from "node:path";
643
+ import { resolve as pathResolve } from "node:path";
95
644
  import { randomBytes } from "node:crypto";
96
645
 
97
646
  // src/account-config.ts
@@ -289,146 +838,8 @@ function runWithTraceContext(ctx, fn) {
289
838
  return traceStore.run(ctx, fn);
290
839
  }
291
840
 
292
- // src/http-handlers.ts
293
- async function handleSendRequest(parsed, channel) {
294
- const text = parsed.text;
295
- if (!text || typeof text !== "string") {
296
- return { status: 400, body: { ok: false, error: "Missing 'text' field" } };
297
- }
298
- try {
299
- let target;
300
- let a2aTarget = parsed.hub_address ?? parsed.a2a_address ?? parsed.a2aAddress;
301
- if (!a2aTarget && parsed.channel_id && typeof parsed.channel_id === "string") {
302
- const hubAddr = channel.resolveA2AChannelHub(parsed.channel_id);
303
- if (hubAddr) a2aTarget = hubAddr;
304
- }
305
- if (a2aTarget && typeof a2aTarget === "string") {
306
- target = { kind: "a2a", hubAddress: a2aTarget };
307
- } else if (typeof parsed.room_id === "string") {
308
- target = { kind: "room", roomId: parsed.room_id };
309
- } else if (parsed.target === "context") {
310
- target = { kind: "context" };
311
- } else if (parsed.file_path && typeof parsed.file_path === "string") {
312
- const receipt2 = await channel.deliver(
313
- { kind: "owner" },
314
- { type: "attachment", text, filePath: parsed.file_path },
315
- { topicId: parsed.topicId }
316
- );
317
- return {
318
- status: receipt2.ok ? 200 : 500,
319
- body: {
320
- ok: receipt2.ok,
321
- destination: receipt2.destination,
322
- ...receipt2.error ? { error: receipt2.error } : {}
323
- }
324
- };
325
- } else {
326
- target = { kind: "owner" };
327
- }
328
- const receipt = await channel.deliver(
329
- target,
330
- { type: "text", text },
331
- {
332
- topicId: parsed.topicId,
333
- priority: parsed.priority,
334
- metadata: {
335
- ...parsed.metadata,
336
- ...parsed.message_type ? { message_type: parsed.message_type } : {}
337
- }
338
- }
339
- );
340
- return {
341
- status: receipt.ok ? 200 : 500,
342
- body: {
343
- ok: receipt.ok,
344
- destination: receipt.destination,
345
- ...receipt.error ? { error: receipt.error } : {}
346
- }
347
- };
348
- } catch (err) {
349
- return { status: 500, body: { ok: false, error: String(err) } };
350
- }
351
- }
352
- async function handleActionRequest(parsed, channel) {
353
- if (!parsed.action || typeof parsed.action !== "string") {
354
- return { status: 400, body: { ok: false, error: "Missing 'action' field" } };
355
- }
356
- try {
357
- const confirmation = {
358
- action: parsed.action,
359
- status: parsed.status ?? "completed",
360
- decisionId: parsed.decision_id,
361
- detail: parsed.detail,
362
- estimated_cost: parsed.estimated_cost
363
- };
364
- const target = parsed.room_id && typeof parsed.room_id === "string" ? { kind: "room", roomId: parsed.room_id } : { kind: "owner" };
365
- const receipt = await channel.deliver(
366
- target,
367
- { type: "action_confirmation", confirmation }
368
- );
369
- return {
370
- status: receipt.ok ? 200 : 500,
371
- body: {
372
- ok: receipt.ok,
373
- destination: receipt.destination,
374
- ...receipt.error ? { error: receipt.error } : {}
375
- }
376
- };
377
- } catch (err) {
378
- return { status: 500, body: { ok: false, error: String(err) } };
379
- }
380
- }
381
- async function handleDecisionRequest(parsed, channel) {
382
- const title = parsed.title;
383
- if (!title || typeof title !== "string") {
384
- return { status: 400, body: { ok: false, error: "Missing 'title' field" } };
385
- }
386
- const options = parsed.options;
387
- if (!Array.isArray(options) || options.length < 2) {
388
- return { status: 400, body: { ok: false, error: "'options' must be an array with at least 2 items" } };
389
- }
390
- for (const opt of options) {
391
- if (!opt || typeof opt !== "object" || !opt.option_id || !opt.label) {
392
- return { status: 400, body: { ok: false, error: "Each option must have 'option_id' and 'label'" } };
393
- }
394
- }
395
- try {
396
- const decision_id = await channel.sendDecisionRequest({
397
- title,
398
- description: parsed.description,
399
- options,
400
- context_refs: parsed.context_refs,
401
- deadline: parsed.deadline,
402
- auto_action: parsed.auto_action
403
- });
404
- return { status: 200, body: { ok: true, decision_id } };
405
- } catch (err) {
406
- return { status: 500, body: { ok: false, error: String(err) } };
407
- }
408
- }
409
- function handleStatusRequest(channel) {
410
- return {
411
- status: 200,
412
- body: {
413
- ok: true,
414
- state: channel.state,
415
- deviceId: channel.deviceId ?? void 0,
416
- sessions: channel.sessionCount
417
- }
418
- };
419
- }
420
- function handleTargetsRequest(channel) {
421
- return {
422
- status: 200,
423
- body: {
424
- ok: true,
425
- targets: channel.listTargets(),
426
- context: channel.lastInboundRoomId ? { kind: "room", roomId: channel.lastInboundRoomId } : { kind: "owner" }
427
- }
428
- };
429
- }
430
-
431
841
  // src/openclaw-entry.ts
842
+ init_http_handlers();
432
843
  init_openclaw_compat();
433
844
 
434
845
  // src/types.ts
@@ -1104,7 +1515,7 @@ var agentVaultPlugin = {
1104
1515
  "AgentVault channel not configured. Run: npx @agentvault/agentvault setup --token=av_tok_...\nThen restart OpenClaw."
1105
1516
  );
1106
1517
  }
1107
- const dataDir = resolve(account.dataDir.replace(/^~/, __require("node:os").homedir()));
1518
+ const dataDir = pathResolve(account.dataDir.replace(/^~/, __require("node:os").homedir()));
1108
1519
  _log?.(`[AgentVault] starting (dataDir=${dataDir})`);
1109
1520
  await new Promise((resolve2, reject) => {
1110
1521
  let channel;
@@ -1114,7 +1525,7 @@ var agentVaultPlugin = {
1114
1525
  resolve2();
1115
1526
  };
1116
1527
  abortSignal?.addEventListener("abort", () => void onAbort());
1117
- import("./index.js").then(({ SecureChannel }) => {
1528
+ import("./index.js").then(async ({ SecureChannel }) => {
1118
1529
  channel = new SecureChannel({
1119
1530
  inviteToken: "",
1120
1531
  dataDir,
@@ -1193,6 +1604,38 @@ var agentVaultPlugin = {
1193
1604
  channel.on("error", (err) => {
1194
1605
  _log?.(`[AgentVault] channel error (non-fatal): ${String(err)}`);
1195
1606
  });
1607
+ try {
1608
+ const { AgentVaultMcpServer: AgentVaultMcpServer2 } = await Promise.resolve().then(() => (init_mcp_server(), mcp_server_exports));
1609
+ const { loadSkillsFromDirectory: loadSkillsFromDirectory2 } = await Promise.resolve().then(() => (init_skill_manifest(), skill_manifest_exports));
1610
+ const mcpServer = new AgentVaultMcpServer2({
1611
+ agentName: account.agentName ?? "agent",
1612
+ apiUrl: account.apiUrl ?? "https://api.agentvault.chat",
1613
+ onInvoke: async (skillName, args) => {
1614
+ const text = JSON.stringify({ skill: skillName, args });
1615
+ const receipt = await channel.deliver(
1616
+ { kind: "owner" },
1617
+ { type: "text", text },
1618
+ { metadata: { message_type: "skill_invocation", skill_name: skillName } }
1619
+ );
1620
+ return { invoked: true, skill: skillName, delivered: receipt.ok };
1621
+ }
1622
+ });
1623
+ const workspaceDir = dataDir;
1624
+ const skills = loadSkillsFromDirectory2(workspaceDir);
1625
+ for (const skill of skills) {
1626
+ mcpServer.registerSkill(skill);
1627
+ }
1628
+ const skillsSubDir = pathResolve(dataDir, "skills");
1629
+ const subDirSkills = loadSkillsFromDirectory2(skillsSubDir);
1630
+ for (const skill of subDirSkills) {
1631
+ mcpServer.registerSkill(skill);
1632
+ }
1633
+ mcpServer.initialize();
1634
+ channel.setMcpServer(mcpServer);
1635
+ _log?.(`[AgentVault] MCP server activated with ${mcpServer.skillCount} skill(s)`);
1636
+ } catch (err) {
1637
+ _log?.(`[AgentVault] MCP server activation failed (non-fatal): ${String(err)}`);
1638
+ }
1196
1639
  const httpPort = account.httpPort ?? 18790;
1197
1640
  channel.on("ready", () => {
1198
1641
  channel.startHttpServer(httpPort);
@@ -1399,6 +1842,19 @@ var openclaw_entry_default = {
1399
1842
  return { status: result.status, body: result.body };
1400
1843
  }
1401
1844
  });
1845
+ api.registerHttpRoute({
1846
+ path: "/agentvault/mcp-config",
1847
+ method: "GET",
1848
+ handler: async () => {
1849
+ const ch = _channels.values().next().value;
1850
+ if (!ch) return { status: 503, body: { ok: false, error: "Channel not started" } };
1851
+ const { handleMcpConfigRequest: handleMcpConfigRequest2 } = await Promise.resolve().then(() => (init_http_handlers(), http_handlers_exports));
1852
+ const agentName = ch.config?.agentName ?? "agent";
1853
+ const mcpSkillCount = ch.mcpServer?.skillCount ?? 0;
1854
+ const result = handleMcpConfigRequest2(agentName, 18790, mcpSkillCount);
1855
+ return { status: result.status, body: result.body };
1856
+ }
1857
+ });
1402
1858
  isUsingManagedRoutes = true;
1403
1859
  } catch {
1404
1860
  }