@ouro.bot/cli 0.1.0-alpha.49 → 0.1.0-alpha.50
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 +7 -0
- package/dist/heart/active-work.js +157 -0
- package/dist/heart/bridges/manager.js +37 -0
- package/dist/heart/bridges/state-machine.js +20 -0
- package/dist/heart/core.js +6 -1
- package/dist/heart/daemon/daemon-cli.js +15 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/session-activity.js +169 -0
- package/dist/mind/context.js +14 -6
- package/dist/mind/pending.js +52 -8
- package/dist/mind/prompt.js +33 -66
- package/dist/repertoire/tools-base.js +89 -10
- package/dist/senses/bluebubbles.js +110 -0
- package/dist/senses/cli.js +4 -2
- package/dist/senses/debug-activity.js +26 -5
- package/dist/senses/inner-dialog.js +86 -2
- package/dist/senses/pipeline.js +104 -6
- package/dist/senses/teams.js +17 -3
- package/package.json +1 -1
|
@@ -57,6 +57,9 @@ const tokens_1 = require("../mind/friends/tokens");
|
|
|
57
57
|
const pipeline_1 = require("./pipeline");
|
|
58
58
|
const nerves_1 = require("../nerves");
|
|
59
59
|
const runtime_1 = require("../nerves/runtime");
|
|
60
|
+
const manager_1 = require("../heart/bridges/manager");
|
|
61
|
+
const session_activity_1 = require("../heart/session-activity");
|
|
62
|
+
const bluebubbles_1 = require("./bluebubbles");
|
|
60
63
|
const DEFAULT_INNER_DIALOG_INSTINCTS = [
|
|
61
64
|
{
|
|
62
65
|
id: "heartbeat_checkin",
|
|
@@ -244,6 +247,83 @@ function writeInnerDialogRuntimeState(sessionFilePath, state) {
|
|
|
244
247
|
});
|
|
245
248
|
}
|
|
246
249
|
}
|
|
250
|
+
function writePendingEnvelope(pendingDir, message) {
|
|
251
|
+
fs.mkdirSync(pendingDir, { recursive: true });
|
|
252
|
+
const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
|
|
253
|
+
const filePath = path.join(pendingDir, fileName);
|
|
254
|
+
fs.writeFileSync(filePath, JSON.stringify(message, null, 2), "utf8");
|
|
255
|
+
}
|
|
256
|
+
function sessionMatchesActivity(activity, session) {
|
|
257
|
+
return activity.friendId === session.friendId
|
|
258
|
+
&& activity.channel === session.channel
|
|
259
|
+
&& activity.key === session.key;
|
|
260
|
+
}
|
|
261
|
+
function resolveBridgePreferredSession(delegatedFrom, sessionActivity) {
|
|
262
|
+
if (!delegatedFrom.bridgeId)
|
|
263
|
+
return null;
|
|
264
|
+
const bridge = (0, manager_1.createBridgeManager)().getBridge(delegatedFrom.bridgeId);
|
|
265
|
+
if (!bridge || bridge.lifecycle === "completed" || bridge.lifecycle === "cancelled") {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return sessionActivity.find((activity) => activity.friendId === delegatedFrom.friendId
|
|
269
|
+
&& activity.channel !== "inner"
|
|
270
|
+
&& bridge.attachedSessions.some((session) => sessionMatchesActivity(activity, session))) ?? null;
|
|
271
|
+
}
|
|
272
|
+
async function tryDeliverDelegatedCompletion(target, outboundEnvelope) {
|
|
273
|
+
if (target.channel !== "bluebubbles") {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
const result = await (0, bluebubbles_1.sendProactiveBlueBubblesMessageToSession)({
|
|
277
|
+
friendId: target.friendId,
|
|
278
|
+
sessionKey: target.key,
|
|
279
|
+
text: outboundEnvelope.content,
|
|
280
|
+
});
|
|
281
|
+
return result.delivered;
|
|
282
|
+
}
|
|
283
|
+
async function routeDelegatedCompletion(agentRoot, agentName, completion, drainedPending, timestamp) {
|
|
284
|
+
const delegated = (drainedPending ?? []).find((message) => message.delegatedFrom);
|
|
285
|
+
if (!delegated?.delegatedFrom || !completion?.answer?.trim()) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const delegatedFrom = delegated.delegatedFrom;
|
|
289
|
+
const outboundEnvelope = {
|
|
290
|
+
from: agentName,
|
|
291
|
+
friendId: delegatedFrom.friendId,
|
|
292
|
+
channel: delegatedFrom.channel,
|
|
293
|
+
key: delegatedFrom.key,
|
|
294
|
+
content: completion.answer.trim(),
|
|
295
|
+
timestamp,
|
|
296
|
+
delegatedFrom,
|
|
297
|
+
};
|
|
298
|
+
const sessionActivity = (0, session_activity_1.listSessionActivity)({
|
|
299
|
+
sessionsDir: path.join(agentRoot, "state", "sessions"),
|
|
300
|
+
friendsDir: path.join(agentRoot, "friends"),
|
|
301
|
+
agentName,
|
|
302
|
+
});
|
|
303
|
+
const bridgeTarget = resolveBridgePreferredSession(delegatedFrom, sessionActivity);
|
|
304
|
+
if (bridgeTarget) {
|
|
305
|
+
if (await tryDeliverDelegatedCompletion(bridgeTarget, outboundEnvelope)) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
writePendingEnvelope((0, pending_1.getPendingDir)(agentName, bridgeTarget.friendId, bridgeTarget.channel, bridgeTarget.key), outboundEnvelope);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const freshest = (0, session_activity_1.findFreshestFriendSession)({
|
|
312
|
+
sessionsDir: path.join(agentRoot, "state", "sessions"),
|
|
313
|
+
friendsDir: path.join(agentRoot, "friends"),
|
|
314
|
+
agentName,
|
|
315
|
+
friendId: delegatedFrom.friendId,
|
|
316
|
+
activeOnly: true,
|
|
317
|
+
});
|
|
318
|
+
if (freshest && freshest.channel !== "inner") {
|
|
319
|
+
if (await tryDeliverDelegatedCompletion(freshest, outboundEnvelope)) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
writePendingEnvelope((0, pending_1.getPendingDir)(agentName, freshest.friendId, freshest.channel, freshest.key), outboundEnvelope);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
writePendingEnvelope((0, pending_1.getDeferredReturnDir)(agentName, delegatedFrom.friendId), outboundEnvelope);
|
|
326
|
+
}
|
|
247
327
|
// Self-referencing friend record for inner dialog (agent talking to itself).
|
|
248
328
|
// No real friend to resolve -- this satisfies the pipeline's friend resolver contract.
|
|
249
329
|
function createSelfFriend(agentName) {
|
|
@@ -274,6 +354,8 @@ async function runInnerDialogTurn(options) {
|
|
|
274
354
|
const now = options?.now ?? (() => new Date());
|
|
275
355
|
const reason = options?.reason ?? "heartbeat";
|
|
276
356
|
const sessionFilePath = innerDialogSessionPath();
|
|
357
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
358
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
277
359
|
writeInnerDialogRuntimeState(sessionFilePath, {
|
|
278
360
|
status: "running",
|
|
279
361
|
reason,
|
|
@@ -316,8 +398,8 @@ async function runInnerDialogTurn(options) {
|
|
|
316
398
|
const userMessage = { role: "user", content: userContent };
|
|
317
399
|
// ── Session loader: wraps existing session logic ──────────────────
|
|
318
400
|
const innerCapabilities = (0, channel_1.getChannelCapabilities)("inner");
|
|
319
|
-
const pendingDir = (0, pending_1.getInnerDialogPendingDir)(
|
|
320
|
-
const selfFriend = createSelfFriend(
|
|
401
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)(agentName);
|
|
402
|
+
const selfFriend = createSelfFriend(agentName);
|
|
321
403
|
const selfContext = { friend: selfFriend, channel: innerCapabilities };
|
|
322
404
|
const sessionLoader = {
|
|
323
405
|
loadOrCreate: async () => {
|
|
@@ -358,6 +440,7 @@ async function runInnerDialogTurn(options) {
|
|
|
358
440
|
skipConfirmation: true,
|
|
359
441
|
},
|
|
360
442
|
});
|
|
443
|
+
await routeDelegatedCompletion(agentRoot, agentName, result.completion, result.drainedPending, now().getTime());
|
|
361
444
|
const resultMessages = result.messages ?? [];
|
|
362
445
|
const assistantPreview = extractAssistantPreview(resultMessages);
|
|
363
446
|
const toolCalls = extractToolCallNames(resultMessages);
|
|
@@ -382,6 +465,7 @@ async function runInnerDialogTurn(options) {
|
|
|
382
465
|
messages: resultMessages,
|
|
383
466
|
usage: result.usage,
|
|
384
467
|
sessionPath: result.sessionPath ?? sessionFilePath,
|
|
468
|
+
completion: result.completion,
|
|
385
469
|
};
|
|
386
470
|
}
|
|
387
471
|
finally {
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -9,6 +9,50 @@ exports.handleInboundTurn = handleInboundTurn;
|
|
|
9
9
|
const runtime_1 = require("../nerves/runtime");
|
|
10
10
|
const continuity_1 = require("./continuity");
|
|
11
11
|
const manager_1 = require("../heart/bridges/manager");
|
|
12
|
+
const identity_1 = require("../heart/identity");
|
|
13
|
+
const tasks_1 = require("../repertoire/tasks");
|
|
14
|
+
const session_activity_1 = require("../heart/session-activity");
|
|
15
|
+
const active_work_1 = require("../heart/active-work");
|
|
16
|
+
const delegation_1 = require("../heart/delegation");
|
|
17
|
+
const thoughts_1 = require("../heart/daemon/thoughts");
|
|
18
|
+
const pending_1 = require("../mind/pending");
|
|
19
|
+
function emptyTaskBoard() {
|
|
20
|
+
return {
|
|
21
|
+
compact: "",
|
|
22
|
+
full: "",
|
|
23
|
+
byStatus: {
|
|
24
|
+
drafting: [],
|
|
25
|
+
processing: [],
|
|
26
|
+
validating: [],
|
|
27
|
+
collaborating: [],
|
|
28
|
+
paused: [],
|
|
29
|
+
blocked: [],
|
|
30
|
+
done: [],
|
|
31
|
+
},
|
|
32
|
+
actionRequired: [],
|
|
33
|
+
unresolvedDependencies: [],
|
|
34
|
+
activeSessions: [],
|
|
35
|
+
activeBridges: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function readInnerWorkState() {
|
|
39
|
+
try {
|
|
40
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
41
|
+
const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
|
|
42
|
+
const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
|
|
43
|
+
const status = (0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir);
|
|
44
|
+
return {
|
|
45
|
+
status: status.processing === "started" ? "running" : "idle",
|
|
46
|
+
hasPending: status.queue !== "clear",
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {
|
|
51
|
+
status: "idle",
|
|
52
|
+
hasPending: false,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
12
56
|
// ── Pipeline ──────────────────────────────────────────────────────
|
|
13
57
|
async function handleInboundTurn(input) {
|
|
14
58
|
// Step 1: Resolve friend
|
|
@@ -57,6 +101,9 @@ async function handleInboundTurn(input) {
|
|
|
57
101
|
const session = await input.sessionLoader.loadOrCreate();
|
|
58
102
|
const sessionMessages = session.messages;
|
|
59
103
|
let mustResolveBeforeHandoff = (0, continuity_1.resolveMustResolveBeforeHandoff)(session.state?.mustResolveBeforeHandoff === true, input.continuityIngressTexts);
|
|
104
|
+
const lastFriendActivityAt = input.channel === "inner"
|
|
105
|
+
? session.state?.lastFriendActivityAt
|
|
106
|
+
: new Date().toISOString();
|
|
60
107
|
const currentObligation = input.continuityIngressTexts
|
|
61
108
|
?.map((text) => text.trim())
|
|
62
109
|
.filter((text) => text.length > 0)
|
|
@@ -73,8 +120,51 @@ async function handleInboundTurn(input) {
|
|
|
73
120
|
key: currentSession.key,
|
|
74
121
|
});
|
|
75
122
|
const bridgeContext = (0, manager_1.formatBridgeContext)(activeBridges) || undefined;
|
|
76
|
-
|
|
77
|
-
|
|
123
|
+
let sessionActivity = [];
|
|
124
|
+
try {
|
|
125
|
+
const agentRoot = (0, identity_1.getAgentRoot)();
|
|
126
|
+
sessionActivity = (0, session_activity_1.listSessionActivity)({
|
|
127
|
+
sessionsDir: `${agentRoot}/state/sessions`,
|
|
128
|
+
friendsDir: `${agentRoot}/friends`,
|
|
129
|
+
agentName: (0, identity_1.getAgentName)(),
|
|
130
|
+
currentSession: {
|
|
131
|
+
friendId: currentSession.friendId,
|
|
132
|
+
channel: currentSession.channel,
|
|
133
|
+
key: currentSession.key,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
sessionActivity = [];
|
|
139
|
+
}
|
|
140
|
+
const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
|
|
141
|
+
currentSession,
|
|
142
|
+
currentObligation,
|
|
143
|
+
mustResolveBeforeHandoff,
|
|
144
|
+
inner: readInnerWorkState(),
|
|
145
|
+
bridges: activeBridges,
|
|
146
|
+
taskBoard: (() => {
|
|
147
|
+
try {
|
|
148
|
+
return (0, tasks_1.getTaskModule)().getBoard();
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return emptyTaskBoard();
|
|
152
|
+
}
|
|
153
|
+
})(),
|
|
154
|
+
friendActivity: sessionActivity,
|
|
155
|
+
});
|
|
156
|
+
const delegationDecision = (0, delegation_1.decideDelegation)({
|
|
157
|
+
channel: input.channel,
|
|
158
|
+
ingressTexts: input.continuityIngressTexts ?? [],
|
|
159
|
+
activeWork: activeWorkFrame,
|
|
160
|
+
mustResolveBeforeHandoff,
|
|
161
|
+
});
|
|
162
|
+
// Step 4: Drain deferred friend returns, then ordinary per-session pending.
|
|
163
|
+
const deferredReturns = input.channel === "inner"
|
|
164
|
+
? []
|
|
165
|
+
: (input.drainDeferredReturns?.(resolvedContext.friend.id) ?? []);
|
|
166
|
+
const sessionPending = input.drainPending(input.pendingDir);
|
|
167
|
+
const pending = [...deferredReturns, ...sessionPending];
|
|
78
168
|
// Assemble messages: session messages + pending (formatted) + inbound user messages
|
|
79
169
|
if (pending.length > 0) {
|
|
80
170
|
// Format pending messages and prepend to the user content
|
|
@@ -112,6 +202,8 @@ async function handleInboundTurn(input) {
|
|
|
112
202
|
const runAgentOptions = {
|
|
113
203
|
...input.runAgentOptions,
|
|
114
204
|
bridgeContext,
|
|
205
|
+
activeWorkFrame,
|
|
206
|
+
delegationDecision,
|
|
115
207
|
currentSessionKey: currentSession.key,
|
|
116
208
|
currentObligation,
|
|
117
209
|
mustResolveBeforeHandoff,
|
|
@@ -130,11 +222,15 @@ async function handleInboundTurn(input) {
|
|
|
130
222
|
};
|
|
131
223
|
const result = await input.runAgent(sessionMessages, input.callbacks, input.channel, input.signal, runAgentOptions);
|
|
132
224
|
// Step 6: postTurn
|
|
225
|
+
const continuingState = {
|
|
226
|
+
...(mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
|
|
227
|
+
...(typeof lastFriendActivityAt === "string" ? { lastFriendActivityAt } : {}),
|
|
228
|
+
};
|
|
133
229
|
const nextState = result.outcome === "complete" || result.outcome === "blocked" || result.outcome === "superseded"
|
|
134
|
-
?
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
230
|
+
? (typeof lastFriendActivityAt === "string"
|
|
231
|
+
? { lastFriendActivityAt }
|
|
232
|
+
: undefined)
|
|
233
|
+
: (Object.keys(continuingState).length > 0 ? continuingState : undefined);
|
|
138
234
|
input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
|
|
139
235
|
// Step 7: Token accumulation
|
|
140
236
|
await input.accumulateFriendTokens(input.friendStore, resolvedContext.friend.id, result.usage);
|
|
@@ -152,7 +248,9 @@ async function handleInboundTurn(input) {
|
|
|
152
248
|
gateResult,
|
|
153
249
|
usage: result.usage,
|
|
154
250
|
turnOutcome: result.outcome,
|
|
251
|
+
completion: result.completion,
|
|
155
252
|
sessionPath: session.sessionPath,
|
|
156
253
|
messages: sessionMessages,
|
|
254
|
+
drainedPending: pending,
|
|
157
255
|
};
|
|
158
256
|
}
|
package/dist/senses/teams.js
CHANGED
|
@@ -63,6 +63,7 @@ const resolver_1 = require("../mind/friends/resolver");
|
|
|
63
63
|
const tokens_1 = require("../mind/friends/tokens");
|
|
64
64
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
65
65
|
const identity_1 = require("../heart/identity");
|
|
66
|
+
const progress_story_1 = require("../heart/progress-story");
|
|
66
67
|
const http = __importStar(require("http"));
|
|
67
68
|
const path = __importStar(require("path"));
|
|
68
69
|
const trust_gate_1 = require("./trust-gate");
|
|
@@ -334,13 +335,21 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
334
335
|
if (!streamHasContent)
|
|
335
336
|
safeEmit("⏳");
|
|
336
337
|
const argSummary = (0, tools_1.summarizeArgs)(name, args) || Object.keys(args).join(", ");
|
|
337
|
-
safeUpdate(
|
|
338
|
+
safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
339
|
+
scope: "shared-work",
|
|
340
|
+
phase: "processing",
|
|
341
|
+
objective: `running ${name} (${argSummary})...`,
|
|
342
|
+
})));
|
|
338
343
|
hadToolRun = true;
|
|
339
344
|
},
|
|
340
345
|
onToolEnd: (name, summary, success) => {
|
|
341
346
|
stopPhraseRotation();
|
|
342
347
|
const msg = (0, format_1.formatToolResult)(name, summary, success);
|
|
343
|
-
safeUpdate(
|
|
348
|
+
safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
349
|
+
scope: "shared-work",
|
|
350
|
+
phase: "processing",
|
|
351
|
+
objective: msg,
|
|
352
|
+
})));
|
|
344
353
|
},
|
|
345
354
|
onKick: () => {
|
|
346
355
|
stopPhraseRotation();
|
|
@@ -351,7 +360,11 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
351
360
|
stopPhraseRotation();
|
|
352
361
|
if (stopped)
|
|
353
362
|
return;
|
|
354
|
-
const msg = (0,
|
|
363
|
+
const msg = (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
364
|
+
scope: "shared-work",
|
|
365
|
+
phase: "errored",
|
|
366
|
+
outcomeText: (0, format_1.formatError)(error),
|
|
367
|
+
}));
|
|
355
368
|
if (severity === "transient") {
|
|
356
369
|
safeUpdate(msg);
|
|
357
370
|
}
|
|
@@ -560,6 +573,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
560
573
|
hasExistingGroupWithFamily: false,
|
|
561
574
|
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
562
575
|
drainPending: pending_1.drainPending,
|
|
576
|
+
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)((0, identity_1.getAgentName)(), deferredFriendId),
|
|
563
577
|
runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
|
|
564
578
|
...opts,
|
|
565
579
|
toolContext: {
|