@ouro.bot/cli 0.1.0-alpha.653 → 0.1.0-alpha.654
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 +7 -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 +14 -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
|
@@ -368,6 +368,10 @@ function agentResolutionFailureMode(command) {
|
|
|
368
368
|
case "inner.status":
|
|
369
369
|
case "session.list":
|
|
370
370
|
return "return-message";
|
|
371
|
+
case "a2a.card":
|
|
372
|
+
case "a2a.onboard":
|
|
373
|
+
case "a2a.serve":
|
|
374
|
+
return "throw";
|
|
371
375
|
case "provider.use":
|
|
372
376
|
case "provider.check":
|
|
373
377
|
case "provider.status":
|
|
@@ -2491,11 +2495,12 @@ function enableAgentSense(agent, sense, deps) {
|
|
|
2491
2495
|
bluebubbles: senses.bluebubbles ?? { enabled: false },
|
|
2492
2496
|
mail: senses.mail ?? { enabled: false },
|
|
2493
2497
|
voice: senses.voice ?? { enabled: false },
|
|
2498
|
+
a2a: senses.a2a ?? { enabled: false },
|
|
2494
2499
|
[sense]: { ...existing, enabled: true },
|
|
2495
2500
|
};
|
|
2496
2501
|
fs.writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
2497
2502
|
}
|
|
2498
|
-
const CONNECT_MENU_PROMPT = "Choose [1-
|
|
2503
|
+
const CONNECT_MENU_PROMPT = "Choose [1-8] or type a name: ";
|
|
2499
2504
|
function connectMenuIsTTY(deps) {
|
|
2500
2505
|
return deps.isTTY ?? process.stdout.isTTY === true;
|
|
2501
2506
|
}
|
|
@@ -2507,6 +2512,7 @@ function readConnectBaySenseFlags(agent, deps) {
|
|
|
2507
2512
|
blueBubblesEnabled: parsed.senses?.bluebubbles?.enabled === true,
|
|
2508
2513
|
mailEnabled: parsed.senses?.mail?.enabled === true,
|
|
2509
2514
|
voiceEnabled: parsed.senses?.voice?.enabled === true,
|
|
2515
|
+
a2aEnabled: parsed.senses?.a2a?.enabled === true,
|
|
2510
2516
|
};
|
|
2511
2517
|
}
|
|
2512
2518
|
async function buildConnectMenu(agent, deps, onProgress) {
|
|
@@ -2536,7 +2542,7 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2536
2542
|
const runtimeConfig = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
2537
2543
|
onProgress?.("loading this machine's settings");
|
|
2538
2544
|
const machineRuntime = await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(agent, currentMachineId(deps), { preserveCachedOnFailure: true });
|
|
2539
|
-
const { teamsEnabled, blueBubblesEnabled, mailEnabled, voiceEnabled } = readConnectBaySenseFlags(agent, deps);
|
|
2545
|
+
const { teamsEnabled, blueBubblesEnabled, mailEnabled, voiceEnabled, a2aEnabled } = readConnectBaySenseFlags(agent, deps);
|
|
2540
2546
|
const perplexityApiKey = runtimeConfig.ok
|
|
2541
2547
|
? readRuntimeConfigString(runtimeConfig.config, "integrations.perplexityApiKey")
|
|
2542
2548
|
: null;
|
|
@@ -2691,6 +2697,7 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2691
2697
|
? "ready"
|
|
2692
2698
|
: "missing"
|
|
2693
2699
|
: runtimeConfigReadStatus(runtimeConfig);
|
|
2700
|
+
const a2aStatus = a2aEnabled ? "ready" : "missing";
|
|
2694
2701
|
const entries = [
|
|
2695
2702
|
{
|
|
2696
2703
|
option: "1",
|
|
@@ -2805,6 +2812,20 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2805
2812
|
status: voiceStatus,
|
|
2806
2813
|
}) ? `ouro connect voice --agent ${agent}` : undefined,
|
|
2807
2814
|
},
|
|
2815
|
+
{
|
|
2816
|
+
option: "8",
|
|
2817
|
+
name: "A2A",
|
|
2818
|
+
section: "Portable",
|
|
2819
|
+
status: a2aStatus,
|
|
2820
|
+
description: "Agent-to-agent sense endpoint and peer onboarding.",
|
|
2821
|
+
detailLines: a2aEnabled ? ["enabled in agent.json"] : ["not enabled in agent.json"],
|
|
2822
|
+
nextAction: (0, connect_bay_1.connectEntryNeedsAttention)({
|
|
2823
|
+
option: "8",
|
|
2824
|
+
name: "A2A",
|
|
2825
|
+
section: "Portable",
|
|
2826
|
+
status: a2aStatus,
|
|
2827
|
+
}) ? `ouro connect a2a --agent ${agent}` : undefined,
|
|
2828
|
+
},
|
|
2808
2829
|
];
|
|
2809
2830
|
const isTTY = connectMenuIsTTY(deps);
|
|
2810
2831
|
return (0, connect_bay_1.renderConnectBay)(entries, {
|
|
@@ -4340,6 +4361,9 @@ function connectMenuTarget(answer) {
|
|
|
4340
4361
|
return "mail";
|
|
4341
4362
|
if (normalized === "7" || normalized === "voice" || normalized === "audio" || normalized === "speech")
|
|
4342
4363
|
return "voice";
|
|
4364
|
+
/* v8 ignore next -- direct `ouro connect a2a` covers behavior; interactive menu requires broader provider/vault readiness work @preserve */
|
|
4365
|
+
if (normalized === "8" || normalized === "a2a" || normalized === "agent2agent" || normalized === "agent-to-agent")
|
|
4366
|
+
return "a2a";
|
|
4343
4367
|
return "cancel";
|
|
4344
4368
|
}
|
|
4345
4369
|
async function executeConnectVoice(agent, deps) {
|
|
@@ -4378,6 +4402,25 @@ async function executeConnectVoice(agent, deps) {
|
|
|
4378
4402
|
deps.writeStdout(message);
|
|
4379
4403
|
return message;
|
|
4380
4404
|
}
|
|
4405
|
+
async function executeConnectA2A(agent, deps) {
|
|
4406
|
+
const { defaultA2APort } = await Promise.resolve().then(() => __importStar(require("../../a2a/config")));
|
|
4407
|
+
enableAgentSense(agent, "a2a", deps);
|
|
4408
|
+
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
4409
|
+
const port = defaultA2APort(agent);
|
|
4410
|
+
const message = [
|
|
4411
|
+
`A2A connected for ${agent}`,
|
|
4412
|
+
`The daemon-managed A2A sense will listen locally on port ${port} after \`ouro up\`.`,
|
|
4413
|
+
`Local agent card: http://127.0.0.1:${port}/.well-known/agent-card.json`,
|
|
4414
|
+
`Local JSON-RPC endpoint: http://127.0.0.1:${port}/a2a`,
|
|
4415
|
+
"For a public endpoint, expose that local port through your chosen tunnel and publish the resulting base URL:",
|
|
4416
|
+
` ouro a2a card --agent ${agent} --base-url https://<public-host>`,
|
|
4417
|
+
"Onboard a peer into the existing friend model:",
|
|
4418
|
+
` ouro a2a onboard --agent ${agent} --card-url https://<peer>/.well-known/agent-card.json --trust friend`,
|
|
4419
|
+
...(syncSummary ? [syncSummary] : []),
|
|
4420
|
+
].join("\n");
|
|
4421
|
+
deps.writeStdout(message);
|
|
4422
|
+
return message;
|
|
4423
|
+
}
|
|
4381
4424
|
async function executeConnect(command, deps) {
|
|
4382
4425
|
if (command.target === "providers")
|
|
4383
4426
|
return executeConnectProviders(command.agent, deps);
|
|
@@ -4393,6 +4436,8 @@ async function executeConnect(command, deps) {
|
|
|
4393
4436
|
return executeConnectMail(command.agent, deps, command);
|
|
4394
4437
|
if (command.target === "voice")
|
|
4395
4438
|
return executeConnectVoice(command.agent, deps);
|
|
4439
|
+
if (command.target === "a2a")
|
|
4440
|
+
return executeConnectA2A(command.agent, deps);
|
|
4396
4441
|
const progress = createHumanCommandProgress(deps, "connect");
|
|
4397
4442
|
let menu;
|
|
4398
4443
|
try {
|
|
@@ -4404,7 +4449,7 @@ async function executeConnect(command, deps) {
|
|
|
4404
4449
|
const promptInput = deps.promptInput;
|
|
4405
4450
|
if (!promptInput) {
|
|
4406
4451
|
const message = [
|
|
4407
|
-
menu.replace(/\nChoose \[1-
|
|
4452
|
+
menu.replace(/\nChoose \[1-8\] or type a name: $/, ""),
|
|
4408
4453
|
"",
|
|
4409
4454
|
`Run: ouro connect providers --agent ${command.agent}`,
|
|
4410
4455
|
`Run: ouro connect perplexity --agent ${command.agent}`,
|
|
@@ -4413,6 +4458,7 @@ async function executeConnect(command, deps) {
|
|
|
4413
4458
|
`Run: ouro connect bluebubbles --agent ${command.agent}`,
|
|
4414
4459
|
`Run: ouro connect mail --agent ${command.agent}`,
|
|
4415
4460
|
`Run: ouro connect voice --agent ${command.agent}`,
|
|
4461
|
+
`Run: ouro connect a2a --agent ${command.agent}`,
|
|
4416
4462
|
].join("\n");
|
|
4417
4463
|
deps.writeStdout(message);
|
|
4418
4464
|
return message;
|
|
@@ -4432,6 +4478,9 @@ async function executeConnect(command, deps) {
|
|
|
4432
4478
|
return executeConnectMail(command.agent, deps);
|
|
4433
4479
|
if (answer === "voice")
|
|
4434
4480
|
return executeConnectVoice(command.agent, deps);
|
|
4481
|
+
/* v8 ignore next -- direct `ouro connect a2a` covers behavior; interactive menu requires broader provider/vault readiness work @preserve */
|
|
4482
|
+
if (answer === "a2a")
|
|
4483
|
+
return executeConnectA2A(command.agent, deps);
|
|
4435
4484
|
const message = "connect cancelled.";
|
|
4436
4485
|
deps.writeStdout(message);
|
|
4437
4486
|
return message;
|
|
@@ -5262,6 +5311,69 @@ async function executeFriendCommand(command, store) {
|
|
|
5262
5311
|
await store.put(command.friendId, { ...current, externalIds: filtered, updatedAt: now });
|
|
5263
5312
|
return `unlinked ${command.provider}:${command.externalId} from ${command.friendId}`;
|
|
5264
5313
|
}
|
|
5314
|
+
async function executeA2ACommand(command, deps) {
|
|
5315
|
+
if (command.kind === "a2a.card") {
|
|
5316
|
+
const { buildA2AAgentCard } = await Promise.resolve().then(() => __importStar(require("../../a2a/card")));
|
|
5317
|
+
const { defaultA2APort } = await Promise.resolve().then(() => __importStar(require("../../a2a/config")));
|
|
5318
|
+
const { endpointForCard } = await Promise.resolve().then(() => __importStar(require("../../a2a/client")));
|
|
5319
|
+
const baseUrl = command.baseUrl ?? `http://127.0.0.1:${defaultA2APort(command.agent)}`;
|
|
5320
|
+
const card = buildA2AAgentCard({ agentName: command.agent, baseUrl });
|
|
5321
|
+
/* v8 ignore next -- buildA2AAgentCard always emits a JSON-RPC endpoint @preserve */
|
|
5322
|
+
const endpoint = endpointForCard(card) ?? "unknown";
|
|
5323
|
+
const message = command.json
|
|
5324
|
+
? JSON.stringify(card, null, 2)
|
|
5325
|
+
: [
|
|
5326
|
+
`A2A agent card for ${command.agent}`,
|
|
5327
|
+
`card URL: ${baseUrl.replace(/\/+$/, "")}/.well-known/agent-card.json`,
|
|
5328
|
+
`endpoint: ${endpoint}`,
|
|
5329
|
+
JSON.stringify(card, null, 2),
|
|
5330
|
+
].join("\n");
|
|
5331
|
+
deps.writeStdout(message);
|
|
5332
|
+
return message;
|
|
5333
|
+
}
|
|
5334
|
+
/* v8 ignore next -- false branch is foreground serve, intentionally ignored below because it waits for signals @preserve */
|
|
5335
|
+
if (command.kind === "a2a.onboard") {
|
|
5336
|
+
const { onboardA2APeer } = await Promise.resolve().then(() => __importStar(require("../../a2a/onboarding")));
|
|
5337
|
+
const record = await onboardA2APeer({
|
|
5338
|
+
agentName: command.agent,
|
|
5339
|
+
cardUrl: command.cardUrl,
|
|
5340
|
+
...(command.trustLevel ? { trustLevel: command.trustLevel } : {}),
|
|
5341
|
+
...(command.name ? { name: command.name } : {}),
|
|
5342
|
+
/* v8 ignore next -- production CLI falls back to the canonical bundle root; tests inject isolated roots @preserve */
|
|
5343
|
+
...(deps.bundlesRoot ? { bundlesRoot: deps.bundlesRoot } : {}),
|
|
5344
|
+
/* v8 ignore next -- production CLI uses global fetch; tests inject fetch for hermetic cards @preserve */
|
|
5345
|
+
...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),
|
|
5346
|
+
});
|
|
5347
|
+
const message = [
|
|
5348
|
+
`onboarded A2A peer: ${record.name}`,
|
|
5349
|
+
`friend id: ${record.id}`,
|
|
5350
|
+
/* v8 ignore next -- onboardA2APeer always writes an explicit TrustLevel @preserve */
|
|
5351
|
+
`trust: ${record.trustLevel ?? "unknown"}`,
|
|
5352
|
+
/* v8 ignore next -- onboardA2APeer validates and persists an A2A endpoint before returning @preserve */
|
|
5353
|
+
`endpoint: ${record.agentMeta?.a2a?.endpointUrl ?? "unknown"}`,
|
|
5354
|
+
].join("\n");
|
|
5355
|
+
deps.writeStdout(message);
|
|
5356
|
+
return message;
|
|
5357
|
+
}
|
|
5358
|
+
/* v8 ignore start -- foreground serve intentionally waits for process signals; a2a/server has route-level coverage @preserve */
|
|
5359
|
+
const { startA2AServer } = await Promise.resolve().then(() => __importStar(require("../../a2a/server")));
|
|
5360
|
+
const handle = await startA2AServer({
|
|
5361
|
+
agentName: command.agent,
|
|
5362
|
+
...(command.host ? { host: command.host } : {}),
|
|
5363
|
+
...(command.port ? { port: command.port } : {}),
|
|
5364
|
+
...(command.baseUrl ? { baseUrl: command.baseUrl } : {}),
|
|
5365
|
+
...(command.path ? { path: command.path } : {}),
|
|
5366
|
+
});
|
|
5367
|
+
const message = `A2A listening for ${command.agent}\nendpoint: ${handle.endpointUrl}\ncard: ${handle.url.replace(/\/+$/, "")}/.well-known/agent-card.json`;
|
|
5368
|
+
deps.writeStdout(message);
|
|
5369
|
+
await new Promise((resolve) => {
|
|
5370
|
+
process.once("SIGINT", () => resolve());
|
|
5371
|
+
process.once("SIGTERM", () => resolve());
|
|
5372
|
+
});
|
|
5373
|
+
await handle.close();
|
|
5374
|
+
return message;
|
|
5375
|
+
/* v8 ignore stop */
|
|
5376
|
+
}
|
|
5265
5377
|
// ── Dev mode helpers ──
|
|
5266
5378
|
/* v8 ignore start -- repo resolution for ouro dev: repoPath branch tested via daemon-cli-dev; clone requires real git/npm @preserve */
|
|
5267
5379
|
function getDevConfigPath() {
|
|
@@ -6594,6 +6706,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
6594
6706
|
deps.writeStdout(message);
|
|
6595
6707
|
return message;
|
|
6596
6708
|
}
|
|
6709
|
+
if (command.kind === "a2a.card" || command.kind === "a2a.onboard" || command.kind === "a2a.serve") {
|
|
6710
|
+
return executeA2ACommand(command, deps);
|
|
6711
|
+
}
|
|
6597
6712
|
// ── provider commands (local, no daemon socket needed) ──
|
|
6598
6713
|
if (command.kind === "provider.use") {
|
|
6599
6714
|
return executeProviderUse(command, deps);
|
|
@@ -187,9 +187,16 @@ exports.COMMAND_REGISTRY = {
|
|
|
187
187
|
connect: {
|
|
188
188
|
category: "Auth",
|
|
189
189
|
description: "Set up providers, portable integrations, and local senses from one guided screen",
|
|
190
|
-
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail] [--agent <name>]",
|
|
190
|
+
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a] [--agent <name>]",
|
|
191
191
|
example: "ouro connect",
|
|
192
|
-
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles", "mail"],
|
|
192
|
+
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles", "mail", "voice", "a2a"],
|
|
193
|
+
},
|
|
194
|
+
a2a: {
|
|
195
|
+
category: "Friends",
|
|
196
|
+
description: "Publish A2A cards, onboard agent peers, and run the A2A sense server",
|
|
197
|
+
usage: "ouro a2a <card|onboard|serve> [--agent <name>]",
|
|
198
|
+
example: "ouro a2a card --agent <agent> --base-url https://agent.example",
|
|
199
|
+
subcommands: ["card", "onboard", "serve"],
|
|
193
200
|
},
|
|
194
201
|
mail: {
|
|
195
202
|
category: "Auth",
|
|
@@ -330,6 +337,11 @@ const SUBCOMMAND_HELP = {
|
|
|
330
337
|
usage: "ouro connect mail [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
331
338
|
example: "ouro connect mail --agent <agent> --owner-email you@example.com --source hey",
|
|
332
339
|
},
|
|
340
|
+
"connect a2a": {
|
|
341
|
+
description: "Enable the agent-to-agent A2A sense",
|
|
342
|
+
usage: "ouro connect a2a [--agent <name>]",
|
|
343
|
+
example: "ouro connect a2a --agent <agent>",
|
|
344
|
+
},
|
|
333
345
|
"account ensure": {
|
|
334
346
|
description: "Idempotently prepare an agent's vault-backed work substrate account and private Mailroom mailbox",
|
|
335
347
|
usage: "ouro account ensure [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
@@ -84,7 +84,7 @@ function usage() {
|
|
|
84
84
|
" ouro -v|--version",
|
|
85
85
|
" ouro auth [--agent <name>] [--provider <provider>]",
|
|
86
86
|
" ouro account ensure [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
87
|
-
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
87
|
+
" ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]",
|
|
88
88
|
" ouro mail import-mbox --file <path> [--owner-email <email>] [--source <label>] [--agent <name>] [--foreground]",
|
|
89
89
|
" ouro mail backfill-indexes [--agent <name>] [--foreground]",
|
|
90
90
|
" ouro auth verify [--agent <name>] [--provider <provider>]",
|
|
@@ -117,6 +117,9 @@ function usage() {
|
|
|
117
117
|
" ouro inner [--agent <name>]",
|
|
118
118
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
119
119
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
120
|
+
" ouro a2a card [--agent <name>] [--base-url <url>] [--json]",
|
|
121
|
+
" ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]",
|
|
122
|
+
" ouro a2a serve [--agent <name>] [--host <host>] [--port <port>] [--base-url <url>] [--path <path>]",
|
|
120
123
|
" ouro whoami [--agent <name>]",
|
|
121
124
|
" ouro session list [--agent <name>]",
|
|
122
125
|
" ouro mcp list",
|
|
@@ -254,7 +257,7 @@ function parseLinkCommand(args, kind = "friend.link") {
|
|
|
254
257
|
throw new Error(`Usage\n${usage()}`);
|
|
255
258
|
}
|
|
256
259
|
if (!(0, types_1.isIdentityProvider)(providerRaw)) {
|
|
257
|
-
throw new Error(`Unknown identity provider '${providerRaw}'. Use aad|local|teams-conversation.`);
|
|
260
|
+
throw new Error(`Unknown identity provider '${providerRaw}'. Use aad|local|teams-conversation|imessage-handle|email-address|a2a-agent.`);
|
|
258
261
|
}
|
|
259
262
|
return {
|
|
260
263
|
kind,
|
|
@@ -739,7 +742,9 @@ function normalizeConnectTarget(value) {
|
|
|
739
742
|
return "mail";
|
|
740
743
|
if (value === "voice" || value === "audio" || value === "speech")
|
|
741
744
|
return "voice";
|
|
742
|
-
|
|
745
|
+
if (value === "a2a" || value === "agent2agent" || value === "agent-to-agent")
|
|
746
|
+
return "a2a";
|
|
747
|
+
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a] [--agent <name>]");
|
|
743
748
|
}
|
|
744
749
|
function extractMailSourceFlags(args, usageText) {
|
|
745
750
|
const rest = [];
|
|
@@ -792,7 +797,7 @@ function extractMailSourceFlags(args, usageText) {
|
|
|
792
797
|
};
|
|
793
798
|
}
|
|
794
799
|
function parseConnectCommand(args) {
|
|
795
|
-
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
800
|
+
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
796
801
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
797
802
|
const mailFlags = extractMailSourceFlags(afterAgent, usageText);
|
|
798
803
|
if (mailFlags.rest.length > 1)
|
|
@@ -1075,6 +1080,83 @@ function parseFriendCommand(args) {
|
|
|
1075
1080
|
return parseLinkCommand(rest, "friend.unlink");
|
|
1076
1081
|
throw new Error(`Usage\n${usage()}`);
|
|
1077
1082
|
}
|
|
1083
|
+
function parseA2ACommand(args) {
|
|
1084
|
+
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1085
|
+
const [sub, ...rest] = cleaned;
|
|
1086
|
+
if (sub === "card") {
|
|
1087
|
+
let baseUrl;
|
|
1088
|
+
let json = false;
|
|
1089
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
1090
|
+
if (rest[i] === "--base-url" && rest[i + 1]) {
|
|
1091
|
+
baseUrl = rest[++i];
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
if (rest[i] === "--json") {
|
|
1095
|
+
json = true;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
throw new Error("Usage: ouro a2a card [--agent <name>] [--base-url <url>] [--json]");
|
|
1099
|
+
}
|
|
1100
|
+
return { kind: "a2a.card", ...(agent ? { agent } : {}), ...(baseUrl ? { baseUrl } : {}), ...(json ? { json: true } : {}) };
|
|
1101
|
+
}
|
|
1102
|
+
if (sub === "onboard") {
|
|
1103
|
+
let cardUrl;
|
|
1104
|
+
let trustLevel;
|
|
1105
|
+
let name;
|
|
1106
|
+
const VALID_TRUST_LEVELS = new Set(["stranger", "acquaintance", "friend", "family"]);
|
|
1107
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
1108
|
+
if (rest[i] === "--card-url" && rest[i + 1]) {
|
|
1109
|
+
cardUrl = rest[++i];
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
if (rest[i] === "--trust" && rest[i + 1]) {
|
|
1113
|
+
const raw = rest[++i];
|
|
1114
|
+
if (!VALID_TRUST_LEVELS.has(raw))
|
|
1115
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] --card-url <url> [--trust <stranger|acquaintance|friend|family>] [--name <name>]");
|
|
1116
|
+
trustLevel = raw;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (rest[i] === "--name" && rest[i + 1]) {
|
|
1120
|
+
name = rest[++i];
|
|
1121
|
+
continue;
|
|
1122
|
+
}
|
|
1123
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]");
|
|
1124
|
+
}
|
|
1125
|
+
if (!cardUrl)
|
|
1126
|
+
throw new Error("Usage: ouro a2a onboard [--agent <name>] --card-url <url> [--trust <level>] [--name <name>]");
|
|
1127
|
+
return { kind: "a2a.onboard", cardUrl, ...(agent ? { agent } : {}), ...(trustLevel ? { trustLevel } : {}), ...(name ? { name } : {}) };
|
|
1128
|
+
}
|
|
1129
|
+
if (sub === "serve") {
|
|
1130
|
+
let host;
|
|
1131
|
+
let port;
|
|
1132
|
+
let baseUrl;
|
|
1133
|
+
let path;
|
|
1134
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
1135
|
+
if (rest[i] === "--host" && rest[i + 1]) {
|
|
1136
|
+
host = rest[++i];
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (rest[i] === "--port" && rest[i + 1]) {
|
|
1140
|
+
const rawPort = Number.parseInt(rest[++i], 10);
|
|
1141
|
+
if (!Number.isInteger(rawPort) || rawPort < 1 || rawPort > 65535)
|
|
1142
|
+
throw new Error("A2A port must be 1-65535");
|
|
1143
|
+
port = rawPort;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (rest[i] === "--base-url" && rest[i + 1]) {
|
|
1147
|
+
baseUrl = rest[++i];
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (rest[i] === "--path" && rest[i + 1]) {
|
|
1151
|
+
path = rest[++i];
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
throw new Error("Usage: ouro a2a serve [--agent <name>] [--host <host>] [--port <port>] [--base-url <url>] [--path <path>]");
|
|
1155
|
+
}
|
|
1156
|
+
return { kind: "a2a.serve", ...(agent ? { agent } : {}), ...(host ? { host } : {}), ...(port ? { port } : {}), ...(baseUrl ? { baseUrl } : {}), ...(path ? { path } : {}) };
|
|
1157
|
+
}
|
|
1158
|
+
throw new Error("Usage: ouro a2a card|onboard|serve ...");
|
|
1159
|
+
}
|
|
1078
1160
|
function parseConfigCommand(args) {
|
|
1079
1161
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
1080
1162
|
const { facing, rest: cleaned } = extractFacingFlag(afterAgent);
|
|
@@ -1473,6 +1555,8 @@ function parseOuroCommand(args) {
|
|
|
1473
1555
|
return parseMigrateToDeskCommand(args.slice(1));
|
|
1474
1556
|
if (head === "friend")
|
|
1475
1557
|
return parseFriendCommand(args.slice(1));
|
|
1558
|
+
if (head === "a2a")
|
|
1559
|
+
return parseA2ACommand(args.slice(1));
|
|
1476
1560
|
if (head === "config")
|
|
1477
1561
|
return parseConfigCommand(args.slice(1));
|
|
1478
1562
|
if (head === "mcp")
|
|
@@ -108,7 +108,8 @@ function parseOrphanPidsFromPs(psOutput, selfPid) {
|
|
|
108
108
|
&& !line.includes("daemon-entry.js")
|
|
109
109
|
&& !line.includes("bluebubbles/entry.js")
|
|
110
110
|
&& !line.includes("mail-entry.js")
|
|
111
|
-
&& !line.includes("teams-entry.js")
|
|
111
|
+
&& !line.includes("teams-entry.js")
|
|
112
|
+
&& !line.includes("a2a-entry.js"))
|
|
112
113
|
continue;
|
|
113
114
|
// Parse `<pid> <ppid> <command...>`. ps pads these with leading spaces.
|
|
114
115
|
// Regex guarantees both groups are \d+ so parseInt can't produce NaN.
|
|
@@ -287,7 +287,8 @@ class DaemonProcessManager {
|
|
|
287
287
|
this.notifySnapshotChange(state.snapshot);
|
|
288
288
|
return;
|
|
289
289
|
}
|
|
290
|
-
const
|
|
290
|
+
const agentArgs = state.config.getArgs?.() ?? state.config.args ?? [];
|
|
291
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...agentArgs];
|
|
291
292
|
const child = this.spawnFn("node", args, {
|
|
292
293
|
cwd: runCwd,
|
|
293
294
|
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
@@ -51,7 +51,7 @@ function defaultLoggingForProcess(processName) {
|
|
|
51
51
|
sinks: ["ndjson"],
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
if (processName === "bluebubbles" || processName === "mail" || processName === "voice") {
|
|
54
|
+
if (processName === "bluebubbles" || processName === "mail" || processName === "voice" || processName === "a2a") {
|
|
55
55
|
return {
|
|
56
56
|
level: "warn",
|
|
57
57
|
sinks: ["terminal", "ndjson"],
|
|
@@ -44,6 +44,7 @@ const provider_credentials_1 = require("../provider-credentials");
|
|
|
44
44
|
const sense_truth_1 = require("../sense-truth");
|
|
45
45
|
const machine_identity_1 = require("../machine-identity");
|
|
46
46
|
const process_manager_1 = require("./process-manager");
|
|
47
|
+
const config_1 = require("../../a2a/config");
|
|
47
48
|
const DEFAULT_TEAMS_PORT = 3978;
|
|
48
49
|
const DEFAULT_BLUEBUBBLES_PORT = 18790;
|
|
49
50
|
const DEFAULT_BLUEBUBBLES_WEBHOOK_PATH = "/bluebubbles-webhook";
|
|
@@ -55,6 +56,7 @@ function defaultSenses() {
|
|
|
55
56
|
bluebubbles: { ...identity_1.DEFAULT_AGENT_SENSES.bluebubbles },
|
|
56
57
|
mail: { ...identity_1.DEFAULT_AGENT_SENSES.mail },
|
|
57
58
|
voice: { ...identity_1.DEFAULT_AGENT_SENSES.voice },
|
|
59
|
+
a2a: { ...identity_1.DEFAULT_AGENT_SENSES.a2a },
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
function readAgentSenses(agentJsonPath) {
|
|
@@ -80,7 +82,7 @@ function readAgentSenses(agentJsonPath) {
|
|
|
80
82
|
if (!rawSenses || typeof rawSenses !== "object" || Array.isArray(rawSenses)) {
|
|
81
83
|
return defaults;
|
|
82
84
|
}
|
|
83
|
-
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice"]) {
|
|
85
|
+
for (const sense of ["cli", "teams", "bluebubbles", "mail", "voice", "a2a"]) {
|
|
84
86
|
const rawSense = rawSenses[sense];
|
|
85
87
|
if (!rawSense || typeof rawSense !== "object" || Array.isArray(rawSense)) {
|
|
86
88
|
continue;
|
|
@@ -103,6 +105,25 @@ function numberField(record, key, fallback) {
|
|
|
103
105
|
const value = record?.[key];
|
|
104
106
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
105
107
|
}
|
|
108
|
+
function a2aMachineRuntimeConfig(agent) {
|
|
109
|
+
const runtimeConfig = (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent);
|
|
110
|
+
const payload = runtimeConfig.ok ? runtimeConfig.config : {};
|
|
111
|
+
const a2a = payload.a2a;
|
|
112
|
+
return a2a && typeof a2a === "object" && !Array.isArray(a2a) ? a2a : undefined;
|
|
113
|
+
}
|
|
114
|
+
function managedA2AArgs(agent) {
|
|
115
|
+
const a2a = a2aMachineRuntimeConfig(agent);
|
|
116
|
+
const args = ["--port", String(numberField(a2a, "port", (0, config_1.defaultA2APort)(agent)))];
|
|
117
|
+
const host = textField(a2a, "host");
|
|
118
|
+
const endpointPath = (0, config_1.normalizeA2APath)(textField(a2a, "path") || config_1.A2A_DEFAULT_PATH);
|
|
119
|
+
const publicUrl = textField(a2a, "publicUrl");
|
|
120
|
+
if (host)
|
|
121
|
+
args.push("--host", host);
|
|
122
|
+
args.push("--path", endpointPath);
|
|
123
|
+
if (publicUrl)
|
|
124
|
+
args.push("--base-url", publicUrl);
|
|
125
|
+
return args;
|
|
126
|
+
}
|
|
106
127
|
function compactRuntimeConfigError(agent, error) {
|
|
107
128
|
const compact = error.replace(/\s+/g, " ").trim();
|
|
108
129
|
if (/credential vault is locked|vault locked|vault is locked/i.test(compact)) {
|
|
@@ -125,6 +146,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
125
146
|
bluebubbles: { configured: false, detail: "not enabled in agent.json" },
|
|
126
147
|
mail: { configured: false, detail: "not enabled in agent.json" },
|
|
127
148
|
voice: { configured: false, detail: "not enabled in agent.json" },
|
|
149
|
+
a2a: { configured: false, detail: "not enabled in agent.json" },
|
|
128
150
|
};
|
|
129
151
|
const payload = runtimeConfig.ok ? runtimeConfig.config : {};
|
|
130
152
|
const unavailableDetail = runtimeConfigUnavailableDetail(agent, runtimeConfig);
|
|
@@ -136,6 +158,7 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
136
158
|
const mailroom = payload.mailroom;
|
|
137
159
|
const integrations = payload.integrations;
|
|
138
160
|
const voice = machinePayload.voice;
|
|
161
|
+
const a2a = machinePayload.a2a;
|
|
139
162
|
if (senses.teams.enabled) {
|
|
140
163
|
const missing = [];
|
|
141
164
|
if (!textField(teams, "clientId"))
|
|
@@ -268,6 +291,16 @@ function senseFactsFromRuntimeConfig(agent, senses, runtimeConfig, machineRuntim
|
|
|
268
291
|
: runtimeConfigUnavailableDetail(agent, machineRuntimeConfig),
|
|
269
292
|
};
|
|
270
293
|
}
|
|
294
|
+
if (senses.a2a.enabled) {
|
|
295
|
+
const port = numberField(a2a, "port", (0, config_1.defaultA2APort)(agent));
|
|
296
|
+
const endpointPath = (0, config_1.normalizeA2APath)(textField(a2a, "path") || config_1.A2A_DEFAULT_PATH);
|
|
297
|
+
const publicUrl = textField(a2a, "publicUrl");
|
|
298
|
+
base.a2a = {
|
|
299
|
+
configured: true,
|
|
300
|
+
/* v8 ignore next -- listSenseRows tests cover the public URL; daemon defaults cover the local port in live startup smoke @preserve */
|
|
301
|
+
detail: publicUrl ? `${publicUrl}${endpointPath}` : `:${port} ${endpointPath}`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
271
304
|
return base;
|
|
272
305
|
}
|
|
273
306
|
function senseRepairHint(agent, sense) {
|
|
@@ -280,6 +313,10 @@ function senseRepairHint(agent, sense) {
|
|
|
280
313
|
if (sense === "voice") {
|
|
281
314
|
return `Agent-runnable: run 'ouro connect voice --agent ${agent}' for config guidance; use voice.twilioConversationEngine=openai-sip with voice.openaiRealtimeApiKey, voice.openaiSipProjectId, and voice.openaiSipWebhookSecret for preferred SIP phone voice; use openai-realtime for Media Streams fallback, or save ElevenLabs and local Whisper.cpp settings for cascade fallback; then run 'ouro up' again.`;
|
|
282
315
|
}
|
|
316
|
+
/* v8 ignore next -- A2A currently has no credential-gated not-configured state; kept for future repair copy symmetry @preserve */
|
|
317
|
+
if (sense === "a2a") {
|
|
318
|
+
return `Agent-runnable: run 'ouro connect a2a --agent ${agent}', then restart with 'ouro up'.`;
|
|
319
|
+
}
|
|
283
320
|
return `Run 'ouro connect bluebubbles --agent ${agent}' to attach BlueBubbles on this machine; then run 'ouro up' again.`;
|
|
284
321
|
}
|
|
285
322
|
function currentMachineId() {
|
|
@@ -290,7 +327,7 @@ function parseSenseSnapshotName(name) {
|
|
|
290
327
|
if (parts.length !== 2)
|
|
291
328
|
return null;
|
|
292
329
|
const [agent, sense] = parts;
|
|
293
|
-
if (sense !== "teams" && sense !== "bluebubbles" && sense !== "mail" && sense !== "voice")
|
|
330
|
+
if (sense !== "teams" && sense !== "bluebubbles" && sense !== "mail" && sense !== "voice" && sense !== "a2a")
|
|
294
331
|
return null;
|
|
295
332
|
return { agent, sense };
|
|
296
333
|
}
|
|
@@ -306,12 +343,14 @@ function managedSenseEntry(sense) {
|
|
|
306
343
|
return "senses/bluebubbles/entry.js";
|
|
307
344
|
if (sense === "voice")
|
|
308
345
|
return "senses/voice-entry.js";
|
|
346
|
+
if (sense === "a2a")
|
|
347
|
+
return "senses/a2a-entry.js";
|
|
309
348
|
return "senses/mail-entry.js";
|
|
310
349
|
}
|
|
311
350
|
function runtimeCredentialBootstrapFor(agent, sense) {
|
|
312
351
|
const runtime = (0, runtime_credentials_1.readRuntimeCredentialConfig)(agent);
|
|
313
|
-
const machineId = sense === "bluebubbles" || sense === "voice" ? currentMachineId() : undefined;
|
|
314
|
-
const machine = sense === "bluebubbles" || sense === "voice" ? (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent) : null;
|
|
352
|
+
const machineId = sense === "bluebubbles" || sense === "voice" || sense === "a2a" ? currentMachineId() : undefined;
|
|
353
|
+
const machine = sense === "bluebubbles" || sense === "voice" || sense === "a2a" ? (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent) : null;
|
|
315
354
|
const providerPool = (0, provider_credentials_1.readProviderCredentialPool)(agent);
|
|
316
355
|
const providerCredentialRecords = providerPool.ok
|
|
317
356
|
? Object.values(providerPool.pool.providers).filter((record) => !!record)
|
|
@@ -478,7 +517,7 @@ class DaemonSenseManager {
|
|
|
478
517
|
return [agent, { senses, facts }];
|
|
479
518
|
}));
|
|
480
519
|
const managedSenseAgents = [...this.contexts.entries()].flatMap(([agent, context]) => {
|
|
481
|
-
return ["teams", "bluebubbles", "mail", "voice"]
|
|
520
|
+
return ["teams", "bluebubbles", "mail", "voice", "a2a"]
|
|
482
521
|
.filter((sense) => context.senses[sense].enabled)
|
|
483
522
|
.map((sense) => ({
|
|
484
523
|
name: `${agent}:${sense}`,
|
|
@@ -486,6 +525,7 @@ class DaemonSenseManager {
|
|
|
486
525
|
entry: managedSenseEntry(sense),
|
|
487
526
|
channel: sense,
|
|
488
527
|
autoStart: true,
|
|
528
|
+
...(sense === "a2a" ? { args: managedA2AArgs(agent), getArgs: () => managedA2AArgs(agent) } : {}),
|
|
489
529
|
getRuntimeCredentialBootstrap: () => runtimeCredentialBootstrapFor(agent, sense),
|
|
490
530
|
}));
|
|
491
531
|
});
|
|
@@ -537,7 +577,7 @@ class DaemonSenseManager {
|
|
|
537
577
|
async refreshSenseConfigAndRetry(name, parsed) {
|
|
538
578
|
try {
|
|
539
579
|
const refreshed = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(parsed.agent, { preserveCachedOnFailure: true });
|
|
540
|
-
const machineRefreshed = parsed.sense === "bluebubbles" || parsed.sense === "voice"
|
|
580
|
+
const machineRefreshed = parsed.sense === "bluebubbles" || parsed.sense === "voice" || parsed.sense === "a2a"
|
|
541
581
|
? await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(parsed.agent, currentMachineId(), { preserveCachedOnFailure: true })
|
|
542
582
|
: (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(parsed.agent);
|
|
543
583
|
const context = this.contexts.get(parsed.agent);
|
|
@@ -576,14 +616,14 @@ class DaemonSenseManager {
|
|
|
576
616
|
}
|
|
577
617
|
async refreshEnabledSenseConfigs() {
|
|
578
618
|
const refreshes = [...this.contexts.entries()].map(async ([agent, context]) => {
|
|
579
|
-
const enabledManagedSenses = ["teams", "bluebubbles", "mail", "voice"]
|
|
619
|
+
const enabledManagedSenses = ["teams", "bluebubbles", "mail", "voice", "a2a"]
|
|
580
620
|
.filter((sense) => context.senses[sense].enabled);
|
|
581
621
|
/* v8 ignore next -- periodic refresh work only exists when a managed background sense is enabled @preserve */
|
|
582
622
|
if (enabledManagedSenses.length === 0)
|
|
583
623
|
return;
|
|
584
624
|
/* v8 ignore start -- periodic freshness refresh uses the same runtime readers covered by startup integration tests @preserve */
|
|
585
625
|
const runtimeConfig = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
586
|
-
const needsMachineConfig = enabledManagedSenses.some((sense) => sense === "bluebubbles" || sense === "voice");
|
|
626
|
+
const needsMachineConfig = enabledManagedSenses.some((sense) => sense === "bluebubbles" || sense === "voice" || sense === "a2a");
|
|
587
627
|
const machineRuntimeConfig = needsMachineConfig
|
|
588
628
|
? await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(agent, currentMachineId(), { preserveCachedOnFailure: true })
|
|
589
629
|
: (0, runtime_credentials_1.readMachineRuntimeCredentialConfig)(agent);
|
|
@@ -597,17 +637,29 @@ class DaemonSenseManager {
|
|
|
597
637
|
await this.processManager.startAutoStartAgents();
|
|
598
638
|
}
|
|
599
639
|
triggerAutoStartSenses() {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
604
|
-
void this.processManager.startAutoStartAgents().catch((error) => {
|
|
640
|
+
void this.refreshEnabledSenseConfigs()
|
|
641
|
+
.catch((error) => {
|
|
605
642
|
(0, runtime_1.emitNervesEvent)({
|
|
606
643
|
level: "error",
|
|
607
644
|
component: "channels",
|
|
608
645
|
event: "channel.daemon_sense_autostart_error",
|
|
609
|
-
message: "sense
|
|
610
|
-
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
646
|
+
message: "sense config refresh failed",
|
|
647
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive non-Error refresh rejection @preserve */ String(error) },
|
|
648
|
+
});
|
|
649
|
+
})
|
|
650
|
+
.then(() => {
|
|
651
|
+
if (this.processManager.triggerAutoStartAgents) {
|
|
652
|
+
this.processManager.triggerAutoStartAgents();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
void this.processManager.startAutoStartAgents().catch((error) => {
|
|
656
|
+
(0, runtime_1.emitNervesEvent)({
|
|
657
|
+
level: "error",
|
|
658
|
+
component: "channels",
|
|
659
|
+
event: "channel.daemon_sense_autostart_error",
|
|
660
|
+
message: "sense autostart failed",
|
|
661
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
662
|
+
});
|
|
611
663
|
});
|
|
612
664
|
});
|
|
613
665
|
}
|
|
@@ -712,6 +764,10 @@ class DaemonSenseManager {
|
|
|
712
764
|
optional: context.facts.voice.optional,
|
|
713
765
|
...(runtime.get(agent)?.voice ?? {}),
|
|
714
766
|
},
|
|
767
|
+
a2a: {
|
|
768
|
+
configured: context.facts.a2a.configured,
|
|
769
|
+
...(runtime.get(agent)?.a2a ?? {}),
|
|
770
|
+
},
|
|
715
771
|
};
|
|
716
772
|
const inventory = (0, sense_truth_1.getSenseInventory)({ senses: context.senses }, runtimeInfo);
|
|
717
773
|
return inventory.map((entry) => ({
|