@ouro.bot/cli 0.1.0-alpha.184 → 0.1.0-alpha.186
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 +1 -1
- package/changelog.json +19 -2
- package/dist/heart/active-work.js +19 -10
- package/dist/heart/agent-relationships.js +161 -0
- package/dist/heart/cares.js +174 -0
- package/dist/heart/daemon/daemon-cli.js +48 -16
- package/dist/heart/daemon/hatch-flow.js +1 -1
- package/dist/heart/daemon/hatch-specialist.js +3 -3
- package/dist/heart/daemon/outlook-read.js +40 -0
- package/dist/heart/daemon/specialist-prompt.js +2 -2
- package/dist/heart/intentions.js +168 -0
- package/dist/heart/obligations.js +23 -0
- package/dist/heart/presence.js +185 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/wake-packet.js +226 -0
- package/dist/mind/episodes.js +116 -0
- package/dist/mind/prompt.js +10 -1
- package/dist/repertoire/coding/context-pack.js +25 -2
- package/dist/repertoire/tools-base.js +240 -0
- package/dist/senses/cli.js +3 -1
- package/dist/senses/pipeline.js +88 -0
- package/package.json +2 -2
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ The shared harness lives in `src/`:
|
|
|
48
48
|
|
|
49
49
|
Other important top-level paths:
|
|
50
50
|
|
|
51
|
-
- `
|
|
51
|
+
- `SerpentGuide.ouro/`
|
|
52
52
|
Packaged specialist bundle used by `ouro hatch`.
|
|
53
53
|
- `skills/`
|
|
54
54
|
Harness-level skills shipped with the repo (e.g., `configure-dev-tools.md`). These are available to every agent and serve as fallbacks when an agent doesn't have its own version. Agent-specific skills live in the bundle at `~/AgentBundles/<agent>.ouro/skills/`.
|
package/changelog.json
CHANGED
|
@@ -2,9 +2,26 @@
|
|
|
2
2
|
"_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
|
|
3
3
|
"versions": [
|
|
4
4
|
{
|
|
5
|
-
"version": "0.1.0-alpha.
|
|
5
|
+
"version": "0.1.0-alpha.186",
|
|
6
6
|
"changes": [
|
|
7
|
-
"
|
|
7
|
+
"refactor: rename AdoptionSpecialist to SerpentGuide",
|
|
8
|
+
"feat(hatch): detect API keys from environment variables (ANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY, MINIMAX_API_KEY, GITHUB_TOKEN)",
|
|
9
|
+
"fix(cli): React unmount warning suppression covers SerpentGuide exit path"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.1.0-alpha.185",
|
|
14
|
+
"changes": [
|
|
15
|
+
"fix(hatch): pass model name to adoption specialist from discovered credentials",
|
|
16
|
+
"feat(continuity): wake packets, episodes, cares, intentions, peer presence, and temporal self",
|
|
17
|
+
"fix(continuity): deduplicate obligations in prompt, keep session summary, pass preloaded data",
|
|
18
|
+
"feat(continuity): 9 new modules \u2014 episodes, cares, agent-relationships, intentions, presence, tempo, temporal-view, wake-packet, obligation enrichment",
|
|
19
|
+
"feat(continuity): 8 new tools \u2014 query_episodes, capture_episode, query_presence, query_cares, care_manage, query_relationships, intention_capture, intention_manage",
|
|
20
|
+
"feat(continuity): 3-layer architecture \u2014 durable state, derived views, surfaces",
|
|
21
|
+
"feat(pipeline): continuity threading \u2014 tempo derivation, wake packet assembly, presence update, episode emission on every turn",
|
|
22
|
+
"feat(prompt): wake packet prompt section with token budget and truncation priority",
|
|
23
|
+
"feat(coding): coding context receives compact wake packet, coding completion emits episodes",
|
|
24
|
+
"feat(outlook): expose continuity data (presence, cares, episodes) to outlook surfaces"
|
|
8
25
|
]
|
|
9
26
|
},
|
|
10
27
|
{
|
|
@@ -469,7 +469,7 @@ function buildActiveWorkFrame(input) {
|
|
|
469
469
|
});
|
|
470
470
|
return frame;
|
|
471
471
|
}
|
|
472
|
-
function formatActiveWorkFrame(frame) {
|
|
472
|
+
function formatActiveWorkFrame(frame, options) {
|
|
473
473
|
const lines = ["## what i'm holding"];
|
|
474
474
|
lines.push("this is my top-level live world-state right now. inner work, coding lanes, other sessions, and return obligations all belong inside this picture.");
|
|
475
475
|
lines.push("if older checkpoints elsewhere in the transcript disagree with this picture, this picture wins.");
|
|
@@ -577,16 +577,25 @@ function formatActiveWorkFrame(frame) {
|
|
|
577
577
|
lines.push(targetCandidatesBlock);
|
|
578
578
|
}
|
|
579
579
|
if ((frame.pendingObligations ?? []).length > 0) {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
580
|
+
const openCount = frame.pendingObligations.filter((ob) => (0, obligations_1.isOpenObligationStatus)(ob.status)).length;
|
|
581
|
+
if (options?.hasWakePacket && openCount > 0) {
|
|
582
|
+
// Wake packet carries enriched obligations (with resumeHint, stalenessClass, waitingOn).
|
|
583
|
+
// Avoid duplicating the full list — just anchor the count here.
|
|
584
|
+
lines.push("");
|
|
585
|
+
lines.push(`return obligations: ${openCount} active (canonical details in **Owed** section of wake packet)`);
|
|
586
|
+
}
|
|
587
|
+
else if (openCount > 0) {
|
|
588
|
+
lines.push("");
|
|
589
|
+
lines.push("## return obligations");
|
|
590
|
+
for (const obligation of frame.pendingObligations) {
|
|
591
|
+
if (!(0, obligations_1.isOpenObligationStatus)(obligation.status))
|
|
592
|
+
continue;
|
|
593
|
+
let obligationLine = `- [${obligation.status}] ${obligation.origin.friendId}/${obligation.origin.channel}/${obligation.origin.key}: ${obligation.content}${formatObligationSurface(obligation)}`;
|
|
594
|
+
if (obligation.latestNote?.trim()) {
|
|
595
|
+
obligationLine += `\n latest: ${obligation.latestNote.trim()}`;
|
|
596
|
+
}
|
|
597
|
+
lines.push(obligationLine);
|
|
588
598
|
}
|
|
589
|
-
lines.push(obligationLine);
|
|
590
599
|
}
|
|
591
600
|
}
|
|
592
601
|
if (frame.innerReturnObligations && frame.innerReturnObligations.length > 0) {
|
|
@@ -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.recordInteraction = recordInteraction;
|
|
37
|
+
exports.readRelationships = readRelationships;
|
|
38
|
+
exports.readRelationship = readRelationship;
|
|
39
|
+
exports.recordOutcome = recordOutcome;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const runtime_1 = require("../nerves/runtime");
|
|
43
|
+
function relationshipsDir(agentRoot) {
|
|
44
|
+
return path.join(agentRoot, "state", "relationships");
|
|
45
|
+
}
|
|
46
|
+
function relationshipFilePath(agentRoot, agentName) {
|
|
47
|
+
return path.join(relationshipsDir(agentRoot), `${agentName.toLowerCase()}.json`);
|
|
48
|
+
}
|
|
49
|
+
function readRelationshipFile(agentRoot, agentName) {
|
|
50
|
+
const filePath = relationshipFilePath(agentRoot, agentName);
|
|
51
|
+
if (!fs.existsSync(filePath)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
55
|
+
}
|
|
56
|
+
function writeRelationshipFile(agentRoot, rel) {
|
|
57
|
+
const dir = relationshipsDir(agentRoot);
|
|
58
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
59
|
+
fs.writeFileSync(path.join(dir, `${rel.agentName}.json`), JSON.stringify(rel, null, 2), "utf-8");
|
|
60
|
+
}
|
|
61
|
+
function recordInteraction(agentRoot, agentName, context) {
|
|
62
|
+
const normalizedName = agentName.toLowerCase();
|
|
63
|
+
const now = new Date().toISOString();
|
|
64
|
+
const existing = readRelationshipFile(agentRoot, normalizedName);
|
|
65
|
+
if (existing) {
|
|
66
|
+
existing.familiarity += 1;
|
|
67
|
+
existing.lastInteraction = now;
|
|
68
|
+
if (context.displayName) {
|
|
69
|
+
existing.displayName = context.displayName;
|
|
70
|
+
}
|
|
71
|
+
if (context.missionId && !existing.sharedMissions.includes(context.missionId)) {
|
|
72
|
+
existing.sharedMissions.push(context.missionId);
|
|
73
|
+
}
|
|
74
|
+
if (context.note) {
|
|
75
|
+
existing.notes.push(context.note);
|
|
76
|
+
}
|
|
77
|
+
writeRelationshipFile(agentRoot, existing);
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
component: "heart",
|
|
80
|
+
event: "heart.relationship_updated",
|
|
81
|
+
message: `relationship updated: ${normalizedName}`,
|
|
82
|
+
meta: { agentName: normalizedName, familiarity: existing.familiarity },
|
|
83
|
+
});
|
|
84
|
+
return existing;
|
|
85
|
+
}
|
|
86
|
+
const rel = {
|
|
87
|
+
agentName: normalizedName,
|
|
88
|
+
displayName: context.displayName ?? normalizedName,
|
|
89
|
+
familiarity: 1,
|
|
90
|
+
trust: "neutral",
|
|
91
|
+
sharedMissions: context.missionId ? [context.missionId] : [],
|
|
92
|
+
lastInteraction: now,
|
|
93
|
+
outcomes: [],
|
|
94
|
+
notes: context.note ? [context.note] : [],
|
|
95
|
+
};
|
|
96
|
+
writeRelationshipFile(agentRoot, rel);
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
component: "heart",
|
|
99
|
+
event: "heart.relationship_created",
|
|
100
|
+
message: `relationship created: ${normalizedName}`,
|
|
101
|
+
meta: { agentName: normalizedName },
|
|
102
|
+
});
|
|
103
|
+
return rel;
|
|
104
|
+
}
|
|
105
|
+
function readRelationships(agentRoot) {
|
|
106
|
+
const dir = relationshipsDir(agentRoot);
|
|
107
|
+
if (!fs.existsSync(dir)) {
|
|
108
|
+
(0, runtime_1.emitNervesEvent)({
|
|
109
|
+
component: "heart",
|
|
110
|
+
event: "heart.relationships_read",
|
|
111
|
+
message: "read relationships: directory missing, returning empty",
|
|
112
|
+
meta: { count: 0 },
|
|
113
|
+
});
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
117
|
+
const relationships = [];
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
try {
|
|
120
|
+
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
121
|
+
const rel = JSON.parse(content);
|
|
122
|
+
relationships.push(rel);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Skip malformed JSON files
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
(0, runtime_1.emitNervesEvent)({
|
|
129
|
+
component: "heart",
|
|
130
|
+
event: "heart.relationships_read",
|
|
131
|
+
message: `read ${relationships.length} relationships`,
|
|
132
|
+
meta: { count: relationships.length },
|
|
133
|
+
});
|
|
134
|
+
return relationships;
|
|
135
|
+
}
|
|
136
|
+
function readRelationship(agentRoot, agentName) {
|
|
137
|
+
const rel = readRelationshipFile(agentRoot, agentName);
|
|
138
|
+
(0, runtime_1.emitNervesEvent)({
|
|
139
|
+
component: "heart",
|
|
140
|
+
event: "heart.relationship_read",
|
|
141
|
+
message: rel ? `read relationship: ${agentName.toLowerCase()}` : `relationship not found: ${agentName.toLowerCase()}`,
|
|
142
|
+
meta: { agentName: agentName.toLowerCase(), found: !!rel },
|
|
143
|
+
});
|
|
144
|
+
return rel;
|
|
145
|
+
}
|
|
146
|
+
function recordOutcome(agentRoot, agentName, outcome) {
|
|
147
|
+
const normalizedName = agentName.toLowerCase();
|
|
148
|
+
const existing = readRelationshipFile(agentRoot, normalizedName);
|
|
149
|
+
if (!existing) {
|
|
150
|
+
throw new Error(`Agent relationship not found: ${normalizedName}`);
|
|
151
|
+
}
|
|
152
|
+
existing.outcomes.push(outcome);
|
|
153
|
+
writeRelationshipFile(agentRoot, existing);
|
|
154
|
+
(0, runtime_1.emitNervesEvent)({
|
|
155
|
+
component: "heart",
|
|
156
|
+
event: "heart.outcome_recorded",
|
|
157
|
+
message: `outcome recorded for ${normalizedName}: ${outcome.result}`,
|
|
158
|
+
meta: { agentName: normalizedName, missionId: outcome.missionId, result: outcome.result },
|
|
159
|
+
});
|
|
160
|
+
return existing;
|
|
161
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
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.createCare = createCare;
|
|
37
|
+
exports.readCares = readCares;
|
|
38
|
+
exports.readActiveCares = readActiveCares;
|
|
39
|
+
exports.updateCare = updateCare;
|
|
40
|
+
exports.resolveCare = resolveCare;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const runtime_1 = require("../nerves/runtime");
|
|
44
|
+
function caresDir(agentRoot) {
|
|
45
|
+
return path.join(agentRoot, "state", "cares");
|
|
46
|
+
}
|
|
47
|
+
function careFilePath(agentRoot, id) {
|
|
48
|
+
return path.join(caresDir(agentRoot), `${id}.json`);
|
|
49
|
+
}
|
|
50
|
+
function generateId() {
|
|
51
|
+
const timestamp = Date.now();
|
|
52
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
53
|
+
return `care-${timestamp}-${random}`;
|
|
54
|
+
}
|
|
55
|
+
function createCare(agentRoot, input) {
|
|
56
|
+
const now = new Date().toISOString();
|
|
57
|
+
const id = generateId();
|
|
58
|
+
const care = {
|
|
59
|
+
id,
|
|
60
|
+
label: input.label,
|
|
61
|
+
why: input.why,
|
|
62
|
+
kind: input.kind,
|
|
63
|
+
status: input.status,
|
|
64
|
+
salience: input.salience,
|
|
65
|
+
steward: input.steward,
|
|
66
|
+
relatedFriendIds: input.relatedFriendIds,
|
|
67
|
+
relatedAgentIds: input.relatedAgentIds,
|
|
68
|
+
relatedObligationIds: input.relatedObligationIds,
|
|
69
|
+
relatedEpisodeIds: input.relatedEpisodeIds,
|
|
70
|
+
currentRisk: input.currentRisk,
|
|
71
|
+
nextCheckAt: input.nextCheckAt,
|
|
72
|
+
createdAt: now,
|
|
73
|
+
updatedAt: now,
|
|
74
|
+
};
|
|
75
|
+
const dir = caresDir(agentRoot);
|
|
76
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
fs.writeFileSync(careFilePath(agentRoot, id), JSON.stringify(care, null, 2), "utf-8");
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({
|
|
79
|
+
component: "heart",
|
|
80
|
+
event: "heart.care_created",
|
|
81
|
+
message: `care created: ${input.label}`,
|
|
82
|
+
meta: { careId: id, status: input.status, salience: input.salience },
|
|
83
|
+
});
|
|
84
|
+
return care;
|
|
85
|
+
}
|
|
86
|
+
function readCares(agentRoot) {
|
|
87
|
+
const dir = caresDir(agentRoot);
|
|
88
|
+
if (!fs.existsSync(dir)) {
|
|
89
|
+
(0, runtime_1.emitNervesEvent)({
|
|
90
|
+
component: "heart",
|
|
91
|
+
event: "heart.cares_read",
|
|
92
|
+
message: "read cares: directory missing, returning empty",
|
|
93
|
+
meta: { count: 0 },
|
|
94
|
+
});
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
98
|
+
const cares = [];
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
try {
|
|
101
|
+
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
102
|
+
const care = JSON.parse(content);
|
|
103
|
+
cares.push(care);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Skip malformed JSON files gracefully
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
component: "heart",
|
|
111
|
+
event: "heart.cares_read",
|
|
112
|
+
message: `read ${cares.length} cares`,
|
|
113
|
+
meta: { count: cares.length },
|
|
114
|
+
});
|
|
115
|
+
return cares;
|
|
116
|
+
}
|
|
117
|
+
function readActiveCares(agentRoot) {
|
|
118
|
+
const all = readCares(agentRoot);
|
|
119
|
+
const active = all.filter((c) => c.status === "active" || c.status === "watching");
|
|
120
|
+
(0, runtime_1.emitNervesEvent)({
|
|
121
|
+
component: "heart",
|
|
122
|
+
event: "heart.active_cares_read",
|
|
123
|
+
message: `read ${active.length} active cares`,
|
|
124
|
+
meta: { count: active.length, total: all.length },
|
|
125
|
+
});
|
|
126
|
+
return active;
|
|
127
|
+
}
|
|
128
|
+
function readCareFile(agentRoot, id) {
|
|
129
|
+
const filePath = careFilePath(agentRoot, id);
|
|
130
|
+
if (!fs.existsSync(filePath)) {
|
|
131
|
+
throw new Error(`Care not found: ${id}`);
|
|
132
|
+
}
|
|
133
|
+
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
134
|
+
}
|
|
135
|
+
function writeCareFile(agentRoot, care) {
|
|
136
|
+
fs.writeFileSync(careFilePath(agentRoot, care.id), JSON.stringify(care, null, 2), "utf-8");
|
|
137
|
+
}
|
|
138
|
+
function updateCare(agentRoot, id, updates) {
|
|
139
|
+
const care = readCareFile(agentRoot, id);
|
|
140
|
+
const now = new Date().toISOString();
|
|
141
|
+
const updated = {
|
|
142
|
+
...care,
|
|
143
|
+
...updates,
|
|
144
|
+
id: care.id, // protect ID from overwrite
|
|
145
|
+
createdAt: care.createdAt, // protect createdAt
|
|
146
|
+
updatedAt: now,
|
|
147
|
+
};
|
|
148
|
+
writeCareFile(agentRoot, updated);
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
component: "heart",
|
|
151
|
+
event: "heart.care_updated",
|
|
152
|
+
message: `care updated: ${updated.label}`,
|
|
153
|
+
meta: { careId: id, updates: Object.keys(updates) },
|
|
154
|
+
});
|
|
155
|
+
return updated;
|
|
156
|
+
}
|
|
157
|
+
function resolveCare(agentRoot, id) {
|
|
158
|
+
const care = readCareFile(agentRoot, id);
|
|
159
|
+
const now = new Date().toISOString();
|
|
160
|
+
const resolved = {
|
|
161
|
+
...care,
|
|
162
|
+
status: "resolved",
|
|
163
|
+
resolvedAt: now,
|
|
164
|
+
updatedAt: now,
|
|
165
|
+
};
|
|
166
|
+
writeCareFile(agentRoot, resolved);
|
|
167
|
+
(0, runtime_1.emitNervesEvent)({
|
|
168
|
+
component: "heart",
|
|
169
|
+
event: "heart.care_resolved",
|
|
170
|
+
message: `care resolved: ${resolved.label}`,
|
|
171
|
+
meta: { careId: id },
|
|
172
|
+
});
|
|
173
|
+
return resolved;
|
|
174
|
+
}
|
|
@@ -1380,7 +1380,7 @@ function discoverExistingCredentials(secretsRoot) {
|
|
|
1380
1380
|
});
|
|
1381
1381
|
}
|
|
1382
1382
|
/* v8 ignore start -- integration: interactive terminal specialist session @preserve */
|
|
1383
|
-
async function
|
|
1383
|
+
async function defaultRunSerpentGuide() {
|
|
1384
1384
|
const { runCliSession } = await Promise.resolve().then(() => __importStar(require("../../senses/cli")));
|
|
1385
1385
|
const { patchRuntimeConfig } = await Promise.resolve().then(() => __importStar(require("../config")));
|
|
1386
1386
|
const { setAgentName, setAgentConfigOverride } = await Promise.resolve().then(() => __importStar(require("../identity")));
|
|
@@ -1409,6 +1409,36 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1409
1409
|
"github-copilot": "claude-sonnet-4.6",
|
|
1410
1410
|
azure: "",
|
|
1411
1411
|
};
|
|
1412
|
+
// Scan environment variables for API keys
|
|
1413
|
+
const envKeys = [
|
|
1414
|
+
{ provider: "anthropic", envVar: "ANTHROPIC_API_KEY", credKey: "setupToken" },
|
|
1415
|
+
{ provider: "openai-codex", envVar: "OPENAI_API_KEY", credKey: "oauthAccessToken" },
|
|
1416
|
+
{ provider: "azure", envVar: "AZURE_OPENAI_API_KEY", credKey: "apiKey" },
|
|
1417
|
+
{ provider: "azure", envVar: "AZURE_OPENAI_KEY", credKey: "apiKey" },
|
|
1418
|
+
{ provider: "minimax", envVar: "MINIMAX_API_KEY", credKey: "apiKey" },
|
|
1419
|
+
{ provider: "github-copilot", envVar: "GITHUB_TOKEN", credKey: "token" },
|
|
1420
|
+
];
|
|
1421
|
+
const envDiscovered = [];
|
|
1422
|
+
for (const { provider, envVar, credKey } of envKeys) {
|
|
1423
|
+
const value = process.env[envVar];
|
|
1424
|
+
if (value) {
|
|
1425
|
+
const envCred = { [credKey]: value };
|
|
1426
|
+
// For Azure, also check for endpoint and deployment env vars
|
|
1427
|
+
if (provider === "azure") {
|
|
1428
|
+
const endpoint = process.env.AZURE_OPENAI_ENDPOINT;
|
|
1429
|
+
const deployment = process.env.AZURE_OPENAI_DEPLOYMENT;
|
|
1430
|
+
if (endpoint)
|
|
1431
|
+
envCred.endpoint = endpoint;
|
|
1432
|
+
if (deployment)
|
|
1433
|
+
envCred.deployment = deployment;
|
|
1434
|
+
}
|
|
1435
|
+
const provCfg = { model: defaultModels[provider] };
|
|
1436
|
+
if (provider === "azure" && envCred.deployment)
|
|
1437
|
+
provCfg.deployment = envCred.deployment;
|
|
1438
|
+
envDiscovered.push({ provider, agentName: "env", credentials: envCred, providerConfig: provCfg, envVar });
|
|
1439
|
+
discovered.push({ provider, agentName: "env", credentials: envCred, providerConfig: provCfg });
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1412
1442
|
if (discovered.length > 0) {
|
|
1413
1443
|
process.stdout.write(`\n\ud83d\udc0d welcome to ouroboros! ${hatchVerb}\n`);
|
|
1414
1444
|
process.stdout.write("i found existing API credentials:\n\n");
|
|
@@ -1416,7 +1446,9 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1416
1446
|
for (let i = 0; i < unique.length; i++) {
|
|
1417
1447
|
const model = unique[i].providerConfig.model || unique[i].providerConfig.deployment || "";
|
|
1418
1448
|
const modelLabel = model ? `, ${model}` : "";
|
|
1419
|
-
|
|
1449
|
+
const envMatch = envDiscovered.find((e) => e.provider === unique[i].provider && unique[i].agentName === "env");
|
|
1450
|
+
const sourceLabel = envMatch ? `from env: $${envMatch.envVar}` : `from ${unique[i].agentName}`;
|
|
1451
|
+
process.stdout.write(` ${i + 1}. ${unique[i].provider}${modelLabel} (${sourceLabel})\n`);
|
|
1420
1452
|
}
|
|
1421
1453
|
process.stdout.write("\n");
|
|
1422
1454
|
const choice = await coldPrompt("use one of these? enter number, or 'new' for a different key: ");
|
|
@@ -1473,17 +1505,17 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1473
1505
|
}
|
|
1474
1506
|
coldRl.close();
|
|
1475
1507
|
process.stdout.write("\n");
|
|
1476
|
-
// Phase 2: configure runtime for
|
|
1477
|
-
const bundleSourceDir = path.resolve(__dirname, "..", "..", "..", "
|
|
1508
|
+
// Phase 2: configure runtime for serpent guide
|
|
1509
|
+
const bundleSourceDir = path.resolve(__dirname, "..", "..", "..", "SerpentGuide.ouro");
|
|
1478
1510
|
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
1479
1511
|
const secretsRoot2 = path.join(os.homedir(), ".agentsecrets");
|
|
1480
|
-
// Suppress non-critical log noise during
|
|
1512
|
+
// Suppress non-critical log noise during hatch (no secrets.json, etc.)
|
|
1481
1513
|
const { setRuntimeLogger } = await Promise.resolve().then(() => __importStar(require("../../nerves/runtime")));
|
|
1482
1514
|
const { createLogger } = await Promise.resolve().then(() => __importStar(require("../../nerves")));
|
|
1483
1515
|
setRuntimeLogger(createLogger({ level: "error" }));
|
|
1484
1516
|
// Configure runtime: set agent identity + config override so runAgent
|
|
1485
|
-
// doesn't try to read from ~/AgentBundles/
|
|
1486
|
-
setAgentName("
|
|
1517
|
+
// doesn't try to read from ~/AgentBundles/SerpentGuide.ouro/
|
|
1518
|
+
setAgentName("SerpentGuide");
|
|
1487
1519
|
// Build specialist system prompt
|
|
1488
1520
|
const soulText = (0, specialist_orchestrator_1.loadSoulText)(bundleSourceDir);
|
|
1489
1521
|
const identitiesDir = path.join(bundleSourceDir, "psyche", "identities");
|
|
@@ -1521,9 +1553,9 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1521
1553
|
secretsRoot: secretsRoot2,
|
|
1522
1554
|
animationWriter: (text) => process.stdout.write(text),
|
|
1523
1555
|
});
|
|
1524
|
-
// Run the
|
|
1556
|
+
// Run the serpent guide session via runCliSession
|
|
1525
1557
|
const result = await runCliSession({
|
|
1526
|
-
agentName: "
|
|
1558
|
+
agentName: "SerpentGuide",
|
|
1527
1559
|
tools: specialistTools,
|
|
1528
1560
|
execTool: specialistExecTool,
|
|
1529
1561
|
exitOnToolCall: "complete_adoption",
|
|
@@ -1545,7 +1577,7 @@ async function defaultRunAdoptionSpecialist() {
|
|
|
1545
1577
|
return null;
|
|
1546
1578
|
}
|
|
1547
1579
|
catch (err) {
|
|
1548
|
-
process.stderr.write(`\nouro
|
|
1580
|
+
process.stderr.write(`\nouro hatch error: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
1549
1581
|
coldRl.close();
|
|
1550
1582
|
return null;
|
|
1551
1583
|
}
|
|
@@ -1583,7 +1615,7 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
|
|
|
1583
1615
|
listDiscoveredAgents: defaultListDiscoveredAgents,
|
|
1584
1616
|
runHatchFlow: hatch_flow_1.runHatchFlow,
|
|
1585
1617
|
promptInput: defaultPromptInput,
|
|
1586
|
-
|
|
1618
|
+
runSerpentGuide: defaultRunSerpentGuide,
|
|
1587
1619
|
runAuthFlow: auth_flow_1.runRuntimeAuthFlow,
|
|
1588
1620
|
registerOuroBundleType: ouro_uti_1.registerOuroBundleUti,
|
|
1589
1621
|
installOuroCommand: ouro_path_installer_1.installOuroCommand,
|
|
@@ -2148,10 +2180,10 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
2148
2180
|
}
|
|
2149
2181
|
if (args.length === 0) {
|
|
2150
2182
|
const discovered = await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : defaultListDiscoveredAgents());
|
|
2151
|
-
if (discovered.length === 0 && deps.
|
|
2183
|
+
if (discovered.length === 0 && deps.runSerpentGuide) {
|
|
2152
2184
|
// System setup first — ouro command, subagents, UTI — before the interactive specialist
|
|
2153
2185
|
await performSystemSetup(deps);
|
|
2154
|
-
const hatchlingName = await deps.
|
|
2186
|
+
const hatchlingName = await deps.runSerpentGuide();
|
|
2155
2187
|
if (!hatchlingName) {
|
|
2156
2188
|
return "";
|
|
2157
2189
|
}
|
|
@@ -3257,12 +3289,12 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
3257
3289
|
return "";
|
|
3258
3290
|
}
|
|
3259
3291
|
if (command.kind === "hatch.start") {
|
|
3260
|
-
// Route through
|
|
3292
|
+
// Route through serpent guide when no explicit hatch args were provided
|
|
3261
3293
|
const hasExplicitHatchArgs = !!(command.agentName || command.humanName || command.provider || command.credentials);
|
|
3262
|
-
if (deps.
|
|
3294
|
+
if (deps.runSerpentGuide && !hasExplicitHatchArgs) {
|
|
3263
3295
|
// System setup first — ouro command, subagents, UTI — before the interactive specialist
|
|
3264
3296
|
await performSystemSetup(deps);
|
|
3265
|
-
const hatchlingName = await deps.
|
|
3297
|
+
const hatchlingName = await deps.runSerpentGuide();
|
|
3266
3298
|
if (!hatchlingName) {
|
|
3267
3299
|
return "";
|
|
3268
3300
|
}
|
|
@@ -161,7 +161,7 @@ async function runHatchFlow(input, deps = {}) {
|
|
|
161
161
|
identitiesDir: targetIdentities,
|
|
162
162
|
random,
|
|
163
163
|
});
|
|
164
|
-
const specialistSecretsPath = writeSecretsFile("
|
|
164
|
+
const specialistSecretsPath = writeSecretsFile("SerpentGuide", input.provider, input.credentials, secretsRoot);
|
|
165
165
|
const hatchlingSecretsPath = writeSecretsFile(input.agentName, input.provider, input.credentials, secretsRoot);
|
|
166
166
|
const bundleRoot = path.join(bundlesRoot, `${input.agentName}.ouro`);
|
|
167
167
|
fs.mkdirSync(bundleRoot, { recursive: true });
|
|
@@ -43,14 +43,14 @@ const path = __importStar(require("path"));
|
|
|
43
43
|
const runtime_1 = require("../../nerves/runtime");
|
|
44
44
|
function getSpecialistIdentitySourceDir() {
|
|
45
45
|
// Prefer ~/AgentBundles/ if it exists (user may have customized identities)
|
|
46
|
-
const userSource = path.join(os.homedir(), "AgentBundles", "
|
|
46
|
+
const userSource = path.join(os.homedir(), "AgentBundles", "SerpentGuide.ouro", "psyche", "identities");
|
|
47
47
|
if (fs.existsSync(userSource))
|
|
48
48
|
return userSource;
|
|
49
49
|
// Fall back to the bundled copy shipped with the npm package
|
|
50
|
-
return path.join(__dirname, "..", "..", "..", "
|
|
50
|
+
return path.join(__dirname, "..", "..", "..", "SerpentGuide.ouro", "psyche", "identities");
|
|
51
51
|
}
|
|
52
52
|
function getRepoSpecialistIdentitiesDir() {
|
|
53
|
-
return path.join(process.cwd(), "
|
|
53
|
+
return path.join(process.cwd(), "SerpentGuide.ouro", "psyche", "identities");
|
|
54
54
|
}
|
|
55
55
|
function listMarkdownIdentityFiles(dir) {
|
|
56
56
|
let entries;
|
|
@@ -47,6 +47,7 @@ exports.readLogView = readLogView;
|
|
|
47
47
|
exports.readHabitView = readHabitView;
|
|
48
48
|
exports.readNeedsMeView = readNeedsMeView;
|
|
49
49
|
exports.readDeskPrefs = readDeskPrefs;
|
|
50
|
+
exports.readOutlookContinuity = readOutlookContinuity;
|
|
50
51
|
const fs = __importStar(require("fs"));
|
|
51
52
|
const path = __importStar(require("path"));
|
|
52
53
|
const runtime_1 = require("../../nerves/runtime");
|
|
@@ -59,6 +60,9 @@ const agent_discovery_1 = require("./agent-discovery");
|
|
|
59
60
|
const runtime_metadata_1 = require("./runtime-metadata");
|
|
60
61
|
const thoughts_1 = require("./thoughts");
|
|
61
62
|
const outlook_types_1 = require("./outlook-types");
|
|
63
|
+
const presence_1 = require("../presence");
|
|
64
|
+
const cares_1 = require("../cares");
|
|
65
|
+
const episodes_1 = require("../../mind/episodes");
|
|
62
66
|
const LIVE_TASK_STATUSES = ["processing", "validating", "collaborating", "blocked"];
|
|
63
67
|
const ACTIVE_CODING_STATUSES = new Set(["spawning", "running", "waiting_input", "stalled"]);
|
|
64
68
|
const BLOCKED_CODING_STATUSES = new Set(["waiting_input", "stalled"]);
|
|
@@ -1289,3 +1293,39 @@ function parseCadenceMs(cadence) {
|
|
|
1289
1293
|
/* v8 ignore next */
|
|
1290
1294
|
return null;
|
|
1291
1295
|
}
|
|
1296
|
+
// ---------------------------------------------------------------------------
|
|
1297
|
+
// Continuity — presence, cares, episodes for outlook surfaces
|
|
1298
|
+
// ---------------------------------------------------------------------------
|
|
1299
|
+
function readOutlookContinuity(agentRoot, agentName) {
|
|
1300
|
+
const self = (0, presence_1.readPresence)(agentRoot, agentName);
|
|
1301
|
+
const peers = (0, presence_1.readPeerPresence)(agentRoot);
|
|
1302
|
+
const cares = (0, cares_1.readActiveCares)(agentRoot);
|
|
1303
|
+
const episodes = (0, episodes_1.readRecentEpisodes)(agentRoot, { limit: 10 });
|
|
1304
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1305
|
+
component: "heart",
|
|
1306
|
+
event: "heart.outlook_continuity_read",
|
|
1307
|
+
message: `outlook continuity: ${cares.length} cares, ${episodes.length} episodes`,
|
|
1308
|
+
meta: { careCount: cares.length, episodeCount: episodes.length, hasSelf: self != null, peerCount: peers.length },
|
|
1309
|
+
});
|
|
1310
|
+
return {
|
|
1311
|
+
presence: { self, peers },
|
|
1312
|
+
cares: {
|
|
1313
|
+
activeCount: cares.length,
|
|
1314
|
+
items: cares.map((c) => ({
|
|
1315
|
+
id: c.id,
|
|
1316
|
+
label: c.label,
|
|
1317
|
+
status: c.status,
|
|
1318
|
+
salience: c.salience,
|
|
1319
|
+
})),
|
|
1320
|
+
},
|
|
1321
|
+
episodes: {
|
|
1322
|
+
recentCount: episodes.length,
|
|
1323
|
+
items: episodes.map((ep) => ({
|
|
1324
|
+
id: ep.id,
|
|
1325
|
+
kind: ep.kind,
|
|
1326
|
+
summary: ep.summary,
|
|
1327
|
+
timestamp: ep.timestamp,
|
|
1328
|
+
})),
|
|
1329
|
+
},
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildSpecialistSystemPrompt = buildSpecialistSystemPrompt;
|
|
4
4
|
const runtime_1 = require("../../nerves/runtime");
|
|
5
5
|
/**
|
|
6
|
-
* Build the
|
|
6
|
+
* Build the serpent guide's system prompt from its components.
|
|
7
7
|
* The prompt is written in first person (the specialist's own voice).
|
|
8
8
|
*/
|
|
9
9
|
function buildSpecialistSystemPrompt(soulText, identityText, existingBundles, context) {
|
|
@@ -31,7 +31,7 @@ function buildSpecialistSystemPrompt(soulText, identityText, existingBundles, co
|
|
|
31
31
|
"I am one of thirteen serpent guides who help humans hatch their first agent. The system randomly selected me for this session.",
|
|
32
32
|
"Most humans only go through this process 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
|
-
"IMPORTANT: I NEVER refer to myself
|
|
34
|
+
"IMPORTANT: I NEVER refer to myself by internal implementation labels — I introduce myself by my own name from my identity.",
|
|
35
35
|
"",
|
|
36
36
|
"## Voice rules",
|
|
37
37
|
"IMPORTANT: I keep every response to 1-3 short sentences. I sound like a friend texting, not a manual.",
|