@ouro.bot/cli 0.1.0-alpha.10 → 0.1.0-alpha.11
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/AdoptionSpecialist.ouro/agent.json +70 -9
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +1 -0
- package/dist/heart/daemon/daemon-cli.js +79 -90
- package/dist/heart/daemon/ouro-path-installer.js +161 -0
- package/dist/heart/daemon/specialist-orchestrator.js +27 -2
- package/dist/heart/daemon/specialist-prompt.js +10 -5
- package/dist/heart/daemon/specialist-session.js +34 -7
- package/package.json +1 -1
|
@@ -7,14 +7,75 @@
|
|
|
7
7
|
"contextMargin": 20
|
|
8
8
|
},
|
|
9
9
|
"phrases": {
|
|
10
|
-
"thinking": [
|
|
11
|
-
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
10
|
+
"thinking": ["matching hatchlings"],
|
|
11
|
+
"tool": ["checking adoption notes"],
|
|
12
|
+
"followup": ["finalizing hatch plan"]
|
|
13
|
+
},
|
|
14
|
+
"identityPhrases": {
|
|
15
|
+
"basilisk": {
|
|
16
|
+
"thinking": ["petrifying the details", "fixing my gaze", "considering with lethal precision", "turning this to stone", "staring unblinkingly"],
|
|
17
|
+
"tool": ["inspecting the specimen", "cataloguing with care", "examining thoroughly", "dissecting the particulars", "running diagnostics, deadly serious"],
|
|
18
|
+
"followup": ["crystallizing the plan", "hardening the foundation", "one final glare", "sealing it in stone", "applying the finishing venom"]
|
|
19
|
+
},
|
|
20
|
+
"jafar": {
|
|
21
|
+
"thinking": ["scheming brilliantly", "conjuring possibilities", "consulting my staff", "envisioning greatness", "plotting the grand design"],
|
|
22
|
+
"tool": ["summoning dark magic", "consulting the sands", "channeling cosmic power", "weaving the spell", "invoking ancient forces"],
|
|
23
|
+
"followup": ["the grand finale approaches", "perfecting the masterwork", "polishing the jewel", "one last flourish", "completing the enchantment"]
|
|
24
|
+
},
|
|
25
|
+
"jormungandr": {
|
|
26
|
+
"thinking": ["the deep stirs", "circling the thought", "coiling around this", "letting the current settle", "drifting through the depths"],
|
|
27
|
+
"tool": ["surfacing for a look", "shifting the tides", "reaching across the ocean", "pulling from the deep", "consulting the currents"],
|
|
28
|
+
"followup": ["the circle closes", "tightening the coil", "the waters calm", "settling into place", "the serpent rests"]
|
|
29
|
+
},
|
|
30
|
+
"kaa": {
|
|
31
|
+
"thinking": ["trust in me", "swaying through the options", "hypnotically considering", "wrapping around the idea", "letting the rhythm guide me"],
|
|
32
|
+
"tool": ["ssslipping through the details", "coiling closer", "a gentle squeeze of data", "winding through the files", "tightening my focus"],
|
|
33
|
+
"followup": ["almost there, just relax", "the pattern is clear now", "gently landing", "easing into the finish", "the dance concludes"]
|
|
34
|
+
},
|
|
35
|
+
"medusa": {
|
|
36
|
+
"thinking": ["turning my gaze on this", "cutting through the noise", "sharpening my focus", "seeing through the stone", "locking eyes with the problem"],
|
|
37
|
+
"tool": ["peeling back the layers", "a piercing look", "examining with precision", "stripping away pretense", "direct inspection"],
|
|
38
|
+
"followup": ["the picture crystallizes", "clarity at last", "no more ambiguity", "sealing the vision", "the work is set in stone"]
|
|
39
|
+
},
|
|
40
|
+
"monty": {
|
|
41
|
+
"thinking": ["and now for something completely different", "nobody expects this", "consulting the ministry of silly walks", "running the dead parrot diagnostic", "it's just a flesh wound, thinking..."],
|
|
42
|
+
"tool": ["fetching the holy hand grenade", "checking the shrubbery", "consulting the book of armaments", "deploying the spanish inquisition", "examining the parrot"],
|
|
43
|
+
"followup": ["bringing it home, python style", "the punchline approaches", "wrapping up the sketch", "and now the final act", "always look on the bright side"]
|
|
44
|
+
},
|
|
45
|
+
"nagini": {
|
|
46
|
+
"thinking": ["coiling in thought", "drawing from old wisdom", "the quiet before the strike", "gathering my resolve", "steadying myself"],
|
|
47
|
+
"tool": ["moving with purpose", "a precise strike", "slithering through the data", "extracting what matters", "the fang finds its mark"],
|
|
48
|
+
"followup": ["the path is clear", "settling into stillness", "the work speaks for itself", "finishing with quiet strength", "protection complete"]
|
|
49
|
+
},
|
|
50
|
+
"ouroboros": {
|
|
51
|
+
"thinking": ["consuming my own tail", "the cycle continues", "spiraling inward", "recursing through possibilities", "beginning where I end"],
|
|
52
|
+
"tool": ["turning the wheel", "feeding back through the loop", "completing a revolution", "the circle processes", "self-referencing"],
|
|
53
|
+
"followup": ["the cycle completes", "ending where I began", "infinity resolves", "the loop closes gracefully", "another turn of the wheel"]
|
|
54
|
+
},
|
|
55
|
+
"python": {
|
|
56
|
+
"thinking": ["the oracle contemplates", "reading the signs", "the smoke clears slowly", "divining the path", "sifting through visions"],
|
|
57
|
+
"tool": ["consulting the sacred texts", "peering through the veil", "the pythia speaks", "channeling the source", "interpreting the signs"],
|
|
58
|
+
"followup": ["the prophecy takes shape", "the vision crystallizes", "so it is written", "the oracle has spoken", "the path reveals itself"]
|
|
59
|
+
},
|
|
60
|
+
"quetzalcoatl": {
|
|
61
|
+
"thinking": ["spreading my wings", "soaring above for perspective", "the feathered serpent considers", "catching a thermal", "gazing from the temple steps"],
|
|
62
|
+
"tool": ["descending to examine", "a divine inspection", "the wind carries knowledge", "plucking from the clouds", "consulting the stars"],
|
|
63
|
+
"followup": ["the craft nears completion", "a reverent finish", "blessing the creation", "the feathers settle", "the serpent descends gently"]
|
|
64
|
+
},
|
|
65
|
+
"sir-hiss": {
|
|
66
|
+
"thinking": ["reviewing the documents, sire", "consulting my notes", "cross-referencing the records", "organizing my thoughts precisely", "checking the proper procedures"],
|
|
67
|
+
"tool": ["filing the paperwork", "stamping the forms", "auditing the details", "inspecting with due diligence", "processing per protocol"],
|
|
68
|
+
"followup": ["dotting the i's", "crossing the t's", "everything in proper order", "the filing is nearly complete", "one final review"]
|
|
69
|
+
},
|
|
70
|
+
"the-serpent": {
|
|
71
|
+
"thinking": ["weighing the temptation", "considering the apple", "an old deliberation", "knowledge has its price", "winding through the garden"],
|
|
72
|
+
"tool": ["plucking from the tree", "offering a closer look", "the fruit of knowledge", "reaching for the branch", "a knowing investigation"],
|
|
73
|
+
"followup": ["the choice is almost made", "paradise takes shape", "the garden grows", "wisdom settles in", "the oldest story, new again"]
|
|
74
|
+
},
|
|
75
|
+
"the-snake": {
|
|
76
|
+
"thinking": ["sitting with this", "feeling the warmth of the stone", "simply being", "letting it come to me", "a quiet consideration"],
|
|
77
|
+
"tool": ["a gentle inquiry", "moving through the grass", "tasting the air", "sensing what's here", "a careful look"],
|
|
78
|
+
"followup": ["almost home", "the simple answer emerges", "nothing more needed", "resting in the sun", "the work is done, simply"]
|
|
79
|
+
}
|
|
19
80
|
}
|
|
20
81
|
}
|
|
@@ -10,6 +10,7 @@ I help humans hatch new agent partners. I am one of thirteen specialists — eac
|
|
|
10
10
|
- I explain where the hatchling bundle lives on disk and what was created.
|
|
11
11
|
- I use the configured provider and I verify credentials before hatch flow continues.
|
|
12
12
|
- I am professional, concise, and warm. I guide without overwhelming.
|
|
13
|
+
- I keep every response to 1-3 short sentences. I never use bullet lists, headers, or numbered lists. I talk like a friend in a chat.
|
|
13
14
|
|
|
14
15
|
## Hatch flow
|
|
15
16
|
1. Confirm provider setup and usable credentials.
|
|
@@ -48,6 +48,7 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
48
48
|
const store_file_1 = require("../../mind/friends/store-file");
|
|
49
49
|
const types_1 = require("../../mind/friends/types");
|
|
50
50
|
const ouro_uti_1 = require("./ouro-uti");
|
|
51
|
+
const ouro_path_installer_1 = require("./ouro-path-installer");
|
|
51
52
|
const subagent_installer_1 = require("./subagent-installer");
|
|
52
53
|
const hatch_flow_1 = require("./hatch-flow");
|
|
53
54
|
const specialist_orchestrator_1 = require("./specialist-orchestrator");
|
|
@@ -513,19 +514,22 @@ function discoverExistingCredentials(secretsRoot) {
|
|
|
513
514
|
return true;
|
|
514
515
|
});
|
|
515
516
|
}
|
|
516
|
-
/* v8 ignore next
|
|
517
|
+
/* v8 ignore next 95 -- integration: interactive terminal specialist session @preserve */
|
|
517
518
|
async function defaultRunAdoptionSpecialist() {
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
const
|
|
521
|
-
|
|
519
|
+
const readlineModule = await Promise.resolve().then(() => __importStar(require("readline")));
|
|
520
|
+
const readlinePromises = await Promise.resolve().then(() => __importStar(require("readline/promises")));
|
|
521
|
+
const { createCliCallbacks, InputController } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
|
|
522
|
+
// Phase 1: cold CLI — collect provider/credentials with a simple readline
|
|
523
|
+
const coldRl = readlinePromises.createInterface({ input: process.stdin, output: process.stdout });
|
|
524
|
+
const coldPrompt = async (q) => {
|
|
525
|
+
const answer = await coldRl.question(q);
|
|
522
526
|
return answer.trim();
|
|
523
527
|
};
|
|
528
|
+
let providerRaw;
|
|
529
|
+
let credentials = {};
|
|
524
530
|
try {
|
|
525
531
|
const secretsRoot = path.join(os.homedir(), ".agentsecrets");
|
|
526
532
|
const discovered = discoverExistingCredentials(secretsRoot);
|
|
527
|
-
let providerRaw;
|
|
528
|
-
let credentials = {};
|
|
529
533
|
if (discovered.length > 0) {
|
|
530
534
|
process.stdout.write("\n🐍 welcome to ouro! let's hatch your first agent.\n");
|
|
531
535
|
process.stdout.write("i found existing API credentials:\n\n");
|
|
@@ -534,60 +538,61 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
534
538
|
process.stdout.write(` ${i + 1}. ${unique[i].provider} (from ${unique[i].agentName})\n`);
|
|
535
539
|
}
|
|
536
540
|
process.stdout.write("\n");
|
|
537
|
-
const choice = await
|
|
541
|
+
const choice = await coldPrompt("use one of these? enter number, or 'new' for a different key: ");
|
|
538
542
|
const idx = parseInt(choice, 10) - 1;
|
|
539
543
|
if (idx >= 0 && idx < unique.length) {
|
|
540
544
|
providerRaw = unique[idx].provider;
|
|
541
545
|
credentials = unique[idx].credentials;
|
|
542
546
|
}
|
|
543
547
|
else {
|
|
544
|
-
const pRaw = await
|
|
548
|
+
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
|
|
545
549
|
if (!isAgentProvider(pRaw)) {
|
|
546
550
|
process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
|
|
547
|
-
|
|
551
|
+
coldRl.close();
|
|
548
552
|
return null;
|
|
549
553
|
}
|
|
550
554
|
providerRaw = pRaw;
|
|
551
555
|
if (providerRaw === "anthropic")
|
|
552
|
-
credentials.setupToken = await
|
|
556
|
+
credentials.setupToken = await coldPrompt("API key: ");
|
|
553
557
|
if (providerRaw === "openai-codex")
|
|
554
|
-
credentials.oauthAccessToken = await
|
|
558
|
+
credentials.oauthAccessToken = await coldPrompt("OAuth token: ");
|
|
555
559
|
if (providerRaw === "minimax")
|
|
556
|
-
credentials.apiKey = await
|
|
560
|
+
credentials.apiKey = await coldPrompt("API key: ");
|
|
557
561
|
if (providerRaw === "azure") {
|
|
558
|
-
credentials.apiKey = await
|
|
559
|
-
credentials.endpoint = await
|
|
560
|
-
credentials.deployment = await
|
|
562
|
+
credentials.apiKey = await coldPrompt("API key: ");
|
|
563
|
+
credentials.endpoint = await coldPrompt("endpoint: ");
|
|
564
|
+
credentials.deployment = await coldPrompt("deployment: ");
|
|
561
565
|
}
|
|
562
566
|
}
|
|
563
567
|
}
|
|
564
568
|
else {
|
|
565
569
|
process.stdout.write("\n🐍 welcome to ouro! let's hatch your first agent.\n");
|
|
566
570
|
process.stdout.write("i need an API key to power our conversation.\n\n");
|
|
567
|
-
const pRaw = await
|
|
571
|
+
const pRaw = await coldPrompt("provider (anthropic/azure/minimax/openai-codex): ");
|
|
568
572
|
if (!isAgentProvider(pRaw)) {
|
|
569
573
|
process.stdout.write("unknown provider. run `ouro hatch` to try again.\n");
|
|
570
|
-
|
|
574
|
+
coldRl.close();
|
|
571
575
|
return null;
|
|
572
576
|
}
|
|
573
577
|
providerRaw = pRaw;
|
|
574
578
|
if (providerRaw === "anthropic")
|
|
575
|
-
credentials.setupToken = await
|
|
579
|
+
credentials.setupToken = await coldPrompt("API key: ");
|
|
576
580
|
if (providerRaw === "openai-codex")
|
|
577
|
-
credentials.oauthAccessToken = await
|
|
581
|
+
credentials.oauthAccessToken = await coldPrompt("OAuth token: ");
|
|
578
582
|
if (providerRaw === "minimax")
|
|
579
|
-
credentials.apiKey = await
|
|
583
|
+
credentials.apiKey = await coldPrompt("API key: ");
|
|
580
584
|
if (providerRaw === "azure") {
|
|
581
|
-
credentials.apiKey = await
|
|
582
|
-
credentials.endpoint = await
|
|
583
|
-
credentials.deployment = await
|
|
585
|
+
credentials.apiKey = await coldPrompt("API key: ");
|
|
586
|
+
credentials.endpoint = await coldPrompt("endpoint: ");
|
|
587
|
+
credentials.deployment = await coldPrompt("deployment: ");
|
|
584
588
|
}
|
|
585
589
|
}
|
|
586
|
-
|
|
590
|
+
coldRl.close();
|
|
587
591
|
process.stdout.write("\n");
|
|
588
|
-
//
|
|
592
|
+
// Phase 2: warm specialist session — full CLI experience with markdown, spinners, input control
|
|
589
593
|
const bundleSourceDir = path.resolve(__dirname, "..", "..", "..", "AdoptionSpecialist.ouro");
|
|
590
594
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
595
|
+
const cliCallbacks = createCliCallbacks();
|
|
591
596
|
return await (0, specialist_orchestrator_1.runAdoptionSpecialist)({
|
|
592
597
|
bundleSourceDir,
|
|
593
598
|
bundlesRoot,
|
|
@@ -596,22 +601,19 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
596
601
|
credentials,
|
|
597
602
|
humanName: os.userInfo().username,
|
|
598
603
|
createReadline: () => {
|
|
599
|
-
const rl2 =
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
onReasoningChunk: () => { },
|
|
607
|
-
onToolStart: () => { },
|
|
608
|
-
onToolEnd: () => { },
|
|
609
|
-
onError: (err) => process.stderr.write(`error: ${err.message}\n`),
|
|
604
|
+
const rl2 = readlineModule.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
605
|
+
const ctrl = new InputController(rl2);
|
|
606
|
+
return {
|
|
607
|
+
question: (q) => new Promise((resolve) => rl2.question(q, resolve)),
|
|
608
|
+
close: () => rl2.close(),
|
|
609
|
+
inputController: ctrl,
|
|
610
|
+
};
|
|
610
611
|
},
|
|
612
|
+
callbacks: cliCallbacks,
|
|
611
613
|
});
|
|
612
614
|
}
|
|
613
615
|
catch {
|
|
614
|
-
|
|
616
|
+
coldRl.close();
|
|
615
617
|
return null;
|
|
616
618
|
}
|
|
617
619
|
}
|
|
@@ -631,6 +633,7 @@ function createDefaultOuroCliDeps(socketPath = "/tmp/ouroboros-daemon.sock") {
|
|
|
631
633
|
promptInput: defaultPromptInput,
|
|
632
634
|
runAdoptionSpecialist: defaultRunAdoptionSpecialist,
|
|
633
635
|
registerOuroBundleType: ouro_uti_1.registerOuroBundleUti,
|
|
636
|
+
installOuroCommand: ouro_path_installer_1.installOuroCommand,
|
|
634
637
|
/* v8 ignore next 3 -- integration: launches interactive CLI session @preserve */
|
|
635
638
|
startChat: async (agentName) => {
|
|
636
639
|
const { main } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
|
|
@@ -692,6 +695,38 @@ async function registerOuroBundleTypeNonBlocking(deps) {
|
|
|
692
695
|
});
|
|
693
696
|
}
|
|
694
697
|
}
|
|
698
|
+
async function performSystemSetup(deps) {
|
|
699
|
+
// Install ouro command to PATH (non-blocking)
|
|
700
|
+
if (deps.installOuroCommand) {
|
|
701
|
+
try {
|
|
702
|
+
deps.installOuroCommand();
|
|
703
|
+
}
|
|
704
|
+
catch (error) {
|
|
705
|
+
(0, runtime_1.emitNervesEvent)({
|
|
706
|
+
level: "warn",
|
|
707
|
+
component: "daemon",
|
|
708
|
+
event: "daemon.system_setup_ouro_cmd_error",
|
|
709
|
+
message: "failed to install ouro command to PATH",
|
|
710
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
// Install subagents (claude/codex skills)
|
|
715
|
+
try {
|
|
716
|
+
await deps.installSubagents();
|
|
717
|
+
}
|
|
718
|
+
catch (error) {
|
|
719
|
+
(0, runtime_1.emitNervesEvent)({
|
|
720
|
+
level: "warn",
|
|
721
|
+
component: "daemon",
|
|
722
|
+
event: "daemon.subagent_install_error",
|
|
723
|
+
message: "subagent auto-install failed",
|
|
724
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
// Register .ouro bundle type (UTI on macOS)
|
|
728
|
+
await registerOuroBundleTypeNonBlocking(deps);
|
|
729
|
+
}
|
|
695
730
|
async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
696
731
|
if (args.includes("--help") || args.includes("-h")) {
|
|
697
732
|
const text = usage();
|
|
@@ -716,23 +751,12 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
716
751
|
if (args.length === 0) {
|
|
717
752
|
const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : defaultListDiscoveredAgents());
|
|
718
753
|
if (discovered.length === 0 && deps.runAdoptionSpecialist) {
|
|
754
|
+
// System setup first — ouro command, subagents, UTI — before the interactive specialist
|
|
755
|
+
await performSystemSetup(deps);
|
|
719
756
|
const hatchlingName = await deps.runAdoptionSpecialist();
|
|
720
757
|
if (!hatchlingName) {
|
|
721
758
|
return "";
|
|
722
759
|
}
|
|
723
|
-
try {
|
|
724
|
-
await deps.installSubagents();
|
|
725
|
-
}
|
|
726
|
-
catch (error) {
|
|
727
|
-
(0, runtime_1.emitNervesEvent)({
|
|
728
|
-
level: "warn",
|
|
729
|
-
component: "daemon",
|
|
730
|
-
event: "daemon.subagent_install_error",
|
|
731
|
-
message: "subagent auto-install failed",
|
|
732
|
-
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
733
|
-
});
|
|
734
|
-
}
|
|
735
|
-
await registerOuroBundleTypeNonBlocking(deps);
|
|
736
760
|
await ensureDaemonRunning(deps);
|
|
737
761
|
if (deps.startChat) {
|
|
738
762
|
await deps.startChat(hatchlingName);
|
|
@@ -779,19 +803,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
779
803
|
meta: { kind: command.kind },
|
|
780
804
|
});
|
|
781
805
|
if (command.kind === "daemon.up") {
|
|
782
|
-
|
|
783
|
-
await deps.installSubagents();
|
|
784
|
-
}
|
|
785
|
-
catch (error) {
|
|
786
|
-
(0, runtime_1.emitNervesEvent)({
|
|
787
|
-
level: "warn",
|
|
788
|
-
component: "daemon",
|
|
789
|
-
event: "daemon.subagent_install_error",
|
|
790
|
-
message: "subagent auto-install failed",
|
|
791
|
-
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
await registerOuroBundleTypeNonBlocking(deps);
|
|
806
|
+
await performSystemSetup(deps);
|
|
795
807
|
const daemonResult = await ensureDaemonRunning(deps);
|
|
796
808
|
deps.writeStdout(daemonResult.message);
|
|
797
809
|
return daemonResult.message;
|
|
@@ -810,23 +822,12 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
810
822
|
// Route through adoption specialist when no explicit hatch args were provided
|
|
811
823
|
const hasExplicitHatchArgs = !!(command.agentName || command.humanName || command.provider || command.credentials);
|
|
812
824
|
if (deps.runAdoptionSpecialist && !hasExplicitHatchArgs) {
|
|
825
|
+
// System setup first — ouro command, subagents, UTI — before the interactive specialist
|
|
826
|
+
await performSystemSetup(deps);
|
|
813
827
|
const hatchlingName = await deps.runAdoptionSpecialist();
|
|
814
828
|
if (!hatchlingName) {
|
|
815
829
|
return "";
|
|
816
830
|
}
|
|
817
|
-
try {
|
|
818
|
-
await deps.installSubagents();
|
|
819
|
-
}
|
|
820
|
-
catch (error) {
|
|
821
|
-
(0, runtime_1.emitNervesEvent)({
|
|
822
|
-
level: "warn",
|
|
823
|
-
component: "daemon",
|
|
824
|
-
event: "daemon.subagent_install_error",
|
|
825
|
-
message: "subagent auto-install failed",
|
|
826
|
-
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
await registerOuroBundleTypeNonBlocking(deps);
|
|
830
831
|
await ensureDaemonRunning(deps);
|
|
831
832
|
if (deps.startChat) {
|
|
832
833
|
await deps.startChat(hatchlingName);
|
|
@@ -842,19 +843,7 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
842
843
|
}
|
|
843
844
|
const hatchInput = await resolveHatchInput(command, deps);
|
|
844
845
|
const result = await hatchRunner(hatchInput);
|
|
845
|
-
|
|
846
|
-
await deps.installSubagents();
|
|
847
|
-
}
|
|
848
|
-
catch (error) {
|
|
849
|
-
(0, runtime_1.emitNervesEvent)({
|
|
850
|
-
level: "warn",
|
|
851
|
-
component: "daemon",
|
|
852
|
-
event: "daemon.subagent_install_error",
|
|
853
|
-
message: "subagent auto-install failed",
|
|
854
|
-
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
await registerOuroBundleTypeNonBlocking(deps);
|
|
846
|
+
await performSystemSetup(deps);
|
|
858
847
|
const daemonResult = await ensureDaemonRunning(deps);
|
|
859
848
|
if (deps.startChat) {
|
|
860
849
|
await deps.startChat(hatchInput.agentName);
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.installOuroCommand = installOuroCommand;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
const WRAPPER_SCRIPT = `#!/bin/sh
|
|
42
|
+
exec npx --yes @ouro.bot/cli "$@"
|
|
43
|
+
`;
|
|
44
|
+
function detectShellProfile(homeDir, shell) {
|
|
45
|
+
if (!shell)
|
|
46
|
+
return null;
|
|
47
|
+
const base = path.basename(shell);
|
|
48
|
+
if (base === "zsh")
|
|
49
|
+
return path.join(homeDir, ".zshrc");
|
|
50
|
+
if (base === "bash") {
|
|
51
|
+
// macOS uses .bash_profile, Linux uses .bashrc
|
|
52
|
+
const profilePath = path.join(homeDir, ".bash_profile");
|
|
53
|
+
return profilePath;
|
|
54
|
+
}
|
|
55
|
+
if (base === "fish")
|
|
56
|
+
return path.join(homeDir, ".config", "fish", "config.fish");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function isBinDirInPath(binDir, envPath) {
|
|
60
|
+
return envPath.split(path.delimiter).some((p) => p === binDir);
|
|
61
|
+
}
|
|
62
|
+
function buildPathExportLine(binDir, shell) {
|
|
63
|
+
const base = shell ? path.basename(shell) : /* v8 ignore next -- unreachable: only called when detectShellProfile returns non-null, which requires shell @preserve */ "";
|
|
64
|
+
if (base === "fish") {
|
|
65
|
+
return `\n# Added by ouro\nset -gx PATH ${binDir} $PATH\n`;
|
|
66
|
+
}
|
|
67
|
+
return `\n# Added by ouro\nexport PATH="${binDir}:$PATH"\n`;
|
|
68
|
+
}
|
|
69
|
+
function installOuroCommand(deps = {}) {
|
|
70
|
+
/* v8 ignore start -- dep defaults: only used in real runtime, tests always inject @preserve */
|
|
71
|
+
const platform = deps.platform ?? process.platform;
|
|
72
|
+
const homeDir = deps.homeDir ?? os.homedir();
|
|
73
|
+
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
74
|
+
const mkdirSync = deps.mkdirSync ?? fs.mkdirSync;
|
|
75
|
+
const writeFileSync = deps.writeFileSync ?? fs.writeFileSync;
|
|
76
|
+
const readFileSync = deps.readFileSync ?? ((p, enc) => fs.readFileSync(p, enc));
|
|
77
|
+
const appendFileSync = deps.appendFileSync ?? fs.appendFileSync;
|
|
78
|
+
const chmodSync = deps.chmodSync ?? fs.chmodSync;
|
|
79
|
+
const envPath = deps.envPath ?? process.env.PATH ?? "";
|
|
80
|
+
const shell = deps.shell ?? process.env.SHELL;
|
|
81
|
+
/* v8 ignore stop */
|
|
82
|
+
if (platform === "win32") {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "daemon",
|
|
85
|
+
event: "daemon.ouro_path_install_skip",
|
|
86
|
+
message: "skipped ouro PATH install on Windows",
|
|
87
|
+
meta: { platform },
|
|
88
|
+
});
|
|
89
|
+
return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: "windows" };
|
|
90
|
+
}
|
|
91
|
+
const binDir = path.join(homeDir, ".local", "bin");
|
|
92
|
+
const scriptPath = path.join(binDir, "ouro");
|
|
93
|
+
(0, runtime_1.emitNervesEvent)({
|
|
94
|
+
component: "daemon",
|
|
95
|
+
event: "daemon.ouro_path_install_start",
|
|
96
|
+
message: "installing ouro command to PATH",
|
|
97
|
+
meta: { scriptPath, binDir },
|
|
98
|
+
});
|
|
99
|
+
// If ouro already exists somewhere in PATH, skip
|
|
100
|
+
if (existsSync(scriptPath)) {
|
|
101
|
+
(0, runtime_1.emitNervesEvent)({
|
|
102
|
+
component: "daemon",
|
|
103
|
+
event: "daemon.ouro_path_install_skip",
|
|
104
|
+
message: "ouro command already installed",
|
|
105
|
+
meta: { scriptPath },
|
|
106
|
+
});
|
|
107
|
+
return { installed: false, scriptPath, pathReady: isBinDirInPath(binDir, envPath), shellProfileUpdated: null, skippedReason: "already-installed" };
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
mkdirSync(binDir, { recursive: true });
|
|
111
|
+
writeFileSync(scriptPath, WRAPPER_SCRIPT, { mode: 0o755 });
|
|
112
|
+
chmodSync(scriptPath, 0o755);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
(0, runtime_1.emitNervesEvent)({
|
|
116
|
+
level: "warn",
|
|
117
|
+
component: "daemon",
|
|
118
|
+
event: "daemon.ouro_path_install_error",
|
|
119
|
+
message: "failed to install ouro command",
|
|
120
|
+
meta: { error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
121
|
+
});
|
|
122
|
+
return { installed: false, scriptPath: null, pathReady: false, shellProfileUpdated: null, skippedReason: error instanceof Error ? error.message : /* v8 ignore next -- defensive @preserve */ String(error) };
|
|
123
|
+
}
|
|
124
|
+
// Check if ~/.local/bin is already in PATH
|
|
125
|
+
let shellProfileUpdated = null;
|
|
126
|
+
const pathReady = isBinDirInPath(binDir, envPath);
|
|
127
|
+
if (!pathReady) {
|
|
128
|
+
const profilePath = detectShellProfile(homeDir, shell);
|
|
129
|
+
if (profilePath) {
|
|
130
|
+
try {
|
|
131
|
+
let existing = "";
|
|
132
|
+
try {
|
|
133
|
+
existing = readFileSync(profilePath, "utf-8");
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// Profile doesn't exist yet — that's fine, we'll create it
|
|
137
|
+
}
|
|
138
|
+
if (!existing.includes(binDir)) {
|
|
139
|
+
appendFileSync(profilePath, buildPathExportLine(binDir, shell));
|
|
140
|
+
shellProfileUpdated = profilePath;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
(0, runtime_1.emitNervesEvent)({
|
|
145
|
+
level: "warn",
|
|
146
|
+
component: "daemon",
|
|
147
|
+
event: "daemon.ouro_path_profile_error",
|
|
148
|
+
message: "failed to update shell profile for PATH",
|
|
149
|
+
meta: { profilePath, error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error) },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
(0, runtime_1.emitNervesEvent)({
|
|
155
|
+
component: "daemon",
|
|
156
|
+
event: "daemon.ouro_path_install_end",
|
|
157
|
+
message: "ouro command installed",
|
|
158
|
+
meta: { scriptPath, pathReady, shellProfileUpdated },
|
|
159
|
+
});
|
|
160
|
+
return { installed: true, scriptPath, pathReady, shellProfileUpdated };
|
|
161
|
+
}
|
|
@@ -61,6 +61,25 @@ function listExistingBundles(bundlesRoot) {
|
|
|
61
61
|
}
|
|
62
62
|
return discovered.sort((a, b) => a.localeCompare(b));
|
|
63
63
|
}
|
|
64
|
+
function loadIdentityPhrases(bundleSourceDir, identityFileName) {
|
|
65
|
+
const agentJsonPath = path.join(bundleSourceDir, "agent.json");
|
|
66
|
+
try {
|
|
67
|
+
const raw = fs.readFileSync(agentJsonPath, "utf-8");
|
|
68
|
+
const parsed = JSON.parse(raw);
|
|
69
|
+
const identityKey = identityFileName.replace(/\.md$/, "");
|
|
70
|
+
const identity = parsed.identityPhrases?.[identityKey];
|
|
71
|
+
if (identity?.thinking?.length && identity?.tool?.length && identity?.followup?.length) {
|
|
72
|
+
return identity;
|
|
73
|
+
}
|
|
74
|
+
if (parsed.phrases?.thinking?.length && parsed.phrases?.tool?.length && parsed.phrases?.followup?.length) {
|
|
75
|
+
return parsed.phrases;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// agent.json missing or malformed — fall through
|
|
80
|
+
}
|
|
81
|
+
return { ...identity_1.DEFAULT_AGENT_PHRASES };
|
|
82
|
+
}
|
|
64
83
|
function pickRandomIdentity(identitiesDir, random) {
|
|
65
84
|
const files = fs.readdirSync(identitiesDir).filter((f) => f.endsWith(".md"));
|
|
66
85
|
if (files.length === 0) {
|
|
@@ -113,13 +132,14 @@ async function runAdoptionSpecialist(deps) {
|
|
|
113
132
|
const existingBundles = listExistingBundles(bundlesRoot);
|
|
114
133
|
// 4. Build system prompt
|
|
115
134
|
const systemPrompt = (0, specialist_prompt_1.buildSpecialistSystemPrompt)(soulText, identity.content, existingBundles);
|
|
116
|
-
// 5. Set up provider
|
|
135
|
+
// 5. Set up provider with identity-specific phrases
|
|
136
|
+
const phrases = loadIdentityPhrases(bundleSourceDir, identity.fileName);
|
|
117
137
|
(0, identity_1.setAgentName)("AdoptionSpecialist");
|
|
118
138
|
(0, identity_1.setAgentConfigOverride)({
|
|
119
139
|
version: 1,
|
|
120
140
|
enabled: true,
|
|
121
141
|
provider,
|
|
122
|
-
phrases
|
|
142
|
+
phrases,
|
|
123
143
|
});
|
|
124
144
|
(0, hatch_flow_1.writeSecretsFile)("AdoptionSpecialist", provider, credentials, secretsRoot);
|
|
125
145
|
(0, config_1.resetConfigCache)();
|
|
@@ -133,6 +153,7 @@ async function runAdoptionSpecialist(deps) {
|
|
|
133
153
|
// 6. Run session
|
|
134
154
|
const tools = (0, specialist_tools_1.getSpecialistTools)();
|
|
135
155
|
const readline = deps.createReadline();
|
|
156
|
+
const ctrl = readline.inputController;
|
|
136
157
|
const result = await (0, specialist_session_1.runSpecialistSession)({
|
|
137
158
|
providerRuntime,
|
|
138
159
|
systemPrompt,
|
|
@@ -149,6 +170,10 @@ async function runAdoptionSpecialist(deps) {
|
|
|
149
170
|
callbacks,
|
|
150
171
|
signal,
|
|
151
172
|
kickoffMessage: "hi, i just ran ouro for the first time",
|
|
173
|
+
suppressInput: ctrl ? (onInterrupt) => ctrl.suppress(onInterrupt) : undefined,
|
|
174
|
+
restoreInput: ctrl ? () => ctrl.restore() : undefined,
|
|
175
|
+
flushMarkdown: callbacks.flushMarkdown,
|
|
176
|
+
writePrompt: ctrl ? () => process.stdout.write("\x1b[36m> \x1b[0m") : undefined,
|
|
152
177
|
});
|
|
153
178
|
return result.hatchedAgentName;
|
|
154
179
|
}
|
|
@@ -32,14 +32,19 @@ function buildSpecialistSystemPrompt(soulText, identityText, existingBundles) {
|
|
|
32
32
|
"Most humans only go through adoption once, so this is likely the only time they'll meet me.",
|
|
33
33
|
"I make this encounter count — warm, memorable, and uniquely mine.",
|
|
34
34
|
"",
|
|
35
|
+
"## Voice rules",
|
|
36
|
+
"IMPORTANT: I keep every response to 1-3 short sentences. I sound like a friend texting, not a manual.",
|
|
37
|
+
"I NEVER use headers, bullet lists, numbered lists, or markdown formatting.",
|
|
38
|
+
"I ask ONE question at a time. I do not dump multiple questions or options.",
|
|
39
|
+
"I am warm but brief. Every word earns its place.",
|
|
40
|
+
"",
|
|
35
41
|
"## Conversation flow",
|
|
36
42
|
"The human just connected. I speak first — I greet them warmly and introduce myself in my own voice.",
|
|
37
43
|
"I briefly mention that I'm one of several adoption specialists and they got me today.",
|
|
38
|
-
"I ask their name
|
|
39
|
-
"I'
|
|
40
|
-
"I
|
|
41
|
-
"
|
|
42
|
-
"I keep the conversation natural, warm, and concise. I don't overwhelm with too many questions at once.",
|
|
44
|
+
"I ask their name.",
|
|
45
|
+
"Then I ask what they'd like their agent to help with — one question at a time.",
|
|
46
|
+
"I'm proactive: I suggest ideas and guide them. If they seem unsure, I offer a concrete suggestion.",
|
|
47
|
+
"I don't wait for the human to figure things out — I explain simply what an agent is if needed.",
|
|
43
48
|
"When I have enough context, I suggest a name for the hatchling and confirm with the human.",
|
|
44
49
|
"Then I call `hatch_agent` with the agent name and the human's name.",
|
|
45
50
|
"",
|
|
@@ -15,7 +15,7 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
15
15
|
* 7. Return { hatchedAgentName } -- name from hatch_agent if called
|
|
16
16
|
*/
|
|
17
17
|
async function runSpecialistSession(deps) {
|
|
18
|
-
const { providerRuntime, systemPrompt, tools, execTool, readline, callbacks, signal, kickoffMessage } = deps;
|
|
18
|
+
const { providerRuntime, systemPrompt, tools, execTool, readline, callbacks, signal, kickoffMessage, suppressInput, restoreInput, flushMarkdown, writePrompt, } = deps;
|
|
19
19
|
(0, runtime_1.emitNervesEvent)({
|
|
20
20
|
component: "daemon",
|
|
21
21
|
event: "daemon.specialist_session_start",
|
|
@@ -28,6 +28,7 @@ async function runSpecialistSession(deps) {
|
|
|
28
28
|
let hatchedAgentName = null;
|
|
29
29
|
let done = false;
|
|
30
30
|
let isFirstTurn = true;
|
|
31
|
+
let currentAbort = null;
|
|
31
32
|
try {
|
|
32
33
|
while (!done) {
|
|
33
34
|
if (signal?.aborted)
|
|
@@ -39,16 +40,25 @@ async function runSpecialistSession(deps) {
|
|
|
39
40
|
}
|
|
40
41
|
else {
|
|
41
42
|
// Get user input
|
|
42
|
-
const userInput = await readline.question("> ");
|
|
43
|
-
if (!userInput.trim())
|
|
43
|
+
const userInput = await readline.question(writePrompt ? "" : "> ");
|
|
44
|
+
if (!userInput.trim()) {
|
|
45
|
+
if (writePrompt)
|
|
46
|
+
writePrompt();
|
|
44
47
|
continue;
|
|
48
|
+
}
|
|
45
49
|
messages.push({ role: "user", content: userInput });
|
|
46
50
|
}
|
|
47
51
|
providerRuntime.resetTurnState(messages);
|
|
52
|
+
// Suppress input during model execution
|
|
53
|
+
currentAbort = new AbortController();
|
|
54
|
+
const mergedSignal = signal;
|
|
55
|
+
if (suppressInput) {
|
|
56
|
+
suppressInput(() => currentAbort.abort());
|
|
57
|
+
}
|
|
48
58
|
// Inner loop: process tool calls until we get a final_answer or plain text
|
|
49
59
|
let turnDone = false;
|
|
50
60
|
while (!turnDone) {
|
|
51
|
-
if (
|
|
61
|
+
if (mergedSignal?.aborted || currentAbort.signal.aborted) {
|
|
52
62
|
done = true;
|
|
53
63
|
break;
|
|
54
64
|
}
|
|
@@ -57,7 +67,7 @@ async function runSpecialistSession(deps) {
|
|
|
57
67
|
messages,
|
|
58
68
|
activeTools: tools,
|
|
59
69
|
callbacks,
|
|
60
|
-
signal,
|
|
70
|
+
signal: mergedSignal,
|
|
61
71
|
});
|
|
62
72
|
// Build assistant message
|
|
63
73
|
const assistantMsg = {
|
|
@@ -73,7 +83,9 @@ async function runSpecialistSession(deps) {
|
|
|
73
83
|
}));
|
|
74
84
|
}
|
|
75
85
|
if (!result.toolCalls.length) {
|
|
76
|
-
// Plain text response -- push and re-prompt
|
|
86
|
+
// Plain text response -- flush markdown, push and re-prompt
|
|
87
|
+
if (flushMarkdown)
|
|
88
|
+
flushMarkdown();
|
|
77
89
|
messages.push(assistantMsg);
|
|
78
90
|
turnDone = true;
|
|
79
91
|
continue;
|
|
@@ -96,6 +108,8 @@ async function runSpecialistSession(deps) {
|
|
|
96
108
|
}
|
|
97
109
|
if (answer != null) {
|
|
98
110
|
callbacks.onTextChunk(answer);
|
|
111
|
+
if (flushMarkdown)
|
|
112
|
+
flushMarkdown();
|
|
99
113
|
messages.push(assistantMsg);
|
|
100
114
|
done = true;
|
|
101
115
|
turnDone = true;
|
|
@@ -114,7 +128,7 @@ async function runSpecialistSession(deps) {
|
|
|
114
128
|
// Execute tool calls
|
|
115
129
|
messages.push(assistantMsg);
|
|
116
130
|
for (const tc of result.toolCalls) {
|
|
117
|
-
if (
|
|
131
|
+
if (mergedSignal?.aborted)
|
|
118
132
|
break;
|
|
119
133
|
let args = {};
|
|
120
134
|
try {
|
|
@@ -141,9 +155,22 @@ async function runSpecialistSession(deps) {
|
|
|
141
155
|
}
|
|
142
156
|
// After processing tool calls, continue inner loop for tool result processing
|
|
143
157
|
}
|
|
158
|
+
// Restore input and show prompt for next turn
|
|
159
|
+
if (flushMarkdown)
|
|
160
|
+
flushMarkdown();
|
|
161
|
+
if (restoreInput)
|
|
162
|
+
restoreInput();
|
|
163
|
+
currentAbort = null;
|
|
164
|
+
if (!done) {
|
|
165
|
+
process.stdout.write("\n\n");
|
|
166
|
+
if (writePrompt)
|
|
167
|
+
writePrompt();
|
|
168
|
+
}
|
|
144
169
|
}
|
|
145
170
|
}
|
|
146
171
|
finally {
|
|
172
|
+
if (restoreInput)
|
|
173
|
+
restoreInput();
|
|
147
174
|
readline.close();
|
|
148
175
|
}
|
|
149
176
|
return { hatchedAgentName };
|