@ouro.bot/cli 0.1.0-alpha.653 → 0.1.0-alpha.655
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/changelog.json +13 -0
- package/dist/a2a/card.js +56 -0
- package/dist/a2a/client.js +143 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/onboarding.js +111 -0
- package/dist/a2a/server.js +498 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/types.js +3 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/daemon/cli-exec.js +118 -3
- package/dist/heart/daemon/cli-help.js +29 -2
- package/dist/heart/daemon/cli-parse.js +88 -4
- package/dist/heart/daemon/daemon.js +2 -1
- package/dist/heart/daemon/process-manager.js +2 -1
- package/dist/heart/daemon/runtime-logging.js +1 -1
- package/dist/heart/daemon/sense-manager.js +71 -15
- package/dist/heart/identity.js +4 -1
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/turn-context.js +6 -0
- package/dist/mind/friends/channel.js +10 -1
- package/dist/mind/friends/resolver.js +13 -2
- package/dist/mind/friends/store-file.js +13 -0
- package/dist/mind/friends/types.js +1 -1
- package/dist/mind/prompt.js +11 -0
- package/dist/repertoire/guardrails.js +25 -2
- package/dist/repertoire/tools-a2a.js +283 -0
- package/dist/repertoire/tools-base.js +4 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-flight.js +68 -5
- package/dist/repertoire/tools-stripe.js +49 -7
- package/dist/repertoire/tools.js +50 -2
- package/dist/senses/a2a-entry.js +78 -0
- package/dist/senses/pipeline.js +13 -0
- package/dist/senses/shared-turn.js +30 -5
- package/package.json +1 -1
- package/skills/agent-commerce.md +17 -10
package/changelog.json
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.1.0-alpha.655",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Add focused help for A2A CLI subcommands."
|
|
8
|
+
]
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"version": "0.1.0-alpha.654",
|
|
12
|
+
"changes": [
|
|
13
|
+
"Add A2A sense substrate and AP2-compatible commerce authority primitives.",
|
|
14
|
+
"Harden A2A and commerce review gates with scoped task access tokens, bounded anonymous A2A peer identity, sender-card onboarding, continuation support, token-redacted task reads, clean port-collision failures, dynamic managed A2A endpoint refresh, dual-shape A2A discovery compatibility, A2A orphan cleanup and bundle sync, detail-rich one-use commerce authority confirmation, random hash-stored commerce tokens, exact amount parsing, Stripe merchant-category constraints, locked reservation/consumption/release, and amount/currency-bound flight holds."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
"version": "0.1.0-alpha.653",
|
|
6
19
|
"changes": [
|
package/dist/a2a/card.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildA2AAgentCard = buildA2AAgentCard;
|
|
4
|
+
const runtime_1 = require("../nerves/runtime");
|
|
5
|
+
const config_1 = require("./config");
|
|
6
|
+
function buildA2AAgentCard(options) {
|
|
7
|
+
const endpoint = (0, config_1.a2aEndpointFromBaseUrl)(options.baseUrl, options.path ?? config_1.A2A_DEFAULT_PATH);
|
|
8
|
+
const card = {
|
|
9
|
+
name: options.agentName,
|
|
10
|
+
description: options.description ?? `Ouroboros agent ${options.agentName}`,
|
|
11
|
+
protocolVersion: config_1.A2A_DEFAULT_PROTOCOL_VERSION,
|
|
12
|
+
url: endpoint,
|
|
13
|
+
preferredTransport: "JSONRPC",
|
|
14
|
+
supportedInterfaces: [{
|
|
15
|
+
url: endpoint,
|
|
16
|
+
protocolBinding: "JSONRPC",
|
|
17
|
+
protocolVersion: config_1.A2A_DEFAULT_PROTOCOL_VERSION,
|
|
18
|
+
}],
|
|
19
|
+
additionalInterfaces: [{
|
|
20
|
+
url: endpoint,
|
|
21
|
+
transport: "JSONRPC",
|
|
22
|
+
}],
|
|
23
|
+
version: "1.0.0",
|
|
24
|
+
capabilities: {
|
|
25
|
+
streaming: false,
|
|
26
|
+
pushNotifications: false,
|
|
27
|
+
extendedAgentCard: false,
|
|
28
|
+
},
|
|
29
|
+
defaultInputModes: ["text/plain"],
|
|
30
|
+
defaultOutputModes: ["text/plain", "application/json"],
|
|
31
|
+
skills: [
|
|
32
|
+
{
|
|
33
|
+
id: "ouro-message",
|
|
34
|
+
name: "Message Ouro Agent",
|
|
35
|
+
description: "Send a message or task request through the Ouro A2A sense.",
|
|
36
|
+
tags: ["ouro", "agent", "work"],
|
|
37
|
+
examples: ["Ask this Ouro agent to continue or complete a delegated task."],
|
|
38
|
+
inputModes: ["text/plain"],
|
|
39
|
+
outputModes: ["text/plain", "application/json"],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
metadata: {
|
|
43
|
+
ouro: {
|
|
44
|
+
sense: "a2a",
|
|
45
|
+
agentName: options.agentName,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
(0, runtime_1.emitNervesEvent)({
|
|
50
|
+
component: "channels",
|
|
51
|
+
event: "channel.a2a_card_built",
|
|
52
|
+
message: "built A2A agent card",
|
|
53
|
+
meta: { agentName: options.agentName, endpoint },
|
|
54
|
+
});
|
|
55
|
+
return card;
|
|
56
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.endpointForCard = endpointForCard;
|
|
4
|
+
exports.fetchA2AAgentCard = fetchA2AAgentCard;
|
|
5
|
+
exports.sendA2AMessage = sendA2AMessage;
|
|
6
|
+
exports.getA2ATask = getA2ATask;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
function endpointForCard(card) {
|
|
10
|
+
const jsonRpc = card.supportedInterfaces?.find((entry) => (entry.protocolBinding ?? entry.transport)?.toUpperCase() === "JSONRPC");
|
|
11
|
+
if (jsonRpc?.url)
|
|
12
|
+
return jsonRpc.url;
|
|
13
|
+
if (card.preferredTransport?.toUpperCase() === "JSONRPC" && card.url)
|
|
14
|
+
return card.url;
|
|
15
|
+
const legacyJsonRpc = card.additionalInterfaces?.find((entry) => entry.transport.toUpperCase() === "JSONRPC");
|
|
16
|
+
return legacyJsonRpc?.url ?? card.url;
|
|
17
|
+
}
|
|
18
|
+
async function fetchA2AAgentCard(cardUrl, fetchImpl = fetch) {
|
|
19
|
+
(0, runtime_1.emitNervesEvent)({
|
|
20
|
+
component: "channels",
|
|
21
|
+
event: "channel.a2a_card_fetch_start",
|
|
22
|
+
message: "fetching A2A agent card",
|
|
23
|
+
meta: { cardUrl },
|
|
24
|
+
});
|
|
25
|
+
const response = await fetchImpl(cardUrl);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`A2A card fetch failed (${response.status} ${response.statusText})`);
|
|
28
|
+
}
|
|
29
|
+
const parsed = await response.json();
|
|
30
|
+
const endpoint = endpointForCard(parsed);
|
|
31
|
+
if (!parsed || typeof parsed !== "object" || typeof endpoint !== "string" || typeof parsed.name !== "string") {
|
|
32
|
+
throw new Error("A2A card is missing required name or JSONRPC endpoint fields");
|
|
33
|
+
}
|
|
34
|
+
(0, runtime_1.emitNervesEvent)({
|
|
35
|
+
component: "channels",
|
|
36
|
+
event: "channel.a2a_card_fetch_end",
|
|
37
|
+
message: "fetched A2A agent card",
|
|
38
|
+
meta: { cardUrl, endpoint, name: parsed.name },
|
|
39
|
+
});
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
async function postJsonRpc(endpointUrl, request, fetchImpl, protocolVersion = "1.0") {
|
|
43
|
+
const response = await fetchImpl(endpointUrl, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "content-type": "application/json", "A2A-Version": protocolVersion },
|
|
46
|
+
body: JSON.stringify(request),
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`A2A JSON-RPC request failed (${response.status} ${response.statusText})`);
|
|
50
|
+
}
|
|
51
|
+
return await response.json();
|
|
52
|
+
}
|
|
53
|
+
function methodNotFound(response) {
|
|
54
|
+
return "error" in response && response.error.code === -32601;
|
|
55
|
+
}
|
|
56
|
+
function senderMetadata(input) {
|
|
57
|
+
return {
|
|
58
|
+
...(input.senderAgentId ? { senderAgentId: input.senderAgentId } : {}),
|
|
59
|
+
...(input.senderName ? { senderName: input.senderName } : {}),
|
|
60
|
+
...(input.senderCardUrl ? { senderCardUrl: input.senderCardUrl } : {}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function sendA2AMessage(input) {
|
|
64
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
65
|
+
const messageId = (0, node_crypto_1.randomUUID)();
|
|
66
|
+
const request = {
|
|
67
|
+
jsonrpc: "2.0",
|
|
68
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
69
|
+
method: "message/send",
|
|
70
|
+
params: {
|
|
71
|
+
message: {
|
|
72
|
+
kind: "message",
|
|
73
|
+
role: "user",
|
|
74
|
+
messageId,
|
|
75
|
+
...(input.taskId ? { taskId: input.taskId } : {}),
|
|
76
|
+
contextId: input.sessionKey ?? "default",
|
|
77
|
+
parts: [{ kind: "text", text: input.message }],
|
|
78
|
+
metadata: senderMetadata(input),
|
|
79
|
+
},
|
|
80
|
+
...(input.accessToken ? { accessToken: input.accessToken } : {}),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "channels",
|
|
85
|
+
event: "channel.a2a_message_send_start",
|
|
86
|
+
message: "sending A2A message",
|
|
87
|
+
meta: { endpointUrl: input.endpointUrl, messageId },
|
|
88
|
+
});
|
|
89
|
+
let rpc = await postJsonRpc(input.endpointUrl, request, fetchImpl, "0.3");
|
|
90
|
+
if (methodNotFound(rpc)) {
|
|
91
|
+
rpc = await postJsonRpc(input.endpointUrl, {
|
|
92
|
+
...request,
|
|
93
|
+
method: "SendMessage",
|
|
94
|
+
params: {
|
|
95
|
+
...request.params,
|
|
96
|
+
message: {
|
|
97
|
+
...(request.params.message),
|
|
98
|
+
role: "ROLE_USER",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
}, fetchImpl);
|
|
102
|
+
}
|
|
103
|
+
if ("error" in rpc) {
|
|
104
|
+
throw new Error(`A2A error ${rpc.error.code}: ${rpc.error.message}`);
|
|
105
|
+
}
|
|
106
|
+
const result = rpc.result;
|
|
107
|
+
const task = "task" in result && result.task ? result.task : result;
|
|
108
|
+
(0, runtime_1.emitNervesEvent)({
|
|
109
|
+
component: "channels",
|
|
110
|
+
event: "channel.a2a_message_send_end",
|
|
111
|
+
message: "sent A2A message",
|
|
112
|
+
meta: { endpointUrl: input.endpointUrl, messageId, taskId: task.id },
|
|
113
|
+
});
|
|
114
|
+
return task;
|
|
115
|
+
}
|
|
116
|
+
async function getA2ATask(input) {
|
|
117
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
118
|
+
const metadata = senderMetadata(input);
|
|
119
|
+
const request = {
|
|
120
|
+
jsonrpc: "2.0",
|
|
121
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
122
|
+
method: "tasks/get",
|
|
123
|
+
params: {
|
|
124
|
+
id: input.taskId,
|
|
125
|
+
...(input.accessToken ? { accessToken: input.accessToken } : {}),
|
|
126
|
+
...(Object.keys(metadata).length > 0 ? { metadata } : {}),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
(0, runtime_1.emitNervesEvent)({
|
|
130
|
+
component: "channels",
|
|
131
|
+
event: "channel.a2a_task_get",
|
|
132
|
+
message: "fetching A2A task",
|
|
133
|
+
meta: { endpointUrl: input.endpointUrl, taskId: input.taskId },
|
|
134
|
+
});
|
|
135
|
+
let rpc = await postJsonRpc(input.endpointUrl, request, fetchImpl, "0.3");
|
|
136
|
+
if (methodNotFound(rpc)) {
|
|
137
|
+
rpc = await postJsonRpc(input.endpointUrl, { ...request, method: "GetTask" }, fetchImpl);
|
|
138
|
+
}
|
|
139
|
+
if ("error" in rpc) {
|
|
140
|
+
throw new Error(`A2A error ${rpc.error.code}: ${rpc.error.message}`);
|
|
141
|
+
}
|
|
142
|
+
return rpc.result;
|
|
143
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.A2A_DEFAULT_PATH = exports.A2A_DEFAULT_HOST = exports.A2A_DEFAULT_PROTOCOL_VERSION = void 0;
|
|
4
|
+
exports.defaultA2APort = defaultA2APort;
|
|
5
|
+
exports.normalizeA2APath = normalizeA2APath;
|
|
6
|
+
exports.a2aEndpointFromBaseUrl = a2aEndpointFromBaseUrl;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
exports.A2A_DEFAULT_PROTOCOL_VERSION = "1.0";
|
|
10
|
+
exports.A2A_DEFAULT_HOST = "127.0.0.1";
|
|
11
|
+
exports.A2A_DEFAULT_PATH = "/a2a";
|
|
12
|
+
function defaultA2APort(agentName) {
|
|
13
|
+
const digest = (0, node_crypto_1.createHash)("sha256").update(agentName).digest();
|
|
14
|
+
const port = 18920 + (digest[0] % 60);
|
|
15
|
+
(0, runtime_1.emitNervesEvent)({
|
|
16
|
+
component: "channels",
|
|
17
|
+
event: "channel.a2a_default_port",
|
|
18
|
+
message: "computed default A2A port",
|
|
19
|
+
meta: { agentName, port },
|
|
20
|
+
});
|
|
21
|
+
return port;
|
|
22
|
+
}
|
|
23
|
+
function normalizeA2APath(value) {
|
|
24
|
+
const trimmed = value?.trim();
|
|
25
|
+
const normalized = !trimmed
|
|
26
|
+
? exports.A2A_DEFAULT_PATH
|
|
27
|
+
: trimmed.startsWith("/")
|
|
28
|
+
? trimmed
|
|
29
|
+
: `/${trimmed}`;
|
|
30
|
+
(0, runtime_1.emitNervesEvent)({
|
|
31
|
+
component: "channels",
|
|
32
|
+
event: "channel.a2a_path_normalized",
|
|
33
|
+
message: "normalized A2A path",
|
|
34
|
+
meta: { normalized },
|
|
35
|
+
});
|
|
36
|
+
return normalized;
|
|
37
|
+
}
|
|
38
|
+
function a2aEndpointFromBaseUrl(baseUrl, a2aPath = exports.A2A_DEFAULT_PATH) {
|
|
39
|
+
const url = new URL(baseUrl);
|
|
40
|
+
url.pathname = normalizeA2APath(a2aPath);
|
|
41
|
+
url.search = "";
|
|
42
|
+
url.hash = "";
|
|
43
|
+
(0, runtime_1.emitNervesEvent)({
|
|
44
|
+
component: "channels",
|
|
45
|
+
event: "channel.a2a_endpoint_built",
|
|
46
|
+
message: "built A2A endpoint URL",
|
|
47
|
+
meta: { endpoint: url.toString() },
|
|
48
|
+
});
|
|
49
|
+
return url.toString();
|
|
50
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.onboardA2APeer = onboardA2APeer;
|
|
37
|
+
const node_crypto_1 = require("node:crypto");
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const identity_1 = require("../heart/identity");
|
|
40
|
+
const runtime_1 = require("../nerves/runtime");
|
|
41
|
+
const store_file_1 = require("../mind/friends/store-file");
|
|
42
|
+
const client_1 = require("./client");
|
|
43
|
+
function storeFor(options) {
|
|
44
|
+
if (options.store)
|
|
45
|
+
return options.store;
|
|
46
|
+
/* v8 ignore next -- default bundle root fallback is owned by identity path tests; CLI and onboarding tests inject explicit roots @preserve */
|
|
47
|
+
return new store_file_1.FileFriendStore(path.join(options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(), `${options.agentName}.ouro`, "friends"));
|
|
48
|
+
}
|
|
49
|
+
function agentIdFor(_card, cardUrl) {
|
|
50
|
+
const parsed = new URL(cardUrl);
|
|
51
|
+
parsed.hash = "";
|
|
52
|
+
return parsed.toString();
|
|
53
|
+
}
|
|
54
|
+
async function onboardA2APeer(options) {
|
|
55
|
+
const card = await (0, client_1.fetchA2AAgentCard)(options.cardUrl, options.fetchImpl);
|
|
56
|
+
const store = storeFor(options);
|
|
57
|
+
const now = new Date().toISOString();
|
|
58
|
+
const externalId = agentIdFor(card, options.cardUrl);
|
|
59
|
+
/* v8 ignore next -- fetchA2AAgentCard validates a usable endpoint or legacy url before returning a card @preserve */
|
|
60
|
+
const endpointUrl = (0, client_1.endpointForCard)(card) ?? options.cardUrl;
|
|
61
|
+
const protocolVersion = card.supportedInterfaces?.find((entry) => entry.url === endpointUrl)?.protocolVersion
|
|
62
|
+
?? card.protocolVersion;
|
|
63
|
+
const existing = await store.findByExternalId("a2a-agent", externalId);
|
|
64
|
+
const name = options.name ?? card.name;
|
|
65
|
+
const trustLevel = options.trustLevel ?? existing?.trustLevel ?? "acquaintance";
|
|
66
|
+
const baseMeta = existing?.agentMeta ?? {
|
|
67
|
+
bundleName: name,
|
|
68
|
+
familiarity: 0,
|
|
69
|
+
sharedMissions: [],
|
|
70
|
+
outcomes: [],
|
|
71
|
+
};
|
|
72
|
+
const record = {
|
|
73
|
+
...(existing ?? {
|
|
74
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
75
|
+
createdAt: now,
|
|
76
|
+
externalIds: [],
|
|
77
|
+
tenantMemberships: [],
|
|
78
|
+
toolPreferences: {},
|
|
79
|
+
notes: {},
|
|
80
|
+
totalTokens: 0,
|
|
81
|
+
schemaVersion: 1,
|
|
82
|
+
}),
|
|
83
|
+
name,
|
|
84
|
+
role: "agent-peer",
|
|
85
|
+
trustLevel,
|
|
86
|
+
kind: "agent",
|
|
87
|
+
agentMeta: {
|
|
88
|
+
...baseMeta,
|
|
89
|
+
bundleName: baseMeta.bundleName || name,
|
|
90
|
+
a2a: {
|
|
91
|
+
cardUrl: options.cardUrl,
|
|
92
|
+
endpointUrl,
|
|
93
|
+
agentId: externalId,
|
|
94
|
+
protocolVersion,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
externalIds: [
|
|
98
|
+
...(existing?.externalIds.filter((id) => !(id.provider === "a2a-agent" && id.externalId === externalId)) ?? []),
|
|
99
|
+
{ provider: "a2a-agent", externalId, linkedAt: now },
|
|
100
|
+
],
|
|
101
|
+
updatedAt: now,
|
|
102
|
+
};
|
|
103
|
+
await store.put(record.id, record);
|
|
104
|
+
(0, runtime_1.emitNervesEvent)({
|
|
105
|
+
component: "friends",
|
|
106
|
+
event: "friends.a2a_peer_onboarded",
|
|
107
|
+
message: "onboarded A2A peer into friend model",
|
|
108
|
+
meta: { agentName: options.agentName, friendId: record.id, peerName: record.name, trustLevel },
|
|
109
|
+
});
|
|
110
|
+
return record;
|
|
111
|
+
}
|