@ouro.bot/cli 0.1.0-alpha.190 → 0.1.0-alpha.192
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
CHANGED
|
@@ -2,105 +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.192",
|
|
6
6
|
"changes": [
|
|
7
|
-
"
|
|
8
|
-
"feat(
|
|
9
|
-
"feat(coding): verification truthfulness"
|
|
7
|
+
"refactor: canonical obligations — ActiveWorkFrame as single source of truth for prompt sections",
|
|
8
|
+
"feat(mcp): dynamic server add/remove \u2014 agents can manage MCP servers without daemon restart"
|
|
10
9
|
]
|
|
11
10
|
},
|
|
12
11
|
{
|
|
13
|
-
"version": "0.1.0-alpha.
|
|
14
|
-
"changes": [
|
|
15
|
-
"feat(heart): ResumeHandle type, primary obligation selection, status priority",
|
|
16
|
-
"feat(heart): lane/obligation precedence rules",
|
|
17
|
-
"feat(heart): cross-session change detection (snapshot, diff, format)",
|
|
18
|
-
"feat(cli): TUI edge-case hardening (44 new tests)",
|
|
19
|
-
"feat(mind): memory judgement heuristics in system prompt",
|
|
20
|
-
"feat(mind): memory feedback loop (noise prevention)",
|
|
21
|
-
"fix(cli): blockquote wrap width for narrow terminals"
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
"version": "0.1.0-alpha.187",
|
|
26
|
-
"changes": [
|
|
27
|
-
"refactor: collapse AgentRelationship into FriendRecord \u2014 one identity system for humans and agents",
|
|
28
|
-
"refactor: rename AdoptionSpecialist to SerpentGuide",
|
|
29
|
-
"feat(hatch): detect API keys from environment variables (ANTHROPIC_API_KEY, OPENAI_API_KEY, AZURE_OPENAI_API_KEY, MINIMAX_API_KEY, GITHUB_TOKEN)",
|
|
30
|
-
"fix(cli): React unmount warning suppression covers SerpentGuide exit path"
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
"version": "0.1.0-alpha.185",
|
|
35
|
-
"changes": [
|
|
36
|
-
"fix(hatch): pass model name to adoption specialist from discovered credentials",
|
|
37
|
-
"feat(continuity): wake packets, episodes, cares, intentions, peer presence, and temporal self",
|
|
38
|
-
"fix(continuity): deduplicate obligations in prompt, keep session summary, pass preloaded data",
|
|
39
|
-
"feat(continuity): 9 new modules \u2014 episodes, cares, agent-relationships, intentions, presence, tempo, temporal-view, wake-packet, obligation enrichment",
|
|
40
|
-
"feat(continuity): 8 new tools \u2014 query_episodes, capture_episode, query_presence, query_cares, care_manage, query_relationships, intention_capture, intention_manage",
|
|
41
|
-
"feat(continuity): 3-layer architecture \u2014 durable state, derived views, surfaces",
|
|
42
|
-
"feat(pipeline): continuity threading \u2014 tempo derivation, wake packet assembly, presence update, episode emission on every turn",
|
|
43
|
-
"feat(prompt): wake packet prompt section with token budget and truncation priority",
|
|
44
|
-
"feat(coding): coding context receives compact wake packet, coding completion emits episodes",
|
|
45
|
-
"feat(outlook): expose continuity data (presence, cares, episodes) to outlook surfaces"
|
|
46
|
-
]
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"version": "0.1.0-alpha.183",
|
|
50
|
-
"changes": [
|
|
51
|
-
"fix(cli): status/hints moved BELOW input box (between bottom separator and end)",
|
|
52
|
-
"fix(cli): Ctrl-C double-press requires 2s window (not any two presses ever)",
|
|
53
|
-
"feat(cli): /help command with all keyboard shortcuts",
|
|
54
|
-
"docs: CLI keyboard shortcuts documentation"
|
|
55
|
-
]
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
"version": "0.1.0-alpha.182",
|
|
59
|
-
"changes": [
|
|
60
|
-
"fix(cli): remove persistent status bar from input box (header already shows info)",
|
|
61
|
-
"fix(cli): Meta+B/F word movement (Option+Arrow on macOS)",
|
|
62
|
-
"fix(cli): Meta+D delete word forward"
|
|
63
|
-
]
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"version": "0.1.0-alpha.181",
|
|
67
|
-
"changes": [
|
|
68
|
-
"feat(cli): Option+Arrow word jumping, Ctrl+A/E home/end, cursor positioning",
|
|
69
|
-
"feat(cli): never suppress input \u2014 type while agent responds (TTFA)",
|
|
70
|
-
"fix(cli): blockquote continuation lines keep \u2502 prefix on wrap",
|
|
71
|
-
"feat(cli): Option+Backspace to delete word"
|
|
72
|
-
]
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
"version": "0.1.0-alpha.180",
|
|
76
|
-
"changes": [
|
|
77
|
-
"fix(cli): suppress React unmount warning on TUI exit"
|
|
78
|
-
]
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"version": "0.1.0-alpha.179",
|
|
82
|
-
"changes": [
|
|
83
|
-
"feat(cli): generous vertical whitespace between TUI sections",
|
|
84
|
-
"feat(cli): image placeholder rendering (\ud83d\uddbc) for markdown images",
|
|
85
|
-
"feat(cli): Alt+Enter multi-line input with continuation prefix",
|
|
86
|
-
"feat(cli): terminal width safety (cap at 200, subtract margin)",
|
|
87
|
-
"fix(cli): settle/ponder/observe/rest fully hidden from imperative tool display"
|
|
88
|
-
]
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
"version": "0.1.0-alpha.178",
|
|
12
|
+
"version": "0.1.0-alpha.177",
|
|
92
13
|
"changes": [
|
|
93
|
-
"
|
|
14
|
+
"feat(mcp): dynamic server add/remove without restart"
|
|
94
15
|
]
|
|
95
16
|
},
|
|
96
17
|
{
|
|
97
|
-
"version": "0.1.0-alpha.
|
|
18
|
+
"version": "0.1.0-alpha.176",
|
|
98
19
|
"changes": [
|
|
99
|
-
"
|
|
20
|
+
"feat(mcp): dynamic MCP server add/remove \u2014 agents can add/remove MCP servers in agent.json without daemon restart"
|
|
100
21
|
]
|
|
101
22
|
},
|
|
102
23
|
{
|
|
103
|
-
"version": "0.1.0-alpha.
|
|
24
|
+
"version": "0.1.0-alpha.175",
|
|
104
25
|
"changes": [
|
|
105
26
|
"fix(daemon): launchd KeepAlive for crash recovery \u2014 auto-restarts on crash",
|
|
106
27
|
"fix(daemon): orphan killer excludes MCP server processes",
|
|
@@ -666,8 +666,8 @@ function formatActiveWorkFrame(frame, options) {
|
|
|
666
666
|
}
|
|
667
667
|
if ((frame.pendingObligations ?? []).length > 0) {
|
|
668
668
|
const openCount = frame.pendingObligations.filter((ob) => (0, obligations_1.isOpenObligationStatus)(ob.status)).length;
|
|
669
|
-
if (options?.
|
|
670
|
-
//
|
|
669
|
+
if (options?.obligationDetailsRenderedElsewhere && openCount > 0) {
|
|
670
|
+
// Enriched obligation data (with resumeHint, stalenessClass, waitingOn) is rendered elsewhere.
|
|
671
671
|
// Avoid duplicating the full list — just anchor the count here.
|
|
672
672
|
lines.push("");
|
|
673
673
|
lines.push(`return obligations: ${openCount} active (canonical details in **Owed** section of wake packet)`);
|
|
@@ -65,10 +65,11 @@ function buildPresenceSection(peers) {
|
|
|
65
65
|
.map((p) => `- ${p.agentName}: ${p.availability}/${p.lane}`)
|
|
66
66
|
.join("\n");
|
|
67
67
|
}
|
|
68
|
-
function buildResumeHint(view) {
|
|
68
|
+
function buildResumeHint(view, obligations) {
|
|
69
69
|
// Compose from authored obligation resumeHints and top intentions
|
|
70
70
|
const hints = [];
|
|
71
|
-
|
|
71
|
+
const effectiveObligations = obligations ?? view.activeObligations;
|
|
72
|
+
for (const ob of effectiveObligations) {
|
|
72
73
|
if (ob.meaning?.resumeHint) {
|
|
73
74
|
hints.push(ob.meaning.resumeHint);
|
|
74
75
|
}
|
|
@@ -78,23 +79,24 @@ function buildResumeHint(view) {
|
|
|
78
79
|
}
|
|
79
80
|
if (hints.length === 0) {
|
|
80
81
|
// Fall back to top obligation content
|
|
81
|
-
if (
|
|
82
|
-
hints.push(
|
|
82
|
+
if (effectiveObligations.length > 0) {
|
|
83
|
+
hints.push(effectiveObligations[0].content);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
if (hints.length === 0)
|
|
86
87
|
return "";
|
|
87
88
|
return hints.join("; ");
|
|
88
89
|
}
|
|
89
|
-
function buildWakePacket(view) {
|
|
90
|
+
function buildWakePacket(view, opts) {
|
|
90
91
|
const tempo = view.tempo;
|
|
91
92
|
const tokenBudget = tempo_1.TEMPO_BUDGETS[tempo];
|
|
93
|
+
const effectiveObligations = opts?.canonicalObligations ? opts.canonicalObligations.all : view.activeObligations;
|
|
92
94
|
const packet = {
|
|
93
95
|
plotLine: buildPlotLine(view.recentEpisodes, tempo),
|
|
94
|
-
obligations: buildObligationsSection(
|
|
96
|
+
obligations: buildObligationsSection(effectiveObligations),
|
|
95
97
|
cares: buildCaresSection(view.activeCares),
|
|
96
98
|
presence: buildPresenceSection(view.peerPresence),
|
|
97
|
-
resumeHint: buildResumeHint(view),
|
|
99
|
+
resumeHint: buildResumeHint(view, opts?.canonicalObligations ? effectiveObligations : undefined),
|
|
98
100
|
tempo,
|
|
99
101
|
tokenBudget,
|
|
100
102
|
assembledAt: new Date().toISOString(),
|
package/dist/mind/prompt.js
CHANGED
|
@@ -106,7 +106,10 @@ function resetPsycheCache() {
|
|
|
106
106
|
}
|
|
107
107
|
const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
108
108
|
function buildSessionSummary(options) {
|
|
109
|
-
const { sessionsDir, friendsDir, agentName,
|
|
109
|
+
const { sessionsDir, friendsDir, agentName, currentSession, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, } = options;
|
|
110
|
+
const currentFriendId = currentSession?.friendId ?? options.currentFriendId;
|
|
111
|
+
const currentChannel = currentSession?.channel ?? options.currentChannel;
|
|
112
|
+
const currentKey = currentSession?.key ?? options.currentKey;
|
|
110
113
|
const now = Date.now();
|
|
111
114
|
const query = {
|
|
112
115
|
sessionsDir,
|
|
@@ -519,7 +522,7 @@ function wakePacketSection(options) {
|
|
|
519
522
|
function activeWorkSection(options) {
|
|
520
523
|
if (!options?.activeWorkFrame)
|
|
521
524
|
return "";
|
|
522
|
-
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, {
|
|
525
|
+
return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame, { obligationDetailsRenderedElsewhere: !!options?.wakePacket });
|
|
523
526
|
}
|
|
524
527
|
function liveWorldStateSection(options) {
|
|
525
528
|
if (!options?.activeWorkFrame)
|
|
@@ -1079,6 +1082,9 @@ async function buildSystem(channel = "cli", options, context) {
|
|
|
1079
1082
|
sessionsDir: path.join((0, identity_1.getAgentRoot)(), "state", "sessions"),
|
|
1080
1083
|
friendsDir: path.join((0, identity_1.getAgentRoot)(), "friends"),
|
|
1081
1084
|
agentName: (0, identity_1.getAgentName)(),
|
|
1085
|
+
currentSession: options?.activeWorkFrame?.currentSession
|
|
1086
|
+
? { friendId: options.activeWorkFrame.currentSession.friendId, channel: options.activeWorkFrame.currentSession.channel, key: options.activeWorkFrame.currentSession.key }
|
|
1087
|
+
: undefined,
|
|
1082
1088
|
currentFriendId: context?.friend?.id,
|
|
1083
1089
|
currentChannel: channel,
|
|
1084
1090
|
currentKey: options?.currentSessionKey ?? "session",
|
|
@@ -41,6 +41,53 @@ class McpManager {
|
|
|
41
41
|
}
|
|
42
42
|
return entry.client.callTool(tool, args);
|
|
43
43
|
}
|
|
44
|
+
/* v8 ignore start — reconcile: dynamic MCP server management, tested via integration @preserve */
|
|
45
|
+
/** Re-read agent config and connect new servers / disconnect removed ones. */
|
|
46
|
+
async reconcile() {
|
|
47
|
+
try {
|
|
48
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
49
|
+
const servers = config.mcpServers ?? {};
|
|
50
|
+
const currentNames = new Set(this.servers.keys());
|
|
51
|
+
const desiredNames = new Set(Object.keys(servers));
|
|
52
|
+
// Connect new servers
|
|
53
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
54
|
+
if (!currentNames.has(name)) {
|
|
55
|
+
(0, runtime_1.emitNervesEvent)({
|
|
56
|
+
event: "mcp.server_added",
|
|
57
|
+
component: "repertoire",
|
|
58
|
+
message: `connecting new MCP server: ${name}`,
|
|
59
|
+
meta: { server: name, command: cfg.command },
|
|
60
|
+
});
|
|
61
|
+
await this.connectServer(name, cfg);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Disconnect removed servers
|
|
65
|
+
for (const name of currentNames) {
|
|
66
|
+
if (!desiredNames.has(name)) {
|
|
67
|
+
(0, runtime_1.emitNervesEvent)({
|
|
68
|
+
event: "mcp.server_removed",
|
|
69
|
+
component: "repertoire",
|
|
70
|
+
message: `disconnecting removed MCP server: ${name}`,
|
|
71
|
+
meta: { server: name },
|
|
72
|
+
});
|
|
73
|
+
const entry = this.servers.get(name);
|
|
74
|
+
if (entry)
|
|
75
|
+
entry.client.shutdown();
|
|
76
|
+
this.servers.delete(name);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
(0, runtime_1.emitNervesEvent)({
|
|
82
|
+
level: "warn",
|
|
83
|
+
event: "mcp.reconcile_error",
|
|
84
|
+
component: "repertoire",
|
|
85
|
+
message: "failed to reconcile MCP servers",
|
|
86
|
+
meta: { reason: error instanceof Error ? error.message : String(error) },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/* v8 ignore stop */
|
|
44
91
|
shutdown() {
|
|
45
92
|
this.shuttingDown = true;
|
|
46
93
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -148,11 +195,17 @@ let _sharedManagerPromise = null;
|
|
|
148
195
|
* Safe to call from multiple senses — will only create one instance.
|
|
149
196
|
*/
|
|
150
197
|
async function getSharedMcpManager() {
|
|
151
|
-
|
|
198
|
+
// If manager exists, reconcile to pick up config changes (new/removed servers)
|
|
199
|
+
/* v8 ignore start — reconcile on existing manager @preserve */
|
|
200
|
+
if (_sharedManager) {
|
|
201
|
+
await _sharedManager.reconcile();
|
|
152
202
|
return _sharedManager;
|
|
203
|
+
}
|
|
204
|
+
/* v8 ignore stop */
|
|
153
205
|
/* v8 ignore next -- race guard: deduplicates concurrent initialization calls @preserve */
|
|
154
206
|
if (_sharedManagerPromise)
|
|
155
207
|
return _sharedManagerPromise;
|
|
208
|
+
// Always re-check config — agent may have added servers since last call
|
|
156
209
|
_sharedManagerPromise = (async () => {
|
|
157
210
|
try {
|
|
158
211
|
const config = (0, identity_1.loadAgentConfig)();
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -485,7 +485,12 @@ async function handleInboundTurn(input) {
|
|
|
485
485
|
activeCares,
|
|
486
486
|
},
|
|
487
487
|
});
|
|
488
|
-
const wakePacket = (0, wake_packet_1.buildWakePacket)(temporalView
|
|
488
|
+
const wakePacket = (0, wake_packet_1.buildWakePacket)(temporalView, {
|
|
489
|
+
canonicalObligations: {
|
|
490
|
+
primary: activeWorkFrame.primaryObligation,
|
|
491
|
+
all: activeWorkFrame.pendingObligations,
|
|
492
|
+
},
|
|
493
|
+
});
|
|
489
494
|
renderedWakePacket = (0, wake_packet_1.renderWakePacket)(wakePacket);
|
|
490
495
|
if (!renderedWakePacket)
|
|
491
496
|
renderedWakePacket = undefined;
|