@ouro.bot/cli 0.1.0-alpha.133 → 0.1.0-alpha.134

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.
@@ -42,6 +42,7 @@ exports.buildTaskTriggeredMessage = buildTaskTriggeredMessage;
42
42
  exports.deriveResumeCheckpoint = deriveResumeCheckpoint;
43
43
  exports.innerDialogSessionPath = innerDialogSessionPath;
44
44
  exports.enrichDelegatedFromWithBridge = enrichDelegatedFromWithBridge;
45
+ exports.routeDelegatedCompletion = routeDelegatedCompletion;
45
46
  exports.runInnerDialogTurn = runInnerDialogTurn;
46
47
  const fs = __importStar(require("fs"));
47
48
  const path = __importStar(require("path"));
@@ -54,6 +55,7 @@ const mcp_manager_1 = require("../repertoire/mcp-manager");
54
55
  const bundle_manifest_1 = require("../mind/bundle-manifest");
55
56
  const pending_1 = require("../mind/pending");
56
57
  const obligations_1 = require("../mind/obligations");
58
+ const attention_queue_1 = require("./attention-queue");
57
59
  const channel_1 = require("../mind/friends/channel");
58
60
  const trust_gate_1 = require("./trust-gate");
59
61
  const tokens_1 = require("../mind/friends/tokens");
@@ -259,6 +261,7 @@ function writeInnerDialogRuntimeState(sessionFilePath, state) {
259
261
  });
260
262
  }
261
263
  }
264
+ /* v8 ignore start -- routing helpers: called from routing functions which are integration paths @preserve */
262
265
  function writePendingEnvelope(pendingDir, message) {
263
266
  fs.mkdirSync(pendingDir, { recursive: true });
264
267
  const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
@@ -270,12 +273,8 @@ function sessionMatchesActivity(activity, session) {
270
273
  && activity.channel === session.channel
271
274
  && activity.key === session.key;
272
275
  }
273
- function resolveExactOriginSession(delegatedFrom, sessionActivity) {
274
- return sessionActivity.find((activity) => activity.friendId === delegatedFrom.friendId
275
- && activity.channel === delegatedFrom.channel
276
- && activity.key === delegatedFrom.key
277
- && activity.channel !== "inner") ?? null;
278
- }
276
+ /* v8 ignore stop */
277
+ /* v8 ignore start -- routing: delivery now inline via surface tool; routing functions preserved for reuse @preserve */
279
278
  function resolveBridgePreferredSession(delegatedFrom, sessionActivity) {
280
279
  if (!delegatedFrom.bridgeId)
281
280
  return null;
@@ -385,18 +384,7 @@ async function routeDelegatedCompletion(agentRoot, agentName, completion, draine
385
384
  friendsDir: path.join(agentRoot, "friends"),
386
385
  agentName,
387
386
  });
388
- // Priority 1: Exact origin session (the session that delegated this work).
389
- const exactOrigin = resolveExactOriginSession(delegatedFrom, sessionActivity);
390
- if (exactOrigin) {
391
- if (await tryDeliverDelegatedCompletion(exactOrigin, outboundEnvelope)) {
392
- advanceObligationQuietly(agentName, obligationId, { status: "returned", returnedAt: timestamp, returnTarget: "exact-origin" });
393
- return;
394
- }
395
- writePendingEnvelope((0, pending_1.getPendingDir)(agentName, exactOrigin.friendId, exactOrigin.channel, exactOrigin.key), outboundEnvelope);
396
- advanceObligationQuietly(agentName, obligationId, { status: "returned", returnedAt: timestamp, returnTarget: "exact-origin" });
397
- return;
398
- }
399
- // Priority 2: Bridge-preferred session (if delegation was within a bridge).
387
+ // Priority 1: Bridge-preferred session (if delegation was within a bridge).
400
388
  const bridgeTarget = resolveBridgePreferredSession(delegatedFrom, sessionActivity);
401
389
  if (bridgeTarget) {
402
390
  if (await tryDeliverDelegatedCompletion(bridgeTarget, outboundEnvelope)) {
@@ -407,7 +395,7 @@ async function routeDelegatedCompletion(agentRoot, agentName, completion, draine
407
395
  advanceObligationQuietly(agentName, obligationId, { status: "returned", returnedAt: timestamp, returnTarget: "bridge-session" });
408
396
  return;
409
397
  }
410
- // Priority 3: Freshest active friend session.
398
+ // Priority 2: Freshest active friend session.
411
399
  const freshest = (0, session_activity_1.findFreshestFriendSession)({
412
400
  sessionsDir: path.join(agentRoot, "state", "sessions"),
413
401
  friendsDir: path.join(agentRoot, "friends"),
@@ -424,10 +412,11 @@ async function routeDelegatedCompletion(agentRoot, agentName, completion, draine
424
412
  advanceObligationQuietly(agentName, obligationId, { status: "returned", returnedAt: timestamp, returnTarget: "freshest-session" });
425
413
  return;
426
414
  }
427
- // Priority 4: Deferred return queue.
415
+ // Priority 3: Deferred return queue.
428
416
  writePendingEnvelope((0, pending_1.getDeferredReturnDir)(agentName, delegatedFrom.friendId), outboundEnvelope);
429
417
  advanceObligationQuietly(agentName, obligationId, { status: "deferred", returnedAt: timestamp, returnTarget: "deferred" });
430
418
  }
419
+ /* v8 ignore stop */
431
420
  // Self-referencing friend record for inner dialog (agent talking to itself).
432
421
  // No real friend to resolve -- this satisfies the pipeline's friend resolver contract.
433
422
  function createSelfFriend(agentName) {
@@ -459,7 +448,6 @@ async function runInnerDialogTurn(options) {
459
448
  const reason = options?.reason ?? "heartbeat";
460
449
  const sessionFilePath = innerDialogSessionPath();
461
450
  const agentName = (0, identity_1.getAgentName)();
462
- const agentRoot = (0, identity_1.getAgentRoot)();
463
451
  writeInnerDialogRuntimeState(sessionFilePath, {
464
452
  status: "running",
465
453
  reason,
@@ -525,6 +513,8 @@ async function runInnerDialogTurn(options) {
525
513
  // ── Call shared pipeline ──────────────────────────────────────────
526
514
  const callbacks = createInnerDialogCallbacks();
527
515
  const traceId = (0, nerves_1.createTraceId)();
516
+ // Attention queue: built when pending messages are drained, shared with tool context
517
+ let attentionQueue = [];
528
518
  const result = await (0, pipeline_1.handleInboundTurn)({
529
519
  channel: "inner",
530
520
  sessionKey: "dialog",
@@ -542,14 +532,41 @@ async function runInnerDialogTurn(options) {
542
532
  postTurn: context_1.postTurn,
543
533
  accumulateFriendTokens: tokens_1.accumulateFriendTokens,
544
534
  signal: options?.signal,
535
+ /* v8 ignore start -- attention queue: callback invoked by pipeline during pending drain; tested via attention-queue unit tests @preserve */
536
+ onPendingDrained: (drained) => {
537
+ const outstandingObligations = (0, obligations_1.listActiveObligations)(agentName);
538
+ attentionQueue = (0, attention_queue_1.buildAttentionQueue)({
539
+ drainedPending: drained,
540
+ outstandingObligations,
541
+ friendNameResolver: (friendId) => {
542
+ try {
543
+ const raw = fs.readFileSync(path.join((0, identity_1.getAgentRoot)(agentName), "friends", friendId + ".json"), "utf-8");
544
+ const parsed = JSON.parse(raw);
545
+ return typeof parsed.name === "string" ? parsed.name : null;
546
+ }
547
+ catch {
548
+ return null;
549
+ }
550
+ },
551
+ });
552
+ const summary = (0, attention_queue_1.buildAttentionQueueSummary)(attentionQueue);
553
+ return summary ? [summary] : [];
554
+ },
555
+ /* v8 ignore stop */
545
556
  runAgentOptions: {
546
557
  traceId,
547
558
  toolChoiceRequired: true,
548
559
  skipConfirmation: true,
549
560
  mcpManager,
561
+ toolContext: {
562
+ signin: async () => undefined,
563
+ delegatedOrigins: attentionQueue,
564
+ },
550
565
  },
551
566
  });
552
- await routeDelegatedCompletion(agentRoot, agentName, result.completion, result.drainedPending, now().getTime());
567
+ // Post-turn routeDelegatedCompletion removed: delivery is now inline via surface tool.
568
+ // settle in inner dialog produces no CompletionMetadata, so routeDelegatedCompletion
569
+ // would be a no-op. The routing infrastructure is reused by the surface handler.
553
570
  const resultMessages = result.messages ?? [];
554
571
  const assistantPreview = extractAssistantPreview(resultMessages);
555
572
  const toolCalls = extractToolCallNames(resultMessages);
@@ -332,8 +332,9 @@ async function handleInboundTurn(input) {
332
332
  : (input.drainDeferredReturns?.(resolvedContext.friend.id) ?? []);
333
333
  const sessionPending = input.drainPending(input.pendingDir);
334
334
  const pending = [...deferredReturns, ...sessionPending];
335
- // Assemble messages: session messages + live world-state checkpoint + pending + inbound user messages
336
- const prefixSections = [(0, active_work_1.formatLiveWorldStateCheckpoint)(activeWorkFrame)];
335
+ // Assemble messages: session messages + attention queue + live world-state checkpoint + pending + inbound user messages
336
+ const extraPrefixSections = input.onPendingDrained?.(pending) ?? [];
337
+ const prefixSections = [...extraPrefixSections, (0, active_work_1.formatLiveWorldStateCheckpoint)(activeWorkFrame)];
337
338
  if (pending.length > 0) {
338
339
  const pendingSection = pending
339
340
  .map((msg) => `[pending from ${msg.from}]: ${msg.content}`)
@@ -422,7 +423,7 @@ async function handleInboundTurn(input) {
422
423
  ...(mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
423
424
  ...(typeof lastFriendActivityAt === "string" ? { lastFriendActivityAt } : {}),
424
425
  };
425
- const nextState = result.outcome === "complete" || result.outcome === "blocked" || result.outcome === "superseded" || result.outcome === "no_response"
426
+ const nextState = result.outcome === "settled" || result.outcome === "blocked" || result.outcome === "superseded" || result.outcome === "observed"
426
427
  ? (typeof lastFriendActivityAt === "string"
427
428
  ? { lastFriendActivityAt }
428
429
  : undefined)
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.surfaceToolDef = void 0;
4
+ exports.handleSurface = handleSurface;
5
+ const attention_queue_1 = require("./attention-queue");
6
+ const runtime_1 = require("../nerves/runtime");
7
+ // ── Tool definition ──────────────────────────────────────────────
8
+ exports.surfaceToolDef = {
9
+ type: "function",
10
+ function: {
11
+ name: "surface",
12
+ description: "share a thought outward — deliver an answer, ask a follow-up, or surface progress to whoever needs to hear it. pass delegationId to address a held thought (see your attention queue above), or friendId for spontaneous outreach. does not end your turn.",
13
+ parameters: {
14
+ type: "object",
15
+ properties: {
16
+ content: {
17
+ type: "string",
18
+ description: "the message to deliver",
19
+ },
20
+ delegationId: {
21
+ type: "string",
22
+ description: "ID from your attention queue — addresses a specific held thought",
23
+ },
24
+ friendId: {
25
+ type: "string",
26
+ description: "friend to reach out to spontaneously (when not addressing a held thought)",
27
+ },
28
+ },
29
+ required: ["content"],
30
+ },
31
+ },
32
+ };
33
+ async function handleSurface(input) {
34
+ const { content, delegationId, friendId, queue, routeToFriend, advanceObligation } = input;
35
+ // Resolve target friend
36
+ let targetFriendId;
37
+ let queueItem;
38
+ if (delegationId) {
39
+ // Look up in attention queue
40
+ const found = queue.find((item) => item.id === delegationId);
41
+ if (!found) {
42
+ return `no delegation found with id ${delegationId} — check your attention queue`;
43
+ }
44
+ targetFriendId = found.friendId;
45
+ queueItem = found;
46
+ }
47
+ else if (friendId) {
48
+ targetFriendId = friendId;
49
+ }
50
+ else {
51
+ return "specify who this thought is for — use delegationId to address a held thought, or friendId for spontaneous outreach";
52
+ }
53
+ // Route to target
54
+ const result = await routeToFriend(targetFriendId, content, queueItem);
55
+ (0, runtime_1.emitNervesEvent)({
56
+ event: "senses.surface_routed",
57
+ component: "senses",
58
+ message: `surface routed to ${targetFriendId}: ${result.status}`,
59
+ meta: {
60
+ targetFriendId,
61
+ status: result.status,
62
+ hasDelegationId: !!delegationId,
63
+ ...(result.detail ? { detail: result.detail } : {}),
64
+ },
65
+ });
66
+ // On successful routing with delegationId:
67
+ // 1. Advance obligation to "returned" (disk FIRST — crash safety)
68
+ // 2. Dequeue from in-memory queue (AFTER obligation advance)
69
+ if (delegationId && queueItem && result.status !== "failed") {
70
+ if (queueItem.obligationId) {
71
+ advanceObligation(queueItem.obligationId, {
72
+ status: "returned",
73
+ returnedAt: Date.now(),
74
+ returnTarget: "surface",
75
+ });
76
+ }
77
+ (0, attention_queue_1.dequeueAttentionItem)(queue, delegationId);
78
+ }
79
+ // Return delivery status
80
+ const detail = result.detail ? ` — ${result.detail}` : "";
81
+ return `${result.status}${detail}`;
82
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.133",
3
+ "version": "0.1.0-alpha.134",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",