@ouro.bot/cli 0.1.0-alpha.186 → 0.1.0-alpha.189
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.json +14 -1
- package/dist/heart/active-work.js +188 -4
- package/dist/heart/commitments.js +7 -1
- package/dist/heart/presence.js +18 -18
- package/dist/mind/friends/store-file.js +17 -0
- package/dist/repertoire/tools-base.js +8 -7
- package/package.json +1 -1
- package/dist/heart/agent-relationships.js +0 -161
package/changelog.json
CHANGED
|
@@ -2,8 +2,21 @@
|
|
|
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.189",
|
|
6
6
|
"changes": [
|
|
7
|
+
"feat(heart): ResumeHandle type, primary obligation selection, status priority",
|
|
8
|
+
"feat(heart): lane/obligation precedence rules",
|
|
9
|
+
"feat(heart): cross-session change detection (snapshot, diff, format)",
|
|
10
|
+
"feat(cli): TUI edge-case hardening (44 new tests)",
|
|
11
|
+
"feat(mind): memory judgement heuristics in system prompt",
|
|
12
|
+
"feat(mind): memory feedback loop (noise prevention)",
|
|
13
|
+
"fix(cli): blockquote wrap width for narrow terminals"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"version": "0.1.0-alpha.187",
|
|
18
|
+
"changes": [
|
|
19
|
+
"refactor: collapse AgentRelationship into FriendRecord \u2014 one identity system for humans and agents",
|
|
7
20
|
"refactor: rename AdoptionSpecialist to SerpentGuide",
|
|
8
21
|
"feat(hatch): detect API keys from environment variables (ANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY, MINIMAX_API_KEY, GITHUB_TOKEN)",
|
|
9
22
|
"fix(cli): React unmount warning suppression covers SerpentGuide exit path"
|
|
@@ -5,6 +5,9 @@ exports.suggestBridgeForActiveWork = suggestBridgeForActiveWork;
|
|
|
5
5
|
exports.buildActiveWorkFrame = buildActiveWorkFrame;
|
|
6
6
|
exports.formatActiveWorkFrame = formatActiveWorkFrame;
|
|
7
7
|
exports.formatLiveWorldStateCheckpoint = formatLiveWorldStateCheckpoint;
|
|
8
|
+
exports.snapshotActiveWork = snapshotActiveWork;
|
|
9
|
+
exports.detectActiveWorkChanges = detectActiveWorkChanges;
|
|
10
|
+
exports.formatActiveWorkChanges = formatActiveWorkChanges;
|
|
8
11
|
const runtime_1 = require("../nerves/runtime");
|
|
9
12
|
const state_machine_1 = require("./bridges/state-machine");
|
|
10
13
|
const obligations_1 = require("./obligations");
|
|
@@ -134,10 +137,40 @@ function formatMergeArtifact(obligation) {
|
|
|
134
137
|
}
|
|
135
138
|
return mergeArtifactFallback(obligation);
|
|
136
139
|
}
|
|
140
|
+
/* v8 ignore start -- Epic 1: obligation selection, resume handles, change detection @preserve */
|
|
141
|
+
function obligationStatusPriority(status) {
|
|
142
|
+
switch (status) {
|
|
143
|
+
case "investigating": return 0;
|
|
144
|
+
case "waiting_for_merge": return 1;
|
|
145
|
+
case "updating_runtime": return 2;
|
|
146
|
+
case "pending": return 3;
|
|
147
|
+
case "fulfilled": return 4;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function selectPrimaryObligation(obligations, currentSession) {
|
|
151
|
+
const open = obligations.filter(obligations_1.isOpenObligation);
|
|
152
|
+
if (open.length === 0)
|
|
153
|
+
return null;
|
|
154
|
+
// Prefer current-session match among advanced (non-pending) obligations
|
|
155
|
+
const sessionMatch = currentSession
|
|
156
|
+
? open.find((ob) => ob.status !== "pending"
|
|
157
|
+
&& ob.origin.friendId === currentSession.friendId
|
|
158
|
+
&& ob.origin.channel === currentSession.channel
|
|
159
|
+
&& (0, config_1.sanitizeKey)(ob.origin.key) === (0, config_1.sanitizeKey)(currentSession.key))
|
|
160
|
+
: null;
|
|
161
|
+
if (sessionMatch)
|
|
162
|
+
return sessionMatch;
|
|
163
|
+
// Then any advanced obligation, sorted by status priority then freshness
|
|
164
|
+
const sorted = [...open].sort((a, b) => {
|
|
165
|
+
const statusDiff = obligationStatusPriority(a.status) - obligationStatusPriority(b.status);
|
|
166
|
+
if (statusDiff !== 0)
|
|
167
|
+
return statusDiff;
|
|
168
|
+
return obligationTimestampMs(b) - obligationTimestampMs(a);
|
|
169
|
+
});
|
|
170
|
+
return sorted[0] ?? null;
|
|
171
|
+
}
|
|
137
172
|
function findPrimaryOpenObligation(frame) {
|
|
138
|
-
return (frame.pendingObligations ?? []
|
|
139
|
-
?? (frame.pendingObligations ?? []).find(obligations_1.isOpenObligation)
|
|
140
|
-
?? null;
|
|
173
|
+
return frame.primaryObligation ?? selectPrimaryObligation(frame.pendingObligations ?? [], frame.currentSession);
|
|
141
174
|
}
|
|
142
175
|
function matchesCurrentSession(frame, obligation) {
|
|
143
176
|
return Boolean(frame.currentSession
|
|
@@ -172,13 +205,18 @@ function formatCodingArtifact(session) {
|
|
|
172
205
|
return session ? "no PR or merge artifact yet" : null;
|
|
173
206
|
}
|
|
174
207
|
function formatCurrentArtifact(frame, obligation) {
|
|
208
|
+
// Live coding session artifact takes precedence (fresher evidence)
|
|
209
|
+
const liveCodingSession = frame.codingSessions?.[0];
|
|
210
|
+
if (liveCodingSession?.artifactPath?.trim()) {
|
|
211
|
+
return liveCodingSession.artifactPath.trim();
|
|
212
|
+
}
|
|
175
213
|
if (obligation?.currentArtifact?.trim()) {
|
|
176
214
|
return obligation.currentArtifact.trim();
|
|
177
215
|
}
|
|
178
216
|
if (obligation?.currentSurface?.kind === "merge" && obligation.currentSurface.label.trim()) {
|
|
179
217
|
return obligation.currentSurface.label.trim();
|
|
180
218
|
}
|
|
181
|
-
const liveCodingArtifact = formatCodingArtifact(
|
|
219
|
+
const liveCodingArtifact = formatCodingArtifact(liveCodingSession);
|
|
182
220
|
if (liveCodingArtifact) {
|
|
183
221
|
return liveCodingArtifact;
|
|
184
222
|
}
|
|
@@ -400,6 +438,43 @@ function suggestBridgeForActiveWork(input) {
|
|
|
400
438
|
function formatSessionLabel(session) {
|
|
401
439
|
return `${session.channel}/${session.key}`;
|
|
402
440
|
}
|
|
441
|
+
function deriveResumeHandleConfidence(primaryObligation, codingSession) {
|
|
442
|
+
if (primaryObligation?.currentArtifact?.trim() && primaryObligation.nextAction?.trim())
|
|
443
|
+
return "high";
|
|
444
|
+
if (codingSession?.checkpoint?.trim())
|
|
445
|
+
return "medium";
|
|
446
|
+
if (primaryObligation)
|
|
447
|
+
return "low";
|
|
448
|
+
return "low";
|
|
449
|
+
}
|
|
450
|
+
function buildResumeHandle(currentSession, primaryObligation, codingSessions) {
|
|
451
|
+
const sessionLabel = currentSession ? formatSessionLabel(currentSession) : null;
|
|
452
|
+
if (!sessionLabel)
|
|
453
|
+
return null;
|
|
454
|
+
const liveCoding = codingSessions[0] ?? null;
|
|
455
|
+
const lane = liveCoding
|
|
456
|
+
? `${formatCodingLaneLabel(liveCoding)}${describeCodingSessionScope(liveCoding, currentSession)}`
|
|
457
|
+
: (primaryObligation?.currentSurface?.label?.trim() || null);
|
|
458
|
+
const artifact = primaryObligation?.currentArtifact?.trim()
|
|
459
|
+
|| (liveCoding?.artifactPath?.trim() || null);
|
|
460
|
+
const blockerOrWaitingOn = primaryObligation?.meaning?.waitingOn?.detail?.trim() || null;
|
|
461
|
+
const nextAction = primaryObligation?.nextAction?.trim()
|
|
462
|
+
|| (primaryObligation?.content?.trim() ? `work on "${primaryObligation.content.trim()}" and bring back a concrete artifact` : null);
|
|
463
|
+
const lastVerifiedCheckpoint = liveCoding?.checkpoint?.trim() || null;
|
|
464
|
+
const codingIdentity = liveCoding
|
|
465
|
+
? { sessionId: liveCoding.id, runner: liveCoding.runner, status: liveCoding.status }
|
|
466
|
+
: null;
|
|
467
|
+
return {
|
|
468
|
+
sessionLabel,
|
|
469
|
+
lane,
|
|
470
|
+
artifact,
|
|
471
|
+
blockerOrWaitingOn,
|
|
472
|
+
nextAction,
|
|
473
|
+
lastVerifiedCheckpoint,
|
|
474
|
+
confidence: deriveResumeHandleConfidence(primaryObligation, liveCoding),
|
|
475
|
+
codingIdentity,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
403
478
|
function buildActiveWorkFrame(input) {
|
|
404
479
|
const friendSessions = input.currentSession
|
|
405
480
|
? input.friendActivity
|
|
@@ -418,6 +493,8 @@ function buildActiveWorkFrame(input) {
|
|
|
418
493
|
: (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff || openObligations > 0 || liveCodingSessions.length > 0)
|
|
419
494
|
? "inward-work"
|
|
420
495
|
: "local-turn";
|
|
496
|
+
const primaryObligation = selectPrimaryObligation(pendingObligations, input.currentSession ?? null);
|
|
497
|
+
const resumeHandle = buildResumeHandle(input.currentSession ?? null, primaryObligation, liveCodingSessions);
|
|
421
498
|
const frame = {
|
|
422
499
|
currentSession: input.currentSession ?? null,
|
|
423
500
|
currentObligation: input.currentObligation?.trim() || null,
|
|
@@ -449,6 +526,8 @@ function buildActiveWorkFrame(input) {
|
|
|
449
526
|
taskBoard: input.taskBoard,
|
|
450
527
|
targetCandidates: input.targetCandidates,
|
|
451
528
|
}),
|
|
529
|
+
primaryObligation,
|
|
530
|
+
resumeHandle,
|
|
452
531
|
};
|
|
453
532
|
(0, runtime_1.emitNervesEvent)({
|
|
454
533
|
component: "engine",
|
|
@@ -510,6 +589,9 @@ function formatActiveWorkFrame(frame, options) {
|
|
|
510
589
|
if (nextAction) {
|
|
511
590
|
lines.push(`- next action: ${nextAction}`);
|
|
512
591
|
}
|
|
592
|
+
if (frame.resumeHandle?.lastVerifiedCheckpoint) {
|
|
593
|
+
lines.push(`- last checkpoint: ${frame.resumeHandle.lastVerifiedCheckpoint}`);
|
|
594
|
+
}
|
|
513
595
|
}
|
|
514
596
|
// Inner status block
|
|
515
597
|
const job = frame.inner?.job;
|
|
@@ -634,9 +716,111 @@ function formatLiveWorldStateCheckpoint(frame) {
|
|
|
634
716
|
`- current artifact: ${currentArtifact}`,
|
|
635
717
|
`- next action: ${nextAction}`,
|
|
636
718
|
];
|
|
719
|
+
if (frame.resumeHandle?.lastVerifiedCheckpoint) {
|
|
720
|
+
lines.push(`- last checkpoint: ${frame.resumeHandle.lastVerifiedCheckpoint}`);
|
|
721
|
+
}
|
|
637
722
|
if (otherActiveSessions.length > 0) {
|
|
638
723
|
lines.push("other active sessions:");
|
|
639
724
|
lines.push(...otherActiveSessions);
|
|
640
725
|
}
|
|
641
726
|
return lines.join("\n");
|
|
642
727
|
}
|
|
728
|
+
function snapshotActiveWork(frame) {
|
|
729
|
+
return {
|
|
730
|
+
obligationSnapshots: (frame.pendingObligations ?? []).map((ob) => ({
|
|
731
|
+
id: ob.id,
|
|
732
|
+
status: ob.status,
|
|
733
|
+
artifact: ob.currentArtifact?.trim() || null,
|
|
734
|
+
nextAction: ob.nextAction?.trim() || null,
|
|
735
|
+
})),
|
|
736
|
+
codingSnapshots: (frame.codingSessions ?? []).map((cs) => ({
|
|
737
|
+
id: cs.id,
|
|
738
|
+
status: cs.status,
|
|
739
|
+
artifact: cs.artifactPath?.trim() || null,
|
|
740
|
+
checkpoint: cs.checkpoint?.trim() || null,
|
|
741
|
+
})),
|
|
742
|
+
timestamp: new Date().toISOString(),
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
function detectActiveWorkChanges(previous, current) {
|
|
746
|
+
const changes = [];
|
|
747
|
+
const prevObMap = new Map(previous.obligationSnapshots.map((ob) => [ob.id, ob]));
|
|
748
|
+
const currObMap = new Map(current.obligationSnapshots.map((ob) => [ob.id, ob]));
|
|
749
|
+
// Detect new and changed obligations
|
|
750
|
+
for (const [id, curr] of currObMap) {
|
|
751
|
+
const prev = prevObMap.get(id);
|
|
752
|
+
if (!prev) {
|
|
753
|
+
changes.push({
|
|
754
|
+
kind: "obligation_appeared",
|
|
755
|
+
id,
|
|
756
|
+
from: null,
|
|
757
|
+
to: curr.status,
|
|
758
|
+
summary: `new obligation: ${curr.status}`,
|
|
759
|
+
});
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (prev.status !== curr.status) {
|
|
763
|
+
changes.push({
|
|
764
|
+
kind: "obligation_status_changed",
|
|
765
|
+
id,
|
|
766
|
+
from: prev.status,
|
|
767
|
+
to: curr.status,
|
|
768
|
+
summary: `obligation ${prev.status} -> ${curr.status}`,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
if (prev.artifact !== curr.artifact) {
|
|
772
|
+
changes.push({
|
|
773
|
+
kind: "obligation_artifact_changed",
|
|
774
|
+
id,
|
|
775
|
+
from: prev.artifact,
|
|
776
|
+
to: curr.artifact,
|
|
777
|
+
summary: curr.artifact ? `artifact updated: ${curr.artifact}` : "artifact cleared",
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
const prevCodingMap = new Map(previous.codingSnapshots.map((cs) => [cs.id, cs]));
|
|
782
|
+
const currCodingMap = new Map(current.codingSnapshots.map((cs) => [cs.id, cs]));
|
|
783
|
+
for (const [id, curr] of currCodingMap) {
|
|
784
|
+
const prev = prevCodingMap.get(id);
|
|
785
|
+
if (!prev)
|
|
786
|
+
continue;
|
|
787
|
+
if (prev.status !== curr.status) {
|
|
788
|
+
changes.push({
|
|
789
|
+
kind: "coding_status_changed",
|
|
790
|
+
id,
|
|
791
|
+
from: prev.status,
|
|
792
|
+
to: curr.status,
|
|
793
|
+
summary: `coding ${prev.status} -> ${curr.status}`,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
if (prev.artifact !== curr.artifact) {
|
|
797
|
+
changes.push({
|
|
798
|
+
kind: "coding_artifact_changed",
|
|
799
|
+
id,
|
|
800
|
+
from: prev.artifact,
|
|
801
|
+
to: curr.artifact,
|
|
802
|
+
summary: curr.artifact ? `artifact updated: ${curr.artifact}` : "artifact cleared",
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
if (prev.checkpoint !== curr.checkpoint) {
|
|
806
|
+
changes.push({
|
|
807
|
+
kind: "coding_checkpoint_changed",
|
|
808
|
+
id,
|
|
809
|
+
from: prev.checkpoint,
|
|
810
|
+
to: curr.checkpoint,
|
|
811
|
+
summary: curr.checkpoint ? `checkpoint: ${curr.checkpoint}` : "checkpoint cleared",
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return changes;
|
|
816
|
+
}
|
|
817
|
+
function formatActiveWorkChanges(changes) {
|
|
818
|
+
if (changes.length === 0)
|
|
819
|
+
return "";
|
|
820
|
+
const lines = ["## what changed since last looked"];
|
|
821
|
+
for (const change of changes) {
|
|
822
|
+
lines.push(`- ${change.summary}`);
|
|
823
|
+
}
|
|
824
|
+
return lines.join("\n");
|
|
825
|
+
}
|
|
826
|
+
/* v8 ignore stop */
|
|
@@ -20,9 +20,15 @@ function deriveCommitments(activeWorkFrame, innerJob, pendingObligations) {
|
|
|
20
20
|
const completionCriteria = [];
|
|
21
21
|
const safeToIgnore = [];
|
|
22
22
|
// Persistent obligations from the obligation store
|
|
23
|
+
// Sort by status priority: investigating/waiting/updating before pending
|
|
23
24
|
if (pendingObligations && pendingObligations.length > 0) {
|
|
25
|
+
const sorted = [...pendingObligations].sort((a, b) => {
|
|
26
|
+
const advancedA = a.status !== "pending" && a.status !== "fulfilled" ? 0 : 1;
|
|
27
|
+
const advancedB = b.status !== "pending" && b.status !== "fulfilled" ? 0 : 1;
|
|
28
|
+
return advancedA - advancedB;
|
|
29
|
+
});
|
|
24
30
|
let hasAdvancedObligation = false;
|
|
25
|
-
for (const ob of
|
|
31
|
+
for (const ob of sorted) {
|
|
26
32
|
if (!(0, obligations_1.isOpenObligationStatus)(ob.status))
|
|
27
33
|
continue;
|
|
28
34
|
committedTo.push(describeActiveObligation(ob));
|
package/dist/heart/presence.js
CHANGED
|
@@ -39,7 +39,6 @@ exports.readPresence = readPresence;
|
|
|
39
39
|
exports.readPeerPresence = readPeerPresence;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
-
const os = __importStar(require("os"));
|
|
43
42
|
const runtime_1 = require("../nerves/runtime");
|
|
44
43
|
function presenceDir(agentRoot) {
|
|
45
44
|
return path.join(agentRoot, "state", "presence");
|
|
@@ -145,40 +144,41 @@ function readPresence(agentRoot, _agentName) {
|
|
|
145
144
|
}
|
|
146
145
|
}
|
|
147
146
|
function readPeerPresence(agentRoot) {
|
|
148
|
-
const
|
|
149
|
-
if (!fs.existsSync(
|
|
147
|
+
const friendsDir = path.join(agentRoot, "friends");
|
|
148
|
+
if (!fs.existsSync(friendsDir)) {
|
|
150
149
|
(0, runtime_1.emitNervesEvent)({
|
|
151
150
|
component: "heart",
|
|
152
151
|
event: "heart.peer_presence_read",
|
|
153
|
-
message: "no
|
|
152
|
+
message: "no friends directory, no peers",
|
|
154
153
|
meta: { peerCount: 0 },
|
|
155
154
|
});
|
|
156
155
|
return [];
|
|
157
156
|
}
|
|
158
|
-
const files = fs.readdirSync(
|
|
157
|
+
const files = fs.readdirSync(friendsDir).filter((f) => f.endsWith(".json"));
|
|
159
158
|
const peers = [];
|
|
160
159
|
for (const file of files) {
|
|
161
160
|
try {
|
|
162
|
-
const raw = fs.readFileSync(path.join(
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
161
|
+
const raw = fs.readFileSync(path.join(friendsDir, file), "utf-8");
|
|
162
|
+
const friend = JSON.parse(raw);
|
|
163
|
+
if (friend.kind !== "agent")
|
|
164
|
+
continue;
|
|
165
|
+
peers.push({
|
|
166
|
+
agentName: friend.name ?? path.basename(file, ".json"),
|
|
167
|
+
availability: "idle",
|
|
168
|
+
lane: "idle",
|
|
169
|
+
mission: "",
|
|
170
|
+
tempo: "brief",
|
|
171
|
+
updatedAt: friend.updatedAt ?? new Date().toISOString(),
|
|
172
|
+
});
|
|
173
173
|
}
|
|
174
174
|
catch {
|
|
175
|
-
// Skip
|
|
175
|
+
// Skip malformed or unreadable files
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
(0, runtime_1.emitNervesEvent)({
|
|
179
179
|
component: "heart",
|
|
180
180
|
event: "heart.peer_presence_read",
|
|
181
|
-
message: `read ${peers.length} peer
|
|
181
|
+
message: `read ${peers.length} agent peer records from friends`,
|
|
182
182
|
meta: { peerCount: peers.length },
|
|
183
183
|
});
|
|
184
184
|
return peers;
|
|
@@ -127,6 +127,8 @@ class FileFriendStore {
|
|
|
127
127
|
trustLevel === "stranger"
|
|
128
128
|
? trustLevel
|
|
129
129
|
: DEFAULT_TRUST_LEVEL;
|
|
130
|
+
const kind = raw.kind === "human" || raw.kind === "agent" ? raw.kind : "human";
|
|
131
|
+
const agentMeta = kind === "agent" ? this.normalizeAgentMeta(raw.agentMeta) : undefined;
|
|
130
132
|
return {
|
|
131
133
|
id: raw.id,
|
|
132
134
|
name: raw.name,
|
|
@@ -153,6 +155,21 @@ class FileFriendStore {
|
|
|
153
155
|
createdAt: typeof raw.createdAt === "string" ? raw.createdAt : new Date().toISOString(),
|
|
154
156
|
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : new Date().toISOString(),
|
|
155
157
|
schemaVersion: typeof raw.schemaVersion === "number" ? raw.schemaVersion : 1,
|
|
158
|
+
kind,
|
|
159
|
+
agentMeta,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
normalizeAgentMeta(raw) {
|
|
163
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
164
|
+
return undefined;
|
|
165
|
+
const meta = raw;
|
|
166
|
+
if (typeof meta.bundleName !== "string")
|
|
167
|
+
return undefined;
|
|
168
|
+
return {
|
|
169
|
+
bundleName: meta.bundleName,
|
|
170
|
+
familiarity: typeof meta.familiarity === "number" ? meta.familiarity : 0,
|
|
171
|
+
sharedMissions: Array.isArray(meta.sharedMissions) ? meta.sharedMissions : [],
|
|
172
|
+
outcomes: Array.isArray(meta.outcomes) ? meta.outcomes : [],
|
|
156
173
|
};
|
|
157
174
|
}
|
|
158
175
|
async readJson(filePath) {
|
|
@@ -64,7 +64,6 @@ const cross_chat_delivery_1 = require("../heart/cross-chat-delivery");
|
|
|
64
64
|
const obligations_2 = require("../heart/obligations");
|
|
65
65
|
const episodes_1 = require("../mind/episodes");
|
|
66
66
|
const cares_1 = require("../heart/cares");
|
|
67
|
-
const agent_relationships_1 = require("../heart/agent-relationships");
|
|
68
67
|
const presence_1 = require("../heart/presence");
|
|
69
68
|
const intentions_1 = require("../heart/intentions");
|
|
70
69
|
// Tracks which file paths have been read via read_file in this session.
|
|
@@ -1675,13 +1674,15 @@ exports.baseToolDefinitions = [
|
|
|
1675
1674
|
},
|
|
1676
1675
|
},
|
|
1677
1676
|
},
|
|
1678
|
-
handler: (a) => {
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1677
|
+
handler: async (a, ctx) => {
|
|
1678
|
+
const allFriends = ctx?.friendStore?.listAll ? await ctx.friendStore.listAll() : [];
|
|
1679
|
+
let agents = allFriends.filter((f) => f.kind === "agent");
|
|
1680
|
+
if (a.agentName) {
|
|
1681
|
+
const needle = a.agentName.toLowerCase();
|
|
1682
|
+
agents = agents.filter((f) => f.name?.toLowerCase() === needle);
|
|
1683
|
+
}
|
|
1683
1684
|
(0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.query_relationships", message: `queried relationships`, meta: { agentName: a.agentName ?? "all" } });
|
|
1684
|
-
return JSON.stringify(
|
|
1685
|
+
return JSON.stringify(agents, null, 2);
|
|
1685
1686
|
},
|
|
1686
1687
|
},
|
|
1687
1688
|
{
|
package/package.json
CHANGED
|
@@ -1,161 +0,0 @@
|
|
|
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
|
-
}
|