@ouro.bot/cli 0.1.0-alpha.655 → 0.1.0-alpha.658
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 +13 -13
- package/changelog.json +21 -0
- package/dist/arc/evolution.js +1 -1
- package/dist/arc/flight-recorder.js +369 -0
- package/dist/arc/obligations.js +24 -2
- package/dist/heart/active-work.js +1 -1
- package/dist/heart/config-registry.js +14 -5
- package/dist/heart/daemon/agent-config-check.js +1 -1
- package/dist/heart/daemon/agent-service.js +18 -17
- package/dist/heart/daemon/cli-exec.js +134 -15
- package/dist/heart/daemon/cli-help.js +21 -2
- package/dist/heart/daemon/cli-parse.js +31 -3
- package/dist/heart/daemon/daemon-entry.js +1 -1
- package/dist/heart/daemon/daemon.js +3 -3
- package/dist/heart/daemon/hooks/bundle-meta.js +29 -9
- package/dist/heart/daemon/inner-status.js +4 -15
- package/dist/heart/daemon/sense-manager.js +16 -1
- package/dist/heart/habits/habit-parser.js +64 -1
- package/dist/heart/hatch/hatch-flow.js +17 -9
- package/dist/heart/hatch/specialist-tools.js +15 -11
- package/dist/heart/identity.js +4 -1
- package/dist/heart/kept-notes.js +5 -73
- package/dist/heart/mailbox/readers/runtime-readers.js +21 -49
- package/dist/heart/mcp/mcp-server.js +8 -8
- package/dist/heart/sense-truth.js +2 -0
- package/dist/heart/session-events.js +1 -31
- package/dist/heart/start-of-turn-packet.js +8 -2
- package/dist/heart/tool-description.js +15 -3
- package/dist/heart/turn-context.js +34 -7
- package/dist/heart/work-card.js +386 -0
- package/dist/mailbox-ui/assets/{index-9-AxCxuB.js → index-Cbasiy6y.js} +1 -1
- package/dist/mailbox-ui/index.html +1 -1
- package/dist/mind/bundle-manifest.js +9 -3
- package/dist/mind/context.js +1 -2
- package/dist/mind/desk-section.js +53 -1
- package/dist/mind/diary.js +2 -3
- package/dist/mind/note-search.js +36 -106
- package/dist/mind/prompt.js +45 -102
- package/dist/mind/record-paths.js +312 -0
- package/dist/repertoire/bundle-templates.js +4 -5
- package/dist/repertoire/tools-bundle.js +1 -1
- package/dist/repertoire/tools-evolution.js +4 -4
- package/dist/repertoire/tools-notes.js +42 -62
- package/dist/repertoire/tools-record.js +16 -11
- package/dist/repertoire/tools-session.js +4 -4
- package/dist/repertoire/tools.js +1 -1
- package/dist/senses/habit-turn-message.js +19 -5
- package/dist/senses/inner-dialog-worker.js +58 -9
- package/dist/senses/inner-dialog.js +30 -11
- package/dist/senses/pipeline.js +135 -1
- package/dist/util/frontmatter.js +17 -1
- package/package.json +3 -3
- package/skills/configure-dev-tools.md +1 -1
- package/skills/travel-planning.md +1 -1
- package/dist/mind/journal-index.js +0 -162
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Agent service layer — handles MCP-facing daemon commands.
|
|
4
4
|
* Each handler receives { agent, friendId, ...params } and returns DaemonResponse.
|
|
5
5
|
*
|
|
6
|
-
* DRY: uses the same shared functions the agent's own tools use (diary, session transcript).
|
|
6
|
+
* DRY: uses the same shared functions the agent's own tools use (Desk record diary, session transcript).
|
|
7
7
|
* This file is a thin adapter — no reimplemented search, parsing, or state reading.
|
|
8
8
|
*/
|
|
9
9
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
@@ -45,7 +45,7 @@ exports.handleAgentAsk = handleAgentAsk;
|
|
|
45
45
|
exports.handleAgentCatchup = handleAgentCatchup;
|
|
46
46
|
exports.handleAgentDelegate = handleAgentDelegate;
|
|
47
47
|
exports.handleAgentGetContext = handleAgentGetContext;
|
|
48
|
-
exports.
|
|
48
|
+
exports.handleAgentSearchFacts = handleAgentSearchFacts;
|
|
49
49
|
exports.handleAgentGetTask = handleAgentGetTask;
|
|
50
50
|
exports.handleAgentCheckScope = handleAgentCheckScope;
|
|
51
51
|
exports.handleAgentRequestDecision = handleAgentRequestDecision;
|
|
@@ -57,9 +57,10 @@ const fs = __importStar(require("fs"));
|
|
|
57
57
|
const path = __importStar(require("path"));
|
|
58
58
|
const identity_1 = require("../identity");
|
|
59
59
|
const diary_1 = require("../../mind/diary");
|
|
60
|
+
const record_paths_1 = require("../../mind/record-paths");
|
|
60
61
|
const runtime_1 = require("../../nerves/runtime");
|
|
61
62
|
const socket_client_1 = require("./socket-client");
|
|
62
|
-
/** Format diary hits the same way the
|
|
63
|
+
/** Format diary hits the same way the search_facts tool does. */
|
|
63
64
|
function formatDiaryHits(hits) {
|
|
64
65
|
return hits.map((f) => `[diary] ${f.text} (source=${f.source}, createdAt=${f.createdAt})`);
|
|
65
66
|
}
|
|
@@ -72,7 +73,7 @@ function readAgentFile(agent, ...segments) {
|
|
|
72
73
|
}
|
|
73
74
|
/** Resolve the diary root for a specific agent. */
|
|
74
75
|
function agentDiaryRoot(agent) {
|
|
75
|
-
return (0,
|
|
76
|
+
return (0, record_paths_1.resolveRecordDiaryRoot)((0, identity_1.getAgentRoot)(agent));
|
|
76
77
|
}
|
|
77
78
|
/** Read inner dialog runtime status. */
|
|
78
79
|
function readInnerDialogStatus(agent) {
|
|
@@ -339,12 +340,12 @@ async function handleAgentAsk(params) {
|
|
|
339
340
|
emit("daemon.agent_service_error", "agent.ask missing question", { agent: params.agent });
|
|
340
341
|
return { ok: false, error: "Missing required parameter: question" };
|
|
341
342
|
}
|
|
342
|
-
// Use the same searchDiaryEntries the
|
|
343
|
+
// Use the same searchDiaryEntries the search_facts tool uses (substring fallback; no embedding provider in shim).
|
|
343
344
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
344
345
|
const hits = await (0, diary_1.searchDiaryEntries)(question, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
345
346
|
const context = hits.length > 0
|
|
346
347
|
? hits.slice(0, 10).map((f) => f.text).join("\n")
|
|
347
|
-
: `No relevant
|
|
348
|
+
: `No relevant facts found for: ${question}`;
|
|
348
349
|
emit("daemon.agent_service_end", "completed agent.ask", { agent: params.agent });
|
|
349
350
|
return { ok: true, message: context };
|
|
350
351
|
}
|
|
@@ -393,17 +394,17 @@ async function handleAgentGetContext(params) {
|
|
|
393
394
|
const innerStatus = readInnerDialogStatus(params.agent);
|
|
394
395
|
const sessions = enumerateSessions(params.agent);
|
|
395
396
|
const taskFiles = listTaskFiles(params.agent);
|
|
396
|
-
let
|
|
397
|
+
let recordSummary = null;
|
|
397
398
|
if (query) {
|
|
398
399
|
const hits = await (0, diary_1.searchDiaryEntries)(query, facts);
|
|
399
|
-
|
|
400
|
+
recordSummary = hits.length > 0
|
|
400
401
|
? hits.slice(0, 10).map((f) => f.text).join("\n")
|
|
401
|
-
: `No relevant
|
|
402
|
+
: `No relevant facts for: ${query}`;
|
|
402
403
|
}
|
|
403
404
|
else {
|
|
404
405
|
const recent = facts.slice(-10);
|
|
405
406
|
if (recent.length > 0)
|
|
406
|
-
|
|
407
|
+
recordSummary = recent.map((f) => f.text).join("\n");
|
|
407
408
|
}
|
|
408
409
|
emit("daemon.agent_service_end", "completed agent.getContext", { agent: params.agent });
|
|
409
410
|
return {
|
|
@@ -412,25 +413,25 @@ async function handleAgentGetContext(params) {
|
|
|
412
413
|
agent: params.agent,
|
|
413
414
|
hasDiaryEntries: facts.length > 0,
|
|
414
415
|
factCount: facts.length,
|
|
415
|
-
|
|
416
|
+
recordSummary,
|
|
416
417
|
taskCount: taskFiles.length,
|
|
417
418
|
sessionCount: sessions.length,
|
|
418
419
|
innerStatus: innerStatus?.status ?? null,
|
|
419
420
|
},
|
|
420
421
|
};
|
|
421
422
|
}
|
|
422
|
-
async function
|
|
423
|
-
emit("daemon.agent_service_start", "handling agent.
|
|
423
|
+
async function handleAgentSearchFacts(params) {
|
|
424
|
+
emit("daemon.agent_service_start", "handling agent.searchFacts", { agent: params.agent });
|
|
424
425
|
const query = params.query;
|
|
425
426
|
if (!query) {
|
|
426
|
-
emit("daemon.agent_service_error", "agent.
|
|
427
|
+
emit("daemon.agent_service_error", "agent.searchFacts missing query", { agent: params.agent });
|
|
427
428
|
return { ok: false, error: "Missing required parameter: query" };
|
|
428
429
|
}
|
|
429
|
-
// Same searchDiaryEntries as the
|
|
430
|
+
// Same searchDiaryEntries as the search_facts tool.
|
|
430
431
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
431
432
|
const hits = await (0, diary_1.searchDiaryEntries)(query, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
432
433
|
const formatted = formatDiaryHits(hits.slice(0, 20));
|
|
433
|
-
emit("daemon.agent_service_end", "completed agent.
|
|
434
|
+
emit("daemon.agent_service_end", "completed agent.searchFacts", { agent: params.agent, matchCount: hits.length });
|
|
434
435
|
return {
|
|
435
436
|
ok: true,
|
|
436
437
|
message: hits.length > 0 ? `Found ${hits.length} matches` : "No matches found",
|
|
@@ -481,7 +482,7 @@ async function handleAgentCheckGuidance(params) {
|
|
|
481
482
|
emit("daemon.agent_service_error", "agent.checkGuidance missing topic", { agent: params.agent });
|
|
482
483
|
return { ok: false, error: "Missing required parameter: topic" };
|
|
483
484
|
}
|
|
484
|
-
// Same searchDiaryEntries as the
|
|
485
|
+
// Same searchDiaryEntries as the search_facts tool.
|
|
485
486
|
const diaryRoot = agentDiaryRoot(params.agent);
|
|
486
487
|
const hits = await (0, diary_1.searchDiaryEntries)(topic, (0, diary_1.readDiaryEntries)(diaryRoot));
|
|
487
488
|
const guidance = hits.length > 0
|
|
@@ -365,6 +365,7 @@ function agentResolutionFailureMode(command) {
|
|
|
365
365
|
case "attention.list":
|
|
366
366
|
case "attention.show":
|
|
367
367
|
case "attention.history":
|
|
368
|
+
case "work.card":
|
|
368
369
|
case "inner.status":
|
|
369
370
|
case "session.list":
|
|
370
371
|
return "return-message";
|
|
@@ -2496,25 +2497,64 @@ function enableAgentSense(agent, sense, deps) {
|
|
|
2496
2497
|
mail: senses.mail ?? { enabled: false },
|
|
2497
2498
|
voice: senses.voice ?? { enabled: false },
|
|
2498
2499
|
a2a: senses.a2a ?? { enabled: false },
|
|
2500
|
+
workbench: senses.workbench ?? { enabled: false },
|
|
2499
2501
|
[sense]: { ...existing, enabled: true },
|
|
2500
2502
|
};
|
|
2501
2503
|
fs.writeFileSync(configPath, `${JSON.stringify(raw, null, 2)}\n`, "utf-8");
|
|
2502
2504
|
}
|
|
2503
|
-
const CONNECT_MENU_PROMPT = "Choose [1-
|
|
2505
|
+
const CONNECT_MENU_PROMPT = "Choose [1-9] or type a name: ";
|
|
2504
2506
|
function connectMenuIsTTY(deps) {
|
|
2505
2507
|
return deps.isTTY ?? process.stdout.isTTY === true;
|
|
2506
2508
|
}
|
|
2507
2509
|
function readConnectBaySenseFlags(agent, deps) {
|
|
2508
2510
|
const configPath = path.join(providerCliAgentRoot({ agent }, deps), "agent.json");
|
|
2509
2511
|
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
2512
|
+
const workbenchCommand = parsed.mcpServers?.ouro_workbench?.command;
|
|
2510
2513
|
return {
|
|
2511
2514
|
teamsEnabled: parsed.senses?.teams?.enabled === true,
|
|
2512
2515
|
blueBubblesEnabled: parsed.senses?.bluebubbles?.enabled === true,
|
|
2513
2516
|
mailEnabled: parsed.senses?.mail?.enabled === true,
|
|
2514
2517
|
voiceEnabled: parsed.senses?.voice?.enabled === true,
|
|
2515
2518
|
a2aEnabled: parsed.senses?.a2a?.enabled === true,
|
|
2519
|
+
workbenchEnabled: parsed.senses?.workbench?.enabled === true,
|
|
2520
|
+
workbenchMcpCommand: typeof workbenchCommand === "string" && workbenchCommand.trim().length > 0
|
|
2521
|
+
? workbenchCommand
|
|
2522
|
+
: null,
|
|
2516
2523
|
};
|
|
2517
2524
|
}
|
|
2525
|
+
function defaultWorkbenchMcpCandidates(deps) {
|
|
2526
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
2527
|
+
return [
|
|
2528
|
+
path.join(homeDir, "Applications", "Ouro Workbench.app", "Contents", "MacOS", "OuroWorkbenchMCP"),
|
|
2529
|
+
path.join("/Applications", "Ouro Workbench.app", "Contents", "MacOS", "OuroWorkbenchMCP"),
|
|
2530
|
+
];
|
|
2531
|
+
}
|
|
2532
|
+
function cliPathExists(deps, filePath) {
|
|
2533
|
+
return !!filePath && (deps.existsSync ?? fs.existsSync)(filePath);
|
|
2534
|
+
}
|
|
2535
|
+
function findInstalledWorkbenchMcp(deps, preferred) {
|
|
2536
|
+
const candidates = [
|
|
2537
|
+
...(preferred ? [preferred] : []),
|
|
2538
|
+
...defaultWorkbenchMcpCandidates(deps),
|
|
2539
|
+
];
|
|
2540
|
+
return candidates.find((candidate) => cliPathExists(deps, candidate)) ?? null;
|
|
2541
|
+
}
|
|
2542
|
+
function writeWorkbenchMcpRegistration(agent, executablePath, deps) {
|
|
2543
|
+
const { configPath } = (0, auth_flow_1.readAgentConfigForAgent)(agent, deps.bundlesRoot);
|
|
2544
|
+
enableAgentSense(agent, "workbench", deps);
|
|
2545
|
+
const nextRaw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
2546
|
+
const mcpServers = nextRaw.mcpServers && typeof nextRaw.mcpServers === "object" && !Array.isArray(nextRaw.mcpServers)
|
|
2547
|
+
? nextRaw.mcpServers
|
|
2548
|
+
: {};
|
|
2549
|
+
nextRaw.mcpServers = {
|
|
2550
|
+
...mcpServers,
|
|
2551
|
+
ouro_workbench: {
|
|
2552
|
+
command: executablePath,
|
|
2553
|
+
args: [],
|
|
2554
|
+
},
|
|
2555
|
+
};
|
|
2556
|
+
fs.writeFileSync(configPath, `${JSON.stringify(nextRaw, null, 2)}\n`, "utf-8");
|
|
2557
|
+
}
|
|
2518
2558
|
async function buildConnectMenu(agent, deps, onProgress) {
|
|
2519
2559
|
const bundlesRoot = path.dirname(providerCliAgentRoot({ agent }, deps));
|
|
2520
2560
|
let providerHealth;
|
|
@@ -2542,7 +2582,7 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2542
2582
|
const runtimeConfig = await (0, runtime_credentials_1.refreshRuntimeCredentialConfig)(agent, { preserveCachedOnFailure: true });
|
|
2543
2583
|
onProgress?.("loading this machine's settings");
|
|
2544
2584
|
const machineRuntime = await (0, runtime_credentials_1.refreshMachineRuntimeCredentialConfig)(agent, currentMachineId(deps), { preserveCachedOnFailure: true });
|
|
2545
|
-
const { teamsEnabled, blueBubblesEnabled, mailEnabled, voiceEnabled, a2aEnabled } = readConnectBaySenseFlags(agent, deps);
|
|
2585
|
+
const { teamsEnabled, blueBubblesEnabled, mailEnabled, voiceEnabled, a2aEnabled, workbenchEnabled, workbenchMcpCommand, } = readConnectBaySenseFlags(agent, deps);
|
|
2546
2586
|
const perplexityApiKey = runtimeConfig.ok
|
|
2547
2587
|
? readRuntimeConfigString(runtimeConfig.config, "integrations.perplexityApiKey")
|
|
2548
2588
|
: null;
|
|
@@ -2698,6 +2738,26 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2698
2738
|
: "missing"
|
|
2699
2739
|
: runtimeConfigReadStatus(runtimeConfig);
|
|
2700
2740
|
const a2aStatus = a2aEnabled ? "ready" : "missing";
|
|
2741
|
+
const installedWorkbenchMcp = findInstalledWorkbenchMcp(deps, workbenchMcpCommand);
|
|
2742
|
+
const configuredWorkbenchMcpExists = cliPathExists(deps, workbenchMcpCommand);
|
|
2743
|
+
const workbenchStatus = workbenchEnabled && configuredWorkbenchMcpExists
|
|
2744
|
+
? "ready"
|
|
2745
|
+
: workbenchEnabled || workbenchMcpCommand
|
|
2746
|
+
? "needs attention"
|
|
2747
|
+
: installedWorkbenchMcp
|
|
2748
|
+
? "not attached"
|
|
2749
|
+
: "missing";
|
|
2750
|
+
const workbenchDetailLines = [
|
|
2751
|
+
workbenchEnabled ? "senses.workbench.enabled = true" : "senses.workbench.enabled is not enabled",
|
|
2752
|
+
workbenchMcpCommand
|
|
2753
|
+
? configuredWorkbenchMcpExists
|
|
2754
|
+
? `MCP command registered: ${workbenchMcpCommand}`
|
|
2755
|
+
: `registered MCP command is missing: ${workbenchMcpCommand}`
|
|
2756
|
+
: "mcpServers.ouro_workbench is not registered",
|
|
2757
|
+
installedWorkbenchMcp
|
|
2758
|
+
? `OuroWorkbenchMCP found: ${installedWorkbenchMcp}`
|
|
2759
|
+
: "OuroWorkbenchMCP not found in ~/Applications or /Applications",
|
|
2760
|
+
];
|
|
2701
2761
|
const entries = [
|
|
2702
2762
|
{
|
|
2703
2763
|
option: "1",
|
|
@@ -2826,6 +2886,20 @@ async function buildConnectMenu(agent, deps, onProgress) {
|
|
|
2826
2886
|
status: a2aStatus,
|
|
2827
2887
|
}) ? `ouro connect a2a --agent ${agent}` : undefined,
|
|
2828
2888
|
},
|
|
2889
|
+
{
|
|
2890
|
+
option: "9",
|
|
2891
|
+
name: "Ouro Workbench",
|
|
2892
|
+
section: "This machine",
|
|
2893
|
+
status: workbenchStatus,
|
|
2894
|
+
description: "Native terminal-agent control room and local MCP sense.",
|
|
2895
|
+
detailLines: workbenchDetailLines,
|
|
2896
|
+
nextAction: (0, connect_bay_1.connectEntryNeedsAttention)({
|
|
2897
|
+
option: "9",
|
|
2898
|
+
name: "Ouro Workbench",
|
|
2899
|
+
section: "This machine",
|
|
2900
|
+
status: workbenchStatus,
|
|
2901
|
+
}) ? `ouro connect workbench --agent ${agent}` : undefined,
|
|
2902
|
+
},
|
|
2829
2903
|
];
|
|
2830
2904
|
const isTTY = connectMenuIsTTY(deps);
|
|
2831
2905
|
return (0, connect_bay_1.renderConnectBay)(entries, {
|
|
@@ -4364,6 +4438,8 @@ function connectMenuTarget(answer) {
|
|
|
4364
4438
|
/* v8 ignore next -- direct `ouro connect a2a` covers behavior; interactive menu requires broader provider/vault readiness work @preserve */
|
|
4365
4439
|
if (normalized === "8" || normalized === "a2a" || normalized === "agent2agent" || normalized === "agent-to-agent")
|
|
4366
4440
|
return "a2a";
|
|
4441
|
+
if (normalized === "9" || normalized === "workbench" || normalized === "ouro-workbench" || normalized === "terminal-workbench")
|
|
4442
|
+
return "workbench";
|
|
4367
4443
|
return "cancel";
|
|
4368
4444
|
}
|
|
4369
4445
|
async function executeConnectVoice(agent, deps) {
|
|
@@ -4421,6 +4497,30 @@ async function executeConnectA2A(agent, deps) {
|
|
|
4421
4497
|
deps.writeStdout(message);
|
|
4422
4498
|
return message;
|
|
4423
4499
|
}
|
|
4500
|
+
async function executeConnectWorkbench(agent, deps) {
|
|
4501
|
+
const { workbenchMcpCommand } = readConnectBaySenseFlags(agent, deps);
|
|
4502
|
+
const executablePath = findInstalledWorkbenchMcp(deps, workbenchMcpCommand);
|
|
4503
|
+
if (!executablePath) {
|
|
4504
|
+
throw new Error([
|
|
4505
|
+
`Ouro Workbench is not installed for ${agent} on this machine.`,
|
|
4506
|
+
"Expected the MCP executable at one of:",
|
|
4507
|
+
...defaultWorkbenchMcpCandidates(deps).map((candidate) => ` ${candidate}`),
|
|
4508
|
+
"Install or copy Ouro Workbench.app there, then run:",
|
|
4509
|
+
` ouro connect workbench --agent ${agent}`,
|
|
4510
|
+
].join("\n"));
|
|
4511
|
+
}
|
|
4512
|
+
writeWorkbenchMcpRegistration(agent, executablePath, deps);
|
|
4513
|
+
const syncSummary = pushAgentBundleAfterCliMutation(agent, deps);
|
|
4514
|
+
const message = [
|
|
4515
|
+
`Workbench connected for ${agent}`,
|
|
4516
|
+
"agent.json: senses.workbench.enabled = true",
|
|
4517
|
+
`agent.json: mcpServers.ouro_workbench.command = ${executablePath}`,
|
|
4518
|
+
"Workbench is a local machine sense; provider secrets stay in the agent vault.",
|
|
4519
|
+
...(syncSummary ? [syncSummary] : []),
|
|
4520
|
+
].join("\n");
|
|
4521
|
+
deps.writeStdout(message);
|
|
4522
|
+
return message;
|
|
4523
|
+
}
|
|
4424
4524
|
async function executeConnect(command, deps) {
|
|
4425
4525
|
if (command.target === "providers")
|
|
4426
4526
|
return executeConnectProviders(command.agent, deps);
|
|
@@ -4438,6 +4538,8 @@ async function executeConnect(command, deps) {
|
|
|
4438
4538
|
return executeConnectVoice(command.agent, deps);
|
|
4439
4539
|
if (command.target === "a2a")
|
|
4440
4540
|
return executeConnectA2A(command.agent, deps);
|
|
4541
|
+
if (command.target === "workbench")
|
|
4542
|
+
return executeConnectWorkbench(command.agent, deps);
|
|
4441
4543
|
const progress = createHumanCommandProgress(deps, "connect");
|
|
4442
4544
|
let menu;
|
|
4443
4545
|
try {
|
|
@@ -4449,7 +4551,7 @@ async function executeConnect(command, deps) {
|
|
|
4449
4551
|
const promptInput = deps.promptInput;
|
|
4450
4552
|
if (!promptInput) {
|
|
4451
4553
|
const message = [
|
|
4452
|
-
menu.replace(/\nChoose \[1-
|
|
4554
|
+
menu.replace(/\nChoose \[1-9\] or type a name: $/, ""),
|
|
4453
4555
|
"",
|
|
4454
4556
|
`Run: ouro connect providers --agent ${command.agent}`,
|
|
4455
4557
|
`Run: ouro connect perplexity --agent ${command.agent}`,
|
|
@@ -4459,6 +4561,7 @@ async function executeConnect(command, deps) {
|
|
|
4459
4561
|
`Run: ouro connect mail --agent ${command.agent}`,
|
|
4460
4562
|
`Run: ouro connect voice --agent ${command.agent}`,
|
|
4461
4563
|
`Run: ouro connect a2a --agent ${command.agent}`,
|
|
4564
|
+
`Run: ouro connect workbench --agent ${command.agent}`,
|
|
4462
4565
|
].join("\n");
|
|
4463
4566
|
deps.writeStdout(message);
|
|
4464
4567
|
return message;
|
|
@@ -4481,6 +4584,8 @@ async function executeConnect(command, deps) {
|
|
|
4481
4584
|
/* v8 ignore next -- direct `ouro connect a2a` covers behavior; interactive menu requires broader provider/vault readiness work @preserve */
|
|
4482
4585
|
if (answer === "a2a")
|
|
4483
4586
|
return executeConnectA2A(command.agent, deps);
|
|
4587
|
+
if (answer === "workbench")
|
|
4588
|
+
return executeConnectWorkbench(command.agent, deps);
|
|
4484
4589
|
const message = "connect cancelled.";
|
|
4485
4590
|
deps.writeStdout(message);
|
|
4486
4591
|
return message;
|
|
@@ -7173,6 +7278,18 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7173
7278
|
}
|
|
7174
7279
|
}
|
|
7175
7280
|
/* v8 ignore stop */
|
|
7281
|
+
// ── work card (local, no daemon socket needed) ──
|
|
7282
|
+
if (command.kind === "work.card") {
|
|
7283
|
+
const { buildWorkCard, formatWorkCardText } = await Promise.resolve().then(() => __importStar(require("../work-card")));
|
|
7284
|
+
if (!command.agent)
|
|
7285
|
+
throw new Error("work card requires --agent <name>");
|
|
7286
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
7287
|
+
const agentRoot = deps.agentBundleRoot ?? path.join(bundlesRoot, `${command.agent}.ouro`);
|
|
7288
|
+
const card = buildWorkCard(command.agent, agentRoot);
|
|
7289
|
+
const message = command.format === "json" ? JSON.stringify(card, null, 2) : formatWorkCardText(card);
|
|
7290
|
+
deps.writeStdout(message);
|
|
7291
|
+
return message;
|
|
7292
|
+
}
|
|
7176
7293
|
// ── inner dialog status (local, no daemon socket needed) ──
|
|
7177
7294
|
/* v8 ignore start -- inner status handler: requires real agent state on disk @preserve */
|
|
7178
7295
|
if (command.kind === "inner.status") {
|
|
@@ -7182,6 +7299,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7182
7299
|
const { parseCadenceToMs: parseCadenceMs, DEFAULT_CADENCE_MS } = await Promise.resolve().then(() => __importStar(require("./cadence")));
|
|
7183
7300
|
const { parseFrontmatter } = await Promise.resolve().then(() => __importStar(require("../../util/frontmatter")));
|
|
7184
7301
|
const { listActiveReturnObligations } = await Promise.resolve().then(() => __importStar(require("../../arc/obligations")));
|
|
7302
|
+
const { resolveDeskRecordPaths } = await Promise.resolve().then(() => __importStar(require("../../mind/record-paths")));
|
|
7185
7303
|
// Read runtime state
|
|
7186
7304
|
const innerSessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
7187
7305
|
const runtimeJsonPath = path.join(path.dirname(innerSessionPath), "runtime.json");
|
|
@@ -7191,19 +7309,20 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7191
7309
|
runtimeState = JSON.parse(raw);
|
|
7192
7310
|
}
|
|
7193
7311
|
catch { /* missing or corrupt — will show "unknown" */ }
|
|
7194
|
-
// Read
|
|
7195
|
-
const
|
|
7196
|
-
|
|
7312
|
+
// Read canonical Desk record summary
|
|
7313
|
+
const recordPaths = resolveDeskRecordPaths(agentRoot);
|
|
7314
|
+
const recordSummary = { diaryFactCount: 0, noteCount: 0 };
|
|
7197
7315
|
try {
|
|
7198
|
-
const
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7316
|
+
const rawFacts = fs.readFileSync(recordPaths.factsPath, "utf-8");
|
|
7317
|
+
recordSummary.diaryFactCount = rawFacts.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
|
|
7318
|
+
}
|
|
7319
|
+
catch { /* missing facts file — count stays zero */ }
|
|
7320
|
+
try {
|
|
7321
|
+
recordSummary.noteCount = fs.readdirSync(recordPaths.notesRoot, { withFileTypes: true })
|
|
7322
|
+
.filter((e) => e.isFile() && !e.name.startsWith(".") && e.name.endsWith(".md"))
|
|
7323
|
+
.length;
|
|
7205
7324
|
}
|
|
7206
|
-
catch { /* missing dir —
|
|
7325
|
+
catch { /* missing notes dir — count stays zero */ }
|
|
7207
7326
|
// Read heartbeat cadence
|
|
7208
7327
|
let heartbeat = null;
|
|
7209
7328
|
try {
|
|
@@ -7238,7 +7357,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
7238
7357
|
const message = buildInnerStatusOutput({
|
|
7239
7358
|
agentName: command.agent,
|
|
7240
7359
|
runtimeState,
|
|
7241
|
-
|
|
7360
|
+
recordSummary,
|
|
7242
7361
|
heartbeat,
|
|
7243
7362
|
attentionCount: activeObligations.length,
|
|
7244
7363
|
now: Date.now(),
|
|
@@ -151,6 +151,20 @@ exports.COMMAND_REGISTRY = {
|
|
|
151
151
|
example: "ouro task list",
|
|
152
152
|
subcommands: ["list", "new", "done", "archive", "show"],
|
|
153
153
|
},
|
|
154
|
+
work: {
|
|
155
|
+
category: "Tasks",
|
|
156
|
+
description: "Show the agent's durable Work Card compiled from arc records.",
|
|
157
|
+
usage: "ouro work card [--agent <name>] [--format text|json|--json]",
|
|
158
|
+
example: "ouro work card --agent slugger --format json",
|
|
159
|
+
subcommands: ["card"],
|
|
160
|
+
},
|
|
161
|
+
"work card": {
|
|
162
|
+
category: "Tasks",
|
|
163
|
+
description: "Show the agent's durable Work Card compiled from arc records.",
|
|
164
|
+
usage: "ouro work card [--agent <name>] [--format text|json|--json]",
|
|
165
|
+
example: "ouro work card --agent slugger --format json",
|
|
166
|
+
hidden: true,
|
|
167
|
+
},
|
|
154
168
|
"migrate-to-desk": {
|
|
155
169
|
category: "Tasks",
|
|
156
170
|
description: "Migrate a legacy `tasks/` tree into the new `desk/` shape (copy semantics — source untouched).",
|
|
@@ -187,9 +201,9 @@ exports.COMMAND_REGISTRY = {
|
|
|
187
201
|
connect: {
|
|
188
202
|
category: "Auth",
|
|
189
203
|
description: "Set up providers, portable integrations, and local senses from one guided screen",
|
|
190
|
-
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a] [--agent <name>]",
|
|
204
|
+
usage: "ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>]",
|
|
191
205
|
example: "ouro connect",
|
|
192
|
-
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles", "mail", "voice", "a2a"],
|
|
206
|
+
subcommands: ["providers", "perplexity", "embeddings", "teams", "bluebubbles", "mail", "voice", "a2a", "workbench"],
|
|
193
207
|
},
|
|
194
208
|
a2a: {
|
|
195
209
|
category: "Friends",
|
|
@@ -342,6 +356,11 @@ const SUBCOMMAND_HELP = {
|
|
|
342
356
|
usage: "ouro connect a2a [--agent <name>]",
|
|
343
357
|
example: "ouro connect a2a --agent <agent>",
|
|
344
358
|
},
|
|
359
|
+
"connect workbench": {
|
|
360
|
+
description: "Attach native Ouro Workbench on this machine and register its MCP sense",
|
|
361
|
+
usage: "ouro connect workbench [--agent <name>]",
|
|
362
|
+
example: "ouro connect workbench --agent <agent>",
|
|
363
|
+
},
|
|
345
364
|
"a2a card": {
|
|
346
365
|
description: "Print this agent's A2A Agent Card",
|
|
347
366
|
usage: "ouro a2a card [--agent <name>] [--base-url <url>] [--json]",
|
|
@@ -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|a2a] [--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|workbench] [--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>]",
|
|
@@ -114,6 +114,7 @@ function usage() {
|
|
|
114
114
|
" ouro friend create --name <name> [--trust <level>] [--agent <name>]",
|
|
115
115
|
" ouro friend update <id> --trust <level> [--agent <name>]",
|
|
116
116
|
" ouro thoughts [--last <n>] [--json] [--follow] [--agent <name>]",
|
|
117
|
+
" ouro work card [--agent <name>] [--format text|json|--json]",
|
|
117
118
|
" ouro inner [--agent <name>]",
|
|
118
119
|
" ouro friend link <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
119
120
|
" ouro friend unlink <agent> --friend <id> --provider <p> --external-id <eid>",
|
|
@@ -744,7 +745,9 @@ function normalizeConnectTarget(value) {
|
|
|
744
745
|
return "voice";
|
|
745
746
|
if (value === "a2a" || value === "agent2agent" || value === "agent-to-agent")
|
|
746
747
|
return "a2a";
|
|
747
|
-
|
|
748
|
+
if (value === "workbench" || value === "ouro-workbench" || value === "terminal-workbench")
|
|
749
|
+
return "workbench";
|
|
750
|
+
throw new Error("Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>]");
|
|
748
751
|
}
|
|
749
752
|
function extractMailSourceFlags(args, usageText) {
|
|
750
753
|
const rest = [];
|
|
@@ -797,7 +800,7 @@ function extractMailSourceFlags(args, usageText) {
|
|
|
797
800
|
};
|
|
798
801
|
}
|
|
799
802
|
function parseConnectCommand(args) {
|
|
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]";
|
|
803
|
+
const usageText = "Usage: ouro connect [providers|perplexity|embeddings|teams|bluebubbles|mail|voice|a2a|workbench] [--agent <name>] [--owner-email <email> --source <label>|--no-delegated-source] [--rotate-missing-mail-keys]";
|
|
801
804
|
const { agent, rest: afterAgent } = extractAgentFlag(args);
|
|
802
805
|
const mailFlags = extractMailSourceFlags(afterAgent, usageText);
|
|
803
806
|
if (mailFlags.rest.length > 1)
|
|
@@ -998,6 +1001,29 @@ function parseAttentionCommand(args) {
|
|
|
998
1001
|
}
|
|
999
1002
|
return { kind: "attention.list", ...(agent ? { agent } : {}) };
|
|
1000
1003
|
}
|
|
1004
|
+
function parseWorkCommand(args) {
|
|
1005
|
+
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1006
|
+
const sub = cleaned[0];
|
|
1007
|
+
if (sub !== "card")
|
|
1008
|
+
throw new Error("Usage: ouro work card [--agent <name>] [--format text|json|--json]");
|
|
1009
|
+
let format = "text";
|
|
1010
|
+
for (let i = 1; i < cleaned.length; i += 1) {
|
|
1011
|
+
if (cleaned[i] === "--json") {
|
|
1012
|
+
format = "json";
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (cleaned[i] === "--format" && cleaned[i + 1]) {
|
|
1016
|
+
const value = cleaned[++i];
|
|
1017
|
+
if (value !== "text" && value !== "json") {
|
|
1018
|
+
throw new Error("--format must be text or json");
|
|
1019
|
+
}
|
|
1020
|
+
format = value;
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
throw new Error("Usage: ouro work card [--agent <name>] [--format text|json|--json]");
|
|
1024
|
+
}
|
|
1025
|
+
return { kind: "work.card", ...(agent ? { agent } : {}), ...(format !== "text" ? { format } : {}) };
|
|
1026
|
+
}
|
|
1001
1027
|
function parseThoughtsCommand(args) {
|
|
1002
1028
|
const { agent, rest: cleaned } = extractAgentFlag(args);
|
|
1003
1029
|
let last;
|
|
@@ -1581,6 +1607,8 @@ function parseOuroCommand(args) {
|
|
|
1581
1607
|
return parseThoughtsCommand(args.slice(1));
|
|
1582
1608
|
if (head === "attention")
|
|
1583
1609
|
return parseAttentionCommand(args.slice(1));
|
|
1610
|
+
if (head === "work")
|
|
1611
|
+
return parseWorkCommand(args.slice(1));
|
|
1584
1612
|
if (head === "inner") {
|
|
1585
1613
|
const { agent } = extractAgentFlag(args.slice(1));
|
|
1586
1614
|
return { kind: "inner.status", ...(agent ? { agent } : {}) };
|
|
@@ -366,7 +366,7 @@ void daemon.start().then(() => {
|
|
|
366
366
|
habitsDir,
|
|
367
367
|
osCronManager,
|
|
368
368
|
onHabitFire: (habitName) => {
|
|
369
|
-
processManager.sendToAgent(agent, { type: "habit", habitName });
|
|
369
|
+
processManager.sendToAgent(agent, { type: "habit", habitName, trigger: "overdue" });
|
|
370
370
|
},
|
|
371
371
|
deps: {
|
|
372
372
|
readdir: (dir) => fs.readdirSync(dir),
|
|
@@ -1205,8 +1205,8 @@ class OuroDaemon {
|
|
|
1205
1205
|
return (0, agent_service_1.handleAgentDelegate)(command);
|
|
1206
1206
|
case "agent.getContext":
|
|
1207
1207
|
return (0, agent_service_1.handleAgentGetContext)(command);
|
|
1208
|
-
case "agent.
|
|
1209
|
-
return (0, agent_service_1.
|
|
1208
|
+
case "agent.searchFacts":
|
|
1209
|
+
return (0, agent_service_1.handleAgentSearchFacts)(command);
|
|
1210
1210
|
case "agent.getTask":
|
|
1211
1211
|
return (0, agent_service_1.handleAgentGetTask)(command);
|
|
1212
1212
|
case "agent.checkScope":
|
|
@@ -1298,7 +1298,7 @@ class OuroDaemon {
|
|
|
1298
1298
|
};
|
|
1299
1299
|
}
|
|
1300
1300
|
case "habit.poke": {
|
|
1301
|
-
this.processManager.sendToAgent?.(command.agent, { type: "habit", habitName: command.habitName });
|
|
1301
|
+
this.processManager.sendToAgent?.(command.agent, { type: "habit", habitName: command.habitName, trigger: "poke" });
|
|
1302
1302
|
return {
|
|
1303
1303
|
ok: true,
|
|
1304
1304
|
message: `poked habit ${command.habitName} for ${command.agent}`,
|
|
@@ -37,10 +37,11 @@ exports.bundleMetaHook = bundleMetaHook;
|
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const runtime_1 = require("../../../nerves/runtime");
|
|
40
|
+
const record_paths_1 = require("../../../mind/record-paths");
|
|
40
41
|
/**
|
|
41
42
|
* Migrate bundle from schema 1 to schema 2:
|
|
42
43
|
* - Move state/{episodes,obligations,cares,intentions}/* to arc/{name}/*
|
|
43
|
-
* - Move
|
|
44
|
+
* - Move legacy record stores into desk/_record/
|
|
44
45
|
* Idempotent: skips missing sources; on collision, newer mtime wins.
|
|
45
46
|
*/
|
|
46
47
|
function migrateToSchema2(agentRoot) {
|
|
@@ -56,7 +57,7 @@ function migrateToSchema2(agentRoot) {
|
|
|
56
57
|
const dest = path.join(agentRoot, "arc", name);
|
|
57
58
|
migrateDirectory(src, dest);
|
|
58
59
|
}
|
|
59
|
-
//
|
|
60
|
+
// Stage legacy diary files for schema 3 to canonicalize into Desk record.
|
|
60
61
|
const legacyDiarySrc = path.join(agentRoot, "psyche", "mem" + "ory");
|
|
61
62
|
const diaryDest = path.join(agentRoot, "diary");
|
|
62
63
|
migrateDirectory(legacyDiarySrc, diaryDest);
|
|
@@ -70,7 +71,7 @@ function migrateToSchema2(agentRoot) {
|
|
|
70
71
|
});
|
|
71
72
|
}
|
|
72
73
|
/**
|
|
73
|
-
* Ensure bundle .gitignore has state/ ignored and does NOT ignore
|
|
74
|
+
* Ensure bundle .gitignore has state/ ignored and does NOT ignore tracked record roots.
|
|
74
75
|
*/
|
|
75
76
|
function updateBundleGitignore(agentRoot) {
|
|
76
77
|
const gitignorePath = path.join(agentRoot, ".gitignore");
|
|
@@ -83,8 +84,8 @@ function updateBundleGitignore(agentRoot) {
|
|
|
83
84
|
catch {
|
|
84
85
|
// If we can't read, start fresh
|
|
85
86
|
}
|
|
86
|
-
// Remove
|
|
87
|
-
const toRemove = new Set(["arc/", "diary/", "journal/"]);
|
|
87
|
+
// Remove tracked bundle roots from ignore.
|
|
88
|
+
const toRemove = new Set(["arc/", "desk/", "diary/", "journal/"]);
|
|
88
89
|
lines = lines.filter((line) => !toRemove.has(line.trim()));
|
|
89
90
|
// Ensure state/ is in the ignore list
|
|
90
91
|
const hasState = lines.some((line) => line.trim() === "state/");
|
|
@@ -100,6 +101,10 @@ function updateBundleGitignore(agentRoot) {
|
|
|
100
101
|
// Non-blocking: if we can't write .gitignore, migration still succeeds
|
|
101
102
|
}
|
|
102
103
|
}
|
|
104
|
+
function migrateToSchema3(agentRoot) {
|
|
105
|
+
(0, record_paths_1.migrateLegacyRecordStores)(agentRoot);
|
|
106
|
+
updateBundleGitignore(agentRoot);
|
|
107
|
+
}
|
|
103
108
|
/**
|
|
104
109
|
* Recursively copy files from src to dest.
|
|
105
110
|
* Creates destination directories as needed. Skips if source doesn't exist.
|
|
@@ -165,14 +170,29 @@ async function bundleMetaHook(ctx) {
|
|
|
165
170
|
// Malformed JSON -- treat as missing, will overwrite with fresh
|
|
166
171
|
existing = undefined;
|
|
167
172
|
}
|
|
168
|
-
// Run schema
|
|
173
|
+
// Run schema migrations if needed.
|
|
169
174
|
const currentSchema = existing?.bundleSchemaVersion ?? 1;
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
try {
|
|
176
|
+
if (currentSchema < 2) {
|
|
177
|
+
migrateToSchema2(ctx.agentRoot);
|
|
178
|
+
}
|
|
179
|
+
if (currentSchema < 3) {
|
|
180
|
+
migrateToSchema3(ctx.agentRoot);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const errorMessage = err instanceof Error ? err.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(err);
|
|
185
|
+
(0, runtime_1.emitNervesEvent)({
|
|
186
|
+
component: "daemon",
|
|
187
|
+
event: "daemon.bundle_meta_hook_error",
|
|
188
|
+
message: "bundle-meta hook migration failed",
|
|
189
|
+
meta: { agentRoot: ctx.agentRoot, error: errorMessage },
|
|
190
|
+
});
|
|
191
|
+
return { ok: false, error: errorMessage };
|
|
172
192
|
}
|
|
173
193
|
const updated = {
|
|
174
194
|
runtimeVersion: ctx.currentVersion,
|
|
175
|
-
bundleSchemaVersion: currentSchema <
|
|
195
|
+
bundleSchemaVersion: currentSchema < 3 ? 3 : currentSchema,
|
|
176
196
|
lastUpdated: new Date().toISOString(),
|
|
177
197
|
};
|
|
178
198
|
// Save old runtimeVersion as previousRuntimeVersion (if there was one)
|