@ouro.bot/cli 0.1.0-alpha.11 → 0.1.0-alpha.13
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/assets/ouroboros.png +0 -0
- package/dist/heart/config.js +2 -2
- package/dist/heart/core.js +3 -2
- package/dist/heart/daemon/daemon-cli.js +202 -23
- package/dist/heart/daemon/daemon-entry.js +13 -5
- package/dist/heart/daemon/daemon.js +40 -9
- package/dist/heart/daemon/hatch-flow.js +0 -10
- package/dist/heart/daemon/ouro-bot-entry.js +0 -0
- package/dist/heart/daemon/ouro-entry.js +0 -0
- package/dist/heart/daemon/ouro-path-installer.js +21 -5
- package/dist/heart/daemon/ouro-uti.js +11 -2
- package/dist/heart/daemon/process-manager.js +1 -1
- package/dist/heart/daemon/sense-manager.js +266 -0
- package/dist/heart/daemon/specialist-orchestrator.js +37 -94
- package/dist/heart/daemon/specialist-prompt.js +43 -8
- package/dist/heart/daemon/specialist-tools.js +161 -59
- package/dist/heart/daemon/subagent-installer.js +10 -1
- package/dist/heart/identity.js +63 -1
- package/dist/heart/sense-truth.js +61 -0
- package/dist/mind/bundle-manifest.js +58 -0
- package/dist/mind/prompt.js +71 -0
- package/dist/senses/cli.js +162 -98
- package/package.json +7 -2
- package/dist/heart/daemon/specialist-session.js +0 -177
- package/dist/inner-worker-entry.js +0 -4
|
Binary file
|
package/dist/heart/config.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.loadConfig = loadConfig;
|
|
37
37
|
exports.resetConfigCache = resetConfigCache;
|
|
38
|
-
exports.
|
|
38
|
+
exports.patchRuntimeConfig = patchRuntimeConfig;
|
|
39
39
|
exports.getAzureConfig = getAzureConfig;
|
|
40
40
|
exports.getMinimaxConfig = getMinimaxConfig;
|
|
41
41
|
exports.getAnthropicConfig = getAnthropicConfig;
|
|
@@ -233,7 +233,7 @@ function resetConfigCache() {
|
|
|
233
233
|
_cachedConfig = null;
|
|
234
234
|
_testContextOverride = null;
|
|
235
235
|
}
|
|
236
|
-
function
|
|
236
|
+
function patchRuntimeConfig(partial) {
|
|
237
237
|
loadConfig(); // ensure _cachedConfig exists
|
|
238
238
|
const contextPatch = partial.context;
|
|
239
239
|
if (contextPatch) {
|
package/dist/heart/core.js
CHANGED
|
@@ -278,7 +278,7 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
278
278
|
}
|
|
279
279
|
catch { /* unsupported */ }
|
|
280
280
|
const toolPreferences = currentContext?.friend?.toolPreferences;
|
|
281
|
-
const baseTools = (0, tools_1.getToolsForChannel)(channel ? (0, channel_1.getChannelCapabilities)(channel) : undefined, toolPreferences && Object.keys(toolPreferences).length > 0 ? toolPreferences : undefined);
|
|
281
|
+
const baseTools = options?.tools ?? (0, tools_1.getToolsForChannel)(channel ? (0, channel_1.getChannelCapabilities)(channel) : undefined, toolPreferences && Object.keys(toolPreferences).length > 0 ? toolPreferences : undefined);
|
|
282
282
|
// Rebase provider-owned turn state from canonical messages at user-turn start.
|
|
283
283
|
// This prevents stale provider caches from replaying prior-turn context.
|
|
284
284
|
providerRuntime.resetTurnState(messages);
|
|
@@ -444,7 +444,8 @@ async function runAgent(messages, callbacks, channel, signal, options) {
|
|
|
444
444
|
let toolResult;
|
|
445
445
|
let success;
|
|
446
446
|
try {
|
|
447
|
-
|
|
447
|
+
const execToolFn = options?.execTool ?? tools_1.execTool;
|
|
448
|
+
toolResult = await execToolFn(tc.name, args, options?.toolContext);
|
|
448
449
|
success = true;
|
|
449
450
|
}
|
|
450
451
|
catch (e) {
|
|
@@ -52,6 +52,141 @@ const ouro_path_installer_1 = require("./ouro-path-installer");
|
|
|
52
52
|
const subagent_installer_1 = require("./subagent-installer");
|
|
53
53
|
const hatch_flow_1 = require("./hatch-flow");
|
|
54
54
|
const specialist_orchestrator_1 = require("./specialist-orchestrator");
|
|
55
|
+
const specialist_prompt_1 = require("./specialist-prompt");
|
|
56
|
+
const specialist_tools_1 = require("./specialist-tools");
|
|
57
|
+
function stringField(value) {
|
|
58
|
+
return typeof value === "string" ? value : null;
|
|
59
|
+
}
|
|
60
|
+
function numberField(value) {
|
|
61
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
62
|
+
}
|
|
63
|
+
function booleanField(value) {
|
|
64
|
+
return typeof value === "boolean" ? value : null;
|
|
65
|
+
}
|
|
66
|
+
function parseStatusPayload(data) {
|
|
67
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
68
|
+
return null;
|
|
69
|
+
const raw = data;
|
|
70
|
+
const overview = raw.overview;
|
|
71
|
+
const senses = raw.senses;
|
|
72
|
+
const workers = raw.workers;
|
|
73
|
+
if (!overview || typeof overview !== "object" || Array.isArray(overview))
|
|
74
|
+
return null;
|
|
75
|
+
if (!Array.isArray(senses) || !Array.isArray(workers))
|
|
76
|
+
return null;
|
|
77
|
+
const parsedOverview = {
|
|
78
|
+
daemon: stringField(overview.daemon) ?? "unknown",
|
|
79
|
+
health: stringField(overview.health) ?? "unknown",
|
|
80
|
+
socketPath: stringField(overview.socketPath) ?? "unknown",
|
|
81
|
+
workerCount: numberField(overview.workerCount) ?? 0,
|
|
82
|
+
senseCount: numberField(overview.senseCount) ?? 0,
|
|
83
|
+
};
|
|
84
|
+
const parsedSenses = senses.map((entry) => {
|
|
85
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
86
|
+
return null;
|
|
87
|
+
const row = entry;
|
|
88
|
+
const agent = stringField(row.agent);
|
|
89
|
+
const sense = stringField(row.sense);
|
|
90
|
+
const status = stringField(row.status);
|
|
91
|
+
const detail = stringField(row.detail);
|
|
92
|
+
const enabled = booleanField(row.enabled);
|
|
93
|
+
if (!agent || !sense || !status || detail === null || enabled === null)
|
|
94
|
+
return null;
|
|
95
|
+
return {
|
|
96
|
+
agent,
|
|
97
|
+
sense,
|
|
98
|
+
label: stringField(row.label) ?? undefined,
|
|
99
|
+
enabled,
|
|
100
|
+
status,
|
|
101
|
+
detail,
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
const parsedWorkers = workers.map((entry) => {
|
|
105
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
106
|
+
return null;
|
|
107
|
+
const row = entry;
|
|
108
|
+
const agent = stringField(row.agent);
|
|
109
|
+
const worker = stringField(row.worker);
|
|
110
|
+
const status = stringField(row.status);
|
|
111
|
+
const restartCount = numberField(row.restartCount);
|
|
112
|
+
const hasPid = Object.prototype.hasOwnProperty.call(row, "pid");
|
|
113
|
+
const pid = row.pid === null ? null : numberField(row.pid);
|
|
114
|
+
const pidInvalid = !hasPid || (row.pid !== null && pid === null);
|
|
115
|
+
if (!agent || !worker || !status || restartCount === null || pidInvalid)
|
|
116
|
+
return null;
|
|
117
|
+
return {
|
|
118
|
+
agent,
|
|
119
|
+
worker,
|
|
120
|
+
status,
|
|
121
|
+
pid,
|
|
122
|
+
restartCount,
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
if (parsedSenses.some((row) => row === null) || parsedWorkers.some((row) => row === null))
|
|
126
|
+
return null;
|
|
127
|
+
return {
|
|
128
|
+
overview: parsedOverview,
|
|
129
|
+
senses: parsedSenses,
|
|
130
|
+
workers: parsedWorkers,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function humanizeSenseName(sense, label) {
|
|
134
|
+
if (label)
|
|
135
|
+
return label;
|
|
136
|
+
if (sense === "cli")
|
|
137
|
+
return "CLI";
|
|
138
|
+
if (sense === "bluebubbles")
|
|
139
|
+
return "BlueBubbles";
|
|
140
|
+
if (sense === "teams")
|
|
141
|
+
return "Teams";
|
|
142
|
+
return sense;
|
|
143
|
+
}
|
|
144
|
+
function formatTable(headers, rows) {
|
|
145
|
+
const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => row[index].length)));
|
|
146
|
+
const renderRow = (row) => `| ${row.map((cell, index) => cell.padEnd(widths[index])).join(" | ")} |`;
|
|
147
|
+
const divider = `|-${widths.map((width) => "-".repeat(width)).join("-|-")}-|`;
|
|
148
|
+
return [
|
|
149
|
+
renderRow(headers),
|
|
150
|
+
divider,
|
|
151
|
+
...rows.map(renderRow),
|
|
152
|
+
].join("\n");
|
|
153
|
+
}
|
|
154
|
+
function formatDaemonStatusOutput(response, fallback) {
|
|
155
|
+
const payload = parseStatusPayload(response.data);
|
|
156
|
+
if (!payload)
|
|
157
|
+
return fallback;
|
|
158
|
+
const overviewRows = [
|
|
159
|
+
["Daemon", payload.overview.daemon],
|
|
160
|
+
["Socket", payload.overview.socketPath],
|
|
161
|
+
["Workers", String(payload.overview.workerCount)],
|
|
162
|
+
["Senses", String(payload.overview.senseCount)],
|
|
163
|
+
["Health", payload.overview.health],
|
|
164
|
+
];
|
|
165
|
+
const senseRows = payload.senses.map((row) => [
|
|
166
|
+
row.agent,
|
|
167
|
+
humanizeSenseName(row.sense, row.label),
|
|
168
|
+
row.enabled ? "ON" : "OFF",
|
|
169
|
+
row.status,
|
|
170
|
+
row.detail,
|
|
171
|
+
]);
|
|
172
|
+
const workerRows = payload.workers.map((row) => [
|
|
173
|
+
row.agent,
|
|
174
|
+
row.worker,
|
|
175
|
+
row.status,
|
|
176
|
+
row.pid === null ? "n/a" : String(row.pid),
|
|
177
|
+
String(row.restartCount),
|
|
178
|
+
]);
|
|
179
|
+
return [
|
|
180
|
+
"Overview",
|
|
181
|
+
formatTable(["Item", "Value"], overviewRows),
|
|
182
|
+
"",
|
|
183
|
+
"Senses",
|
|
184
|
+
formatTable(["Agent", "Sense", "Enabled", "State", "Detail"], senseRows),
|
|
185
|
+
"",
|
|
186
|
+
"Workers",
|
|
187
|
+
formatTable(["Agent", "Worker", "State", "PID", "Restarts"], workerRows),
|
|
188
|
+
].join("\n");
|
|
189
|
+
}
|
|
55
190
|
async function ensureDaemonRunning(deps) {
|
|
56
191
|
const alive = await deps.checkSocketAlive(deps.socketPath);
|
|
57
192
|
if (alive) {
|
|
@@ -514,11 +649,13 @@ function discoverExistingCredentials(secretsRoot) {
|
|
|
514
649
|
return true;
|
|
515
650
|
});
|
|
516
651
|
}
|
|
517
|
-
/* v8 ignore
|
|
652
|
+
/* v8 ignore start -- integration: interactive terminal specialist session @preserve */
|
|
518
653
|
async function defaultRunAdoptionSpecialist() {
|
|
519
|
-
const
|
|
654
|
+
const { runCliSession } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
|
|
655
|
+
const { patchRuntimeConfig } = await Promise.resolve().then(() => __importStar(require("../config")));
|
|
656
|
+
const { setAgentName } = await Promise.resolve().then(() => __importStar(require("../identity")));
|
|
520
657
|
const readlinePromises = await Promise.resolve().then(() => __importStar(require("readline/promises")));
|
|
521
|
-
const
|
|
658
|
+
const crypto = await Promise.resolve().then(() => __importStar(require("crypto")));
|
|
522
659
|
// Phase 1: cold CLI — collect provider/credentials with a simple readline
|
|
523
660
|
const coldRl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
|
|
524
661
|
const coldPrompt = async (q) => {
|
|
@@ -527,11 +664,12 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
527
664
|
};
|
|
528
665
|
let providerRaw;
|
|
529
666
|
let credentials = {};
|
|
667
|
+
const tempDir = path.join(os.tmpdir(), `ouro-hatch-${crypto.randomUUID()}`);
|
|
530
668
|
try {
|
|
531
669
|
const secretsRoot = path.join(os.homedir(), ".agentsecrets");
|
|
532
670
|
const discovered = discoverExistingCredentials(secretsRoot);
|
|
533
671
|
if (discovered.length > 0) {
|
|
534
|
-
process.stdout.write("\n
|
|
672
|
+
process.stdout.write("\n\ud83d\udc0d welcome to ouro! let's hatch your first agent.\n");
|
|
535
673
|
process.stdout.write("i found existing API credentials:\n\n");
|
|
536
674
|
const unique = [...new Map(discovered.map((d) => [`${d.provider}`, d])).values()];
|
|
537
675
|
for (let i = 0; i < unique.length; i++) {
|
|
@@ -566,7 +704,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
566
704
|
}
|
|
567
705
|
}
|
|
568
706
|
else {
|
|
569
|
-
process.stdout.write("\n
|
|
707
|
+
process.stdout.write("\n\ud83d\udc0d welcome to ouro! let's hatch your first agent.\n");
|
|
570
708
|
process.stdout.write("i need an API key to power our conversation.\n\n");
|
|
571
709
|
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
|
|
572
710
|
if (!isAgentProvider(pRaw)) {
|
|
@@ -589,34 +727,72 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
589
727
|
}
|
|
590
728
|
coldRl.close();
|
|
591
729
|
process.stdout.write("\n");
|
|
592
|
-
// Phase 2:
|
|
730
|
+
// Phase 2: configure runtime for adoption specialist
|
|
593
731
|
const bundleSourceDir = path.resolve(__dirname, "..", "..", "..", "AdoptionSpecialist.ouro");
|
|
594
732
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
733
|
+
const secretsRoot2 = path.join(os.homedir(), ".agentsecrets");
|
|
734
|
+
// Configure provider credentials in runtime config
|
|
735
|
+
patchRuntimeConfig({
|
|
736
|
+
providers: {
|
|
737
|
+
[providerRaw]: credentials,
|
|
738
|
+
},
|
|
739
|
+
});
|
|
740
|
+
setAgentName("AdoptionSpecialist");
|
|
741
|
+
// Build specialist system prompt
|
|
742
|
+
const soulText = (0, specialist_orchestrator_1.loadSoulText)(bundleSourceDir);
|
|
743
|
+
const identitiesDir = path.join(bundleSourceDir, "psyche", "identities");
|
|
744
|
+
const identity = (0, specialist_orchestrator_1.pickRandomIdentity)(identitiesDir);
|
|
745
|
+
const existingBundles = (0, specialist_orchestrator_1.listExistingBundles)(bundlesRoot);
|
|
746
|
+
const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles, {
|
|
747
|
+
tempDir,
|
|
600
748
|
provider: providerRaw,
|
|
749
|
+
});
|
|
750
|
+
// Build specialist tools
|
|
751
|
+
const specialistTools = (0, specialist_tools_1.getSpecialistTools)();
|
|
752
|
+
const specialistExecTool = (0, specialist_tools_1.createSpecialistExecTool)({
|
|
753
|
+
tempDir,
|
|
601
754
|
credentials,
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
755
|
+
provider: providerRaw,
|
|
756
|
+
bundlesRoot,
|
|
757
|
+
secretsRoot: secretsRoot2,
|
|
758
|
+
animationWriter: (text) => process.stdout.write(text),
|
|
759
|
+
});
|
|
760
|
+
// Run the adoption specialist session via runCliSession
|
|
761
|
+
const result = await runCliSession({
|
|
762
|
+
agentName: "AdoptionSpecialist",
|
|
763
|
+
tools: specialistTools,
|
|
764
|
+
execTool: specialistExecTool,
|
|
765
|
+
exitOnToolCall: "complete_adoption",
|
|
766
|
+
messages: [
|
|
767
|
+
{ role: "system", content: systemPrompt },
|
|
768
|
+
{ role: "user", content: "hi" },
|
|
769
|
+
],
|
|
613
770
|
});
|
|
771
|
+
if (result.exitReason === "tool_exit" && result.toolResult) {
|
|
772
|
+
const parsed = typeof result.toolResult === "string" ? JSON.parse(result.toolResult) : result.toolResult;
|
|
773
|
+
if (parsed.success && parsed.agentName) {
|
|
774
|
+
return parsed.agentName;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
return null;
|
|
614
778
|
}
|
|
615
779
|
catch {
|
|
616
780
|
coldRl.close();
|
|
617
781
|
return null;
|
|
618
782
|
}
|
|
783
|
+
finally {
|
|
784
|
+
// Clean up temp dir if it still exists
|
|
785
|
+
try {
|
|
786
|
+
if (fs.existsSync(tempDir)) {
|
|
787
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
catch {
|
|
791
|
+
// Best effort cleanup
|
|
792
|
+
}
|
|
793
|
+
}
|
|
619
794
|
}
|
|
795
|
+
/* v8 ignore stop */
|
|
620
796
|
function createDefaultOuroCliDeps(socketPath = "/tmp/ouroboros-daemon.sock") {
|
|
621
797
|
return {
|
|
622
798
|
socketPath,
|
|
@@ -867,7 +1043,10 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
867
1043
|
}
|
|
868
1044
|
throw error;
|
|
869
1045
|
}
|
|
870
|
-
const
|
|
1046
|
+
const fallbackMessage = response.summary ?? response.message ?? (response.ok ? "ok" : `error: ${response.error ?? "unknown error"}`);
|
|
1047
|
+
const message = command.kind === "daemon.status"
|
|
1048
|
+
? formatDaemonStatusOutput(response, fallbackMessage)
|
|
1049
|
+
: fallbackMessage;
|
|
871
1050
|
deps.writeStdout(message);
|
|
872
1051
|
return message;
|
|
873
1052
|
}
|
|
@@ -8,6 +8,7 @@ const message_router_1 = require("./message-router");
|
|
|
8
8
|
const health_monitor_1 = require("./health-monitor");
|
|
9
9
|
const task_scheduler_1 = require("./task-scheduler");
|
|
10
10
|
const runtime_logging_1 = require("./runtime-logging");
|
|
11
|
+
const sense_manager_1 = require("./sense-manager");
|
|
11
12
|
function parseSocketPath(argv) {
|
|
12
13
|
const socketIndex = argv.indexOf("--socket");
|
|
13
14
|
if (socketIndex >= 0) {
|
|
@@ -25,16 +26,22 @@ const socketPath = parseSocketPath(process.argv);
|
|
|
25
26
|
message: "starting daemon entrypoint",
|
|
26
27
|
meta: { socketPath },
|
|
27
28
|
});
|
|
29
|
+
const managedAgents = ["ouroboros", "slugger"];
|
|
28
30
|
const processManager = new process_manager_1.DaemonProcessManager({
|
|
29
|
-
agents:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
agents: managedAgents.map((agent) => ({
|
|
32
|
+
name: agent,
|
|
33
|
+
entry: "heart/agent-entry.js",
|
|
34
|
+
channel: "inner-dialog",
|
|
35
|
+
autoStart: true,
|
|
36
|
+
})),
|
|
33
37
|
});
|
|
34
38
|
const scheduler = new task_scheduler_1.TaskDrivenScheduler({
|
|
35
|
-
agents: [
|
|
39
|
+
agents: [...managedAgents],
|
|
36
40
|
});
|
|
37
41
|
const router = new message_router_1.FileMessageRouter();
|
|
42
|
+
const senseManager = new sense_manager_1.DaemonSenseManager({
|
|
43
|
+
agents: [...managedAgents],
|
|
44
|
+
});
|
|
38
45
|
const healthMonitor = new health_monitor_1.HealthMonitor({
|
|
39
46
|
processManager,
|
|
40
47
|
scheduler,
|
|
@@ -51,6 +58,7 @@ const healthMonitor = new health_monitor_1.HealthMonitor({
|
|
|
51
58
|
const daemon = new daemon_1.OuroDaemon({
|
|
52
59
|
socketPath,
|
|
53
60
|
processManager,
|
|
61
|
+
senseManager,
|
|
54
62
|
scheduler,
|
|
55
63
|
healthMonitor,
|
|
56
64
|
router,
|
|
@@ -39,14 +39,28 @@ const net = __importStar(require("net"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const identity_1 = require("../identity");
|
|
41
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
42
|
-
function
|
|
43
|
-
|
|
42
|
+
function buildWorkerRows(snapshots) {
|
|
43
|
+
return snapshots.map((snapshot) => ({
|
|
44
|
+
agent: snapshot.name,
|
|
45
|
+
worker: snapshot.channel,
|
|
46
|
+
status: snapshot.status,
|
|
47
|
+
pid: snapshot.pid,
|
|
48
|
+
restartCount: snapshot.restartCount,
|
|
49
|
+
startedAt: snapshot.startedAt,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
function formatStatusSummary(payload) {
|
|
53
|
+
if (payload.overview.workerCount === 0 && payload.overview.senseCount === 0) {
|
|
44
54
|
return "no managed agents";
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
}
|
|
56
|
+
const rows = [
|
|
57
|
+
...payload.workers.map((row) => `${row.agent}/${row.worker}:${row.status}`),
|
|
58
|
+
...payload.senses
|
|
59
|
+
.filter((row) => row.enabled)
|
|
60
|
+
.map((row) => `${row.agent}/${row.sense}:${row.status}`),
|
|
61
|
+
];
|
|
62
|
+
const detail = rows.length > 0 ? `\titems=${rows.join(",")}` : "";
|
|
63
|
+
return `daemon=${payload.overview.daemon}\tworkers=${payload.overview.workerCount}\tsenses=${payload.overview.senseCount}\thealth=${payload.overview.health}${detail}`;
|
|
50
64
|
}
|
|
51
65
|
function parseIncomingCommand(raw) {
|
|
52
66
|
let parsed;
|
|
@@ -71,6 +85,7 @@ class OuroDaemon {
|
|
|
71
85
|
scheduler;
|
|
72
86
|
healthMonitor;
|
|
73
87
|
router;
|
|
88
|
+
senseManager;
|
|
74
89
|
bundlesRoot;
|
|
75
90
|
server = null;
|
|
76
91
|
constructor(options) {
|
|
@@ -79,6 +94,7 @@ class OuroDaemon {
|
|
|
79
94
|
this.scheduler = options.scheduler;
|
|
80
95
|
this.healthMonitor = options.healthMonitor;
|
|
81
96
|
this.router = options.router;
|
|
97
|
+
this.senseManager = options.senseManager ?? null;
|
|
82
98
|
this.bundlesRoot = options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
83
99
|
}
|
|
84
100
|
async start() {
|
|
@@ -91,6 +107,7 @@ class OuroDaemon {
|
|
|
91
107
|
meta: { socketPath: this.socketPath },
|
|
92
108
|
});
|
|
93
109
|
await this.processManager.startAutoStartAgents();
|
|
110
|
+
await this.senseManager?.startAutoStartSenses();
|
|
94
111
|
this.scheduler.start?.();
|
|
95
112
|
await this.scheduler.reconcile?.();
|
|
96
113
|
await this.drainPendingBundleMessages();
|
|
@@ -178,6 +195,7 @@ class OuroDaemon {
|
|
|
178
195
|
});
|
|
179
196
|
this.scheduler.stop?.();
|
|
180
197
|
await this.processManager.stopAll();
|
|
198
|
+
await this.senseManager?.stopAll();
|
|
181
199
|
if (this.server) {
|
|
182
200
|
await new Promise((resolve) => {
|
|
183
201
|
this.server?.close(() => resolve());
|
|
@@ -217,10 +235,23 @@ class OuroDaemon {
|
|
|
217
235
|
return { ok: true, message: "daemon stopped" };
|
|
218
236
|
case "daemon.status": {
|
|
219
237
|
const snapshots = this.processManager.listAgentSnapshots();
|
|
238
|
+
const workers = buildWorkerRows(snapshots);
|
|
239
|
+
const senses = this.senseManager?.listSenseRows() ?? [];
|
|
240
|
+
const data = {
|
|
241
|
+
overview: {
|
|
242
|
+
daemon: "running",
|
|
243
|
+
health: workers.every((worker) => worker.status === "running") ? "ok" : "warn",
|
|
244
|
+
socketPath: this.socketPath,
|
|
245
|
+
workerCount: workers.length,
|
|
246
|
+
senseCount: senses.length,
|
|
247
|
+
},
|
|
248
|
+
workers,
|
|
249
|
+
senses,
|
|
250
|
+
};
|
|
220
251
|
return {
|
|
221
252
|
ok: true,
|
|
222
|
-
summary: formatStatusSummary(
|
|
223
|
-
data
|
|
253
|
+
summary: formatStatusSummary(data),
|
|
254
|
+
data,
|
|
224
255
|
};
|
|
225
256
|
}
|
|
226
257
|
case "daemon.health": {
|
|
@@ -207,15 +207,6 @@ function writeFriendImprint(bundleRoot, humanName, now) {
|
|
|
207
207
|
};
|
|
208
208
|
fs.writeFileSync(path.join(friendsDir, `${id}.json`), `${JSON.stringify(record, null, 2)}\n`, "utf-8");
|
|
209
209
|
}
|
|
210
|
-
function writeHatchlingPsyche(bundleRoot, input, identityFileName) {
|
|
211
|
-
const psycheDir = path.join(bundleRoot, "psyche");
|
|
212
|
-
fs.mkdirSync(psycheDir, { recursive: true });
|
|
213
|
-
fs.writeFileSync(path.join(psycheDir, "SOUL.md"), "# SOUL\n\nI am a practical, collaborative agent. I keep commitments and communicate clearly.\n", "utf-8");
|
|
214
|
-
fs.writeFileSync(path.join(psycheDir, "IDENTITY.md"), `# IDENTITY\n\nI'm ${input.agentName}, newly hatched and ready to help ${input.humanName}.`, "utf-8");
|
|
215
|
-
fs.writeFileSync(path.join(psycheDir, "LORE.md"), `# LORE\n\nHatched with specialist identity seed: ${identityFileName}.`, "utf-8");
|
|
216
|
-
fs.writeFileSync(path.join(psycheDir, "TACIT.md"), "# TACIT\n\n- Save what I learn.\n- Keep tasks current.\n", "utf-8");
|
|
217
|
-
fs.writeFileSync(path.join(psycheDir, "ASPIRATIONS.md"), "# ASPIRATIONS\n\n- Become a reliable partner for my primary friend.\n", "utf-8");
|
|
218
|
-
}
|
|
219
210
|
function writeMemoryScaffold(bundleRoot) {
|
|
220
211
|
const memoryRoot = path.join(bundleRoot, "psyche", "memory");
|
|
221
212
|
fs.mkdirSync(path.join(memoryRoot, "daily"), { recursive: true });
|
|
@@ -267,7 +258,6 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
267
258
|
writeReadme(path.join(bundleRoot, "senses"), "Sense-specific config.");
|
|
268
259
|
writeReadme(path.join(bundleRoot, "senses", "teams"), "Teams sense config.");
|
|
269
260
|
writeHatchlingAgentConfig(bundleRoot, input);
|
|
270
|
-
writeHatchlingPsyche(bundleRoot, input, selected.fileName);
|
|
271
261
|
writeMemoryScaffold(bundleRoot);
|
|
272
262
|
writeFriendImprint(bundleRoot, input.humanName, now);
|
|
273
263
|
writeHeartbeatTask(bundleRoot, now);
|
|
File without changes
|
|
File without changes
|
|
@@ -39,7 +39,7 @@ const os = __importStar(require("os"));
|
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
40
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
41
|
const WRAPPER_SCRIPT = `#!/bin/sh
|
|
42
|
-
exec npx --yes
|
|
42
|
+
exec npx --yes ouro.bot "$@"
|
|
43
43
|
`;
|
|
44
44
|
function detectShellProfile(homeDir, shell) {
|
|
45
45
|
if (!shell)
|
|
@@ -96,15 +96,31 @@ function installOuroCommand(deps = {}) {
|
|
|
96
96
|
message: "installing ouro command to PATH",
|
|
97
97
|
meta: { scriptPath, binDir },
|
|
98
98
|
});
|
|
99
|
-
// If ouro already exists
|
|
99
|
+
// If ouro already exists, check content and repair if stale
|
|
100
100
|
if (existsSync(scriptPath)) {
|
|
101
|
+
let existingContent = "";
|
|
102
|
+
try {
|
|
103
|
+
existingContent = readFileSync(scriptPath, "utf-8");
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Can't read — treat as stale, will overwrite below
|
|
107
|
+
}
|
|
108
|
+
if (existingContent === WRAPPER_SCRIPT) {
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "daemon",
|
|
111
|
+
event: "daemon.ouro_path_install_skip",
|
|
112
|
+
message: "ouro command already installed",
|
|
113
|
+
meta: { scriptPath },
|
|
114
|
+
});
|
|
115
|
+
return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed" };
|
|
116
|
+
}
|
|
117
|
+
// Content is stale — repair by overwriting
|
|
101
118
|
(0, runtime_1.emitNervesEvent)({
|
|
102
119
|
component: "daemon",
|
|
103
|
-
event: "daemon.
|
|
104
|
-
message: "ouro
|
|
120
|
+
event: "daemon.ouro_path_install_repair",
|
|
121
|
+
message: "repairing stale ouro wrapper script",
|
|
105
122
|
meta: { scriptPath },
|
|
106
123
|
});
|
|
107
|
-
return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed" };
|
|
108
124
|
}
|
|
109
125
|
try {
|
|
110
126
|
mkdirSync(binDir, { recursive: true });
|
|
@@ -42,7 +42,13 @@ const identity_1 = require("../identity");
|
|
|
42
42
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
43
|
const LSREGISTER_PATH = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister";
|
|
44
44
|
const ICON_SIZES = [16, 32, 128, 256, 512];
|
|
45
|
-
function resolveIconSourcePath(repoRoot) {
|
|
45
|
+
function resolveIconSourcePath(repoRoot, existsSync) {
|
|
46
|
+
// Prefer bundled asset (shipped with npm package)
|
|
47
|
+
const bundledPath = path.resolve(repoRoot, "assets", "ouroboros.png");
|
|
48
|
+
if (existsSync(bundledPath)) {
|
|
49
|
+
return bundledPath;
|
|
50
|
+
}
|
|
51
|
+
// Fall back to adjacent repo path (dev environment)
|
|
46
52
|
return path.resolve(repoRoot, "..", "ouroboros-website", "public", "images", "ouroboros.png");
|
|
47
53
|
}
|
|
48
54
|
function buildIconAsset(iconSourcePath, icnsPath, iconsetDir, deps) {
|
|
@@ -91,6 +97,7 @@ function buildInfoPlist(iconInstalled) {
|
|
|
91
97
|
" <key>UTTypeConformsTo</key>",
|
|
92
98
|
" <array>",
|
|
93
99
|
" <string>public.folder</string>",
|
|
100
|
+
" <string>com.apple.package</string>",
|
|
94
101
|
" </array>",
|
|
95
102
|
" <key>UTTypeTagSpecification</key>",
|
|
96
103
|
" <dict>",
|
|
@@ -112,6 +119,8 @@ function buildInfoPlist(iconInstalled) {
|
|
|
112
119
|
" </array>",
|
|
113
120
|
" <key>CFBundleTypeRole</key>",
|
|
114
121
|
" <string>Editor</string>",
|
|
122
|
+
" <key>LSTypeIsPackage</key>",
|
|
123
|
+
" <true/>",
|
|
115
124
|
` ${iconTag.trim()}`,
|
|
116
125
|
" </dict>",
|
|
117
126
|
" </array>",
|
|
@@ -152,7 +161,7 @@ function registerOuroBundleUti(deps = {}) {
|
|
|
152
161
|
const plistPath = path.join(contentsDir, "Info.plist");
|
|
153
162
|
const icnsPath = path.join(resourcesDir, "ouro.icns");
|
|
154
163
|
const iconsetDir = path.join(supportRoot, "ouro.iconset");
|
|
155
|
-
const iconSourcePath = resolveIconSourcePath(repoRoot);
|
|
164
|
+
const iconSourcePath = resolveIconSourcePath(repoRoot, existsSync);
|
|
156
165
|
(0, runtime_1.emitNervesEvent)({
|
|
157
166
|
component: "daemon",
|
|
158
167
|
event: "daemon.ouro_uti_register_start",
|
|
@@ -96,7 +96,7 @@ class DaemonProcessManager {
|
|
|
96
96
|
state.snapshot.status = "starting";
|
|
97
97
|
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
98
|
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
99
|
-
const args = [entryScript, "--agent", agent, ...(state.config.args ?? [])];
|
|
99
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
|
|
100
100
|
const child = this.spawnFn("node", args, {
|
|
101
101
|
cwd: runCwd,
|
|
102
102
|
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|