@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.
@@ -35,8 +35,11 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.INNER_DIALOG_PENDING = void 0;
37
37
  exports.getPendingDir = getPendingDir;
38
+ exports.getDeferredReturnDir = getDeferredReturnDir;
38
39
  exports.getInnerDialogPendingDir = getInnerDialogPendingDir;
39
40
  exports.hasPendingMessages = hasPendingMessages;
41
+ exports.enqueueDeferredReturn = enqueueDeferredReturn;
42
+ exports.drainDeferredReturns = drainDeferredReturns;
40
43
  exports.drainPending = drainPending;
41
44
  const fs = __importStar(require("fs"));
42
45
  const path = __importStar(require("path"));
@@ -45,6 +48,9 @@ const runtime_1 = require("../nerves/runtime");
45
48
  function getPendingDir(agentName, friendId, channel, key) {
46
49
  return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending", friendId, channel, key);
47
50
  }
51
+ function getDeferredReturnDir(agentName, friendId) {
52
+ return path.join((0, identity_1.getAgentRoot)(agentName), "state", "pending-returns", friendId);
53
+ }
48
54
  /** Canonical inner-dialog pending path segments. */
49
55
  exports.INNER_DIALOG_PENDING = { friendId: "self", channel: "inner", key: "dialog" };
50
56
  /** Returns the pending dir for this agent's inner dialog. */
@@ -61,15 +67,22 @@ function hasPendingMessages(pendingDir) {
61
67
  return false;
62
68
  }
63
69
  }
64
- function drainPending(pendingDir) {
65
- if (!fs.existsSync(pendingDir))
66
- return [];
70
+ function writeQueueFile(queueDir, message) {
71
+ fs.mkdirSync(queueDir, { recursive: true });
72
+ const fileName = `${message.timestamp}-${Math.random().toString(36).slice(2, 10)}.json`;
73
+ const filePath = path.join(queueDir, fileName);
74
+ fs.writeFileSync(filePath, JSON.stringify(message, null, 2));
75
+ return filePath;
76
+ }
77
+ function drainQueue(queueDir) {
78
+ if (!fs.existsSync(queueDir))
79
+ return { messages: [], recovered: 0 };
67
80
  let entries;
68
81
  try {
69
- entries = fs.readdirSync(pendingDir);
82
+ entries = fs.readdirSync(queueDir);
70
83
  }
71
84
  catch {
72
- return [];
85
+ return { messages: [], recovered: 0 };
73
86
  }
74
87
  // Collect both .json (new) and .processing (crash recovery)
75
88
  const jsonFiles = entries.filter(f => f.endsWith(".json") && !f.endsWith(".processing"));
@@ -81,9 +94,9 @@ function drainPending(pendingDir) {
81
94
  ].sort((a, b) => a.file.localeCompare(b.file));
82
95
  const messages = [];
83
96
  for (const { file, needsRename } of allFiles) {
84
- const srcPath = path.join(pendingDir, file);
97
+ const srcPath = path.join(queueDir, file);
85
98
  const processingPath = needsRename
86
- ? path.join(pendingDir, file + ".processing")
99
+ ? path.join(queueDir, file + ".processing")
87
100
  : srcPath;
88
101
  try {
89
102
  if (needsRename) {
@@ -102,11 +115,42 @@ function drainPending(pendingDir) {
102
115
  catch { /* ignore */ }
103
116
  }
104
117
  }
118
+ return {
119
+ messages,
120
+ recovered: processingFiles.length,
121
+ };
122
+ }
123
+ function enqueueDeferredReturn(agentName, friendId, message) {
124
+ const queueDir = getDeferredReturnDir(agentName, friendId);
125
+ const filePath = writeQueueFile(queueDir, message);
126
+ (0, runtime_1.emitNervesEvent)({
127
+ event: "mind.deferred_return_enqueued",
128
+ component: "mind",
129
+ message: "deferred return queued for later friend delivery",
130
+ meta: { friendId, queueDir },
131
+ });
132
+ return filePath;
133
+ }
134
+ function drainDeferredReturns(agentName, friendId) {
135
+ const queueDir = getDeferredReturnDir(agentName, friendId);
136
+ const { messages } = drainQueue(queueDir);
137
+ (0, runtime_1.emitNervesEvent)({
138
+ event: "mind.deferred_returns_drained",
139
+ component: "mind",
140
+ message: "deferred friend returns drained",
141
+ meta: { friendId, queueDir, count: messages.length },
142
+ });
143
+ return messages;
144
+ }
145
+ function drainPending(pendingDir) {
146
+ if (!fs.existsSync(pendingDir))
147
+ return [];
148
+ const { messages, recovered } = drainQueue(pendingDir);
105
149
  (0, runtime_1.emitNervesEvent)({
106
150
  event: "mind.pending_drained",
107
151
  component: "mind",
108
152
  message: "pending queue drained",
109
- meta: { pendingDir, count: messages.length, recovered: processingFiles.length },
153
+ meta: { pendingDir, count: messages.length, recovered },
110
154
  });
111
155
  return messages;
112
156
  }
@@ -56,6 +56,8 @@ const runtime_1 = require("../nerves/runtime");
56
56
  const bundle_manifest_1 = require("./bundle-manifest");
57
57
  const first_impressions_1 = require("./first-impressions");
58
58
  const tasks_1 = require("../repertoire/tasks");
59
+ const session_activity_1 = require("../heart/session-activity");
60
+ const active_work_1 = require("../heart/active-work");
59
61
  // Lazy-loaded psyche text cache
60
62
  let _psycheCache = null;
61
63
  let _senseStatusLinesCache = null;
@@ -85,80 +87,25 @@ function resetPsycheCache() {
85
87
  _senseStatusLinesCache = null;
86
88
  }
87
89
  const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
88
- function resolveFriendName(friendId, friendsDir, agentName) {
89
- if (friendId === "self")
90
- return agentName;
91
- try {
92
- const raw = fs.readFileSync(path.join(friendsDir, `${friendId}.json`), "utf-8");
93
- const record = JSON.parse(raw);
94
- return record.name ?? friendId;
95
- }
96
- catch {
97
- return friendId;
98
- }
99
- }
100
90
  function buildSessionSummary(options) {
101
91
  const { sessionsDir, friendsDir, agentName, currentFriendId, currentChannel, currentKey, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, } = options;
102
- if (!fs.existsSync(sessionsDir))
103
- return "";
104
92
  const now = Date.now();
105
- const entries = [];
106
- let friendDirs;
107
- try {
108
- friendDirs = fs.readdirSync(sessionsDir);
109
- }
110
- catch {
111
- return "";
112
- }
113
- for (const friendId of friendDirs) {
114
- const friendPath = path.join(sessionsDir, friendId);
115
- let channels;
116
- try {
117
- channels = fs.readdirSync(friendPath);
118
- }
119
- catch {
120
- continue;
121
- }
122
- for (const channel of channels) {
123
- const channelPath = path.join(friendPath, channel);
124
- let keys;
125
- try {
126
- keys = fs.readdirSync(channelPath);
127
- }
128
- catch {
129
- continue;
130
- }
131
- for (const keyFile of keys) {
132
- if (!keyFile.endsWith(".json"))
133
- continue;
134
- const key = keyFile.replace(/\.json$/, "");
135
- // Exclude current session
136
- if (friendId === currentFriendId && channel === currentChannel && key === currentKey) {
137
- continue;
138
- }
139
- const filePath = path.join(channelPath, keyFile);
140
- let mtimeMs;
141
- try {
142
- mtimeMs = fs.statSync(filePath).mtimeMs;
143
- }
144
- catch {
145
- continue;
146
- }
147
- if (now - mtimeMs > activeThresholdMs)
148
- continue;
149
- const displayName = resolveFriendName(friendId, friendsDir, agentName);
150
- entries.push({ friendId, displayName, channel, key, lastActivityMs: mtimeMs });
151
- }
152
- }
153
- }
93
+ const query = {
94
+ sessionsDir,
95
+ friendsDir,
96
+ agentName,
97
+ activeThresholdMs,
98
+ currentSession: currentFriendId && currentChannel && currentKey
99
+ ? { friendId: currentFriendId, channel: currentChannel, key: currentKey }
100
+ : null,
101
+ };
102
+ const entries = (0, session_activity_1.listSessionActivity)(query);
154
103
  if (entries.length === 0)
155
104
  return "";
156
- // Sort by most recent first
157
- entries.sort((a, b) => b.lastActivityMs - a.lastActivityMs);
158
105
  const lines = ["## active sessions"];
159
106
  for (const entry of entries) {
160
107
  const ago = formatTimeAgo(now - entry.lastActivityMs);
161
- lines.push(`- ${entry.displayName}/${entry.channel}/${entry.key} (last: ${ago})`);
108
+ lines.push(`- ${entry.friendName}/${entry.channel}/${entry.key} (last: ${ago})`);
162
109
  }
163
110
  return lines.join("\n");
164
111
  }
@@ -413,11 +360,29 @@ function memoryFriendToolContractSection() {
413
360
  - My task board is always loaded - I already know my work.`;
414
361
  }
415
362
  function bridgeContextSection(options) {
363
+ if (options?.activeWorkFrame)
364
+ return "";
416
365
  const bridgeContext = options?.bridgeContext?.trim() ?? "";
417
366
  if (!bridgeContext)
418
367
  return "";
419
368
  return bridgeContext.startsWith("## ") ? bridgeContext : `## active bridge work\n${bridgeContext}`;
420
369
  }
370
+ function activeWorkSection(options) {
371
+ if (!options?.activeWorkFrame)
372
+ return "";
373
+ return (0, active_work_1.formatActiveWorkFrame)(options.activeWorkFrame);
374
+ }
375
+ function delegationHintSection(options) {
376
+ if (!options?.delegationDecision)
377
+ return "";
378
+ const lines = [
379
+ "## delegation hint",
380
+ `target: ${options.delegationDecision.target}`,
381
+ `reasons: ${options.delegationDecision.reasons.length > 0 ? options.delegationDecision.reasons.join(", ") : "none"}`,
382
+ `outward closure: ${options.delegationDecision.outwardClosureRequired ? "required" : "not required"}`,
383
+ ];
384
+ return lines.join("\n");
385
+ }
421
386
  function toolBehaviorSection(options) {
422
387
  if (!(options?.toolChoiceRequired ?? true))
423
388
  return "";
@@ -549,6 +514,8 @@ async function buildSystem(channel = "cli", options, context) {
549
514
  mixedTrustGroupSection(context),
550
515
  skillsSection(),
551
516
  taskBoardSection(),
517
+ activeWorkSection(options),
518
+ delegationHintSection(options),
552
519
  bridgeContextSection(options),
553
520
  buildSessionSummary({
554
521
  sessionsDir: path.join((0, identity_1.getAgentRoot)(), "state", "sessions"),
@@ -49,6 +49,7 @@ const session_recall_1 = require("../heart/session-recall");
49
49
  const tools_1 = require("./coding/tools");
50
50
  const memory_1 = require("../mind/memory");
51
51
  const pending_1 = require("../mind/pending");
52
+ const progress_story_1 = require("../heart/progress-story");
52
53
  // Tracks which file paths have been read via read_file in this session.
53
54
  // edit_file requires a file to be read first (must-read-first guard).
54
55
  exports.editFileReadTracker = new Set();
@@ -65,14 +66,79 @@ function buildContextDiff(lines, changeStart, changeEnd, contextSize = 3) {
65
66
  }
66
67
  const NO_SESSION_FOUND_MESSAGE = "no session found for that friend/channel/key combination.";
67
68
  const EMPTY_SESSION_MESSAGE = "session exists but has no non-system messages.";
69
+ function findDelegatingBridgeId(ctx) {
70
+ const currentSession = ctx?.currentSession;
71
+ if (!currentSession)
72
+ return undefined;
73
+ return ctx?.activeBridges?.find((bridge) => bridge.lifecycle === "active"
74
+ && bridge.attachedSessions.some((session) => session.friendId === currentSession.friendId
75
+ && session.channel === currentSession.channel
76
+ && session.key === currentSession.key))?.id;
77
+ }
68
78
  async function recallSessionSafely(options) {
69
79
  try {
70
80
  return await (0, session_recall_1.recallSession)(options);
71
81
  }
72
- catch {
82
+ catch (error) {
83
+ if (options.summarize) {
84
+ (0, runtime_1.emitNervesEvent)({
85
+ component: "daemon",
86
+ event: "daemon.session_recall_summary_fallback",
87
+ message: "session recall summarization failed; using raw transcript",
88
+ meta: {
89
+ friendId: options.friendId,
90
+ channel: options.channel,
91
+ key: options.key,
92
+ error: error instanceof Error ? error.message : String(error),
93
+ },
94
+ });
95
+ try {
96
+ return await (0, session_recall_1.recallSession)({
97
+ ...options,
98
+ summarize: undefined,
99
+ });
100
+ }
101
+ catch {
102
+ return { kind: "missing" };
103
+ }
104
+ }
73
105
  return { kind: "missing" };
74
106
  }
75
107
  }
108
+ function normalizeProgressOutcome(text) {
109
+ const trimmed = text.trim();
110
+ if (!trimmed || trimmed === "nothing yet" || trimmed === "nothing recent") {
111
+ return null;
112
+ }
113
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") && trimmed.length >= 2) {
114
+ return trimmed.slice(1, -1);
115
+ }
116
+ return trimmed;
117
+ }
118
+ function renderInnerProgressStatus(status) {
119
+ if (status.processing === "pending") {
120
+ return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
121
+ scope: "inner-delegation",
122
+ phase: "queued",
123
+ objective: status.queue,
124
+ outcomeText: `wake: ${status.wake}`,
125
+ }));
126
+ }
127
+ if (status.processing === "started") {
128
+ return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
129
+ scope: "inner-delegation",
130
+ phase: "processing",
131
+ outcomeText: `wake: ${status.wake}`,
132
+ }));
133
+ }
134
+ const completedOutcome = normalizeProgressOutcome(status.surfaced) ?? status.surfaced;
135
+ return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
136
+ scope: "inner-delegation",
137
+ phase: "completed",
138
+ objective: null,
139
+ outcomeText: completedOutcome,
140
+ }));
141
+ }
76
142
  exports.baseToolDefinitions = [
77
143
  {
78
144
  tool: {
@@ -725,7 +791,7 @@ exports.baseToolDefinitions = [
725
791
  }
726
792
  const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)((0, identity_1.getAgentRoot)());
727
793
  const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
728
- return (0, thoughts_1.formatInnerDialogStatus)((0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir));
794
+ return renderInnerProgressStatus((0, thoughts_1.readInnerDialogStatus)(sessionPath, pendingDir));
729
795
  }
730
796
  const sessFile = (0, config_1.resolveSessionPath)(friendId, channel, key);
731
797
  const recall = await recallSessionSafely({
@@ -780,6 +846,17 @@ exports.baseToolDefinitions = [
780
846
  fs.mkdirSync(pendingDir, { recursive: true });
781
847
  const fileName = `${now}-${Math.random().toString(36).slice(2, 10)}.json`;
782
848
  const filePath = path.join(pendingDir, fileName);
849
+ const delegatingBridgeId = findDelegatingBridgeId(ctx);
850
+ const delegatedFrom = isSelf
851
+ && ctx?.currentSession
852
+ && !(ctx.currentSession.friendId === "self" && ctx.currentSession.channel === "inner")
853
+ ? {
854
+ friendId: ctx.currentSession.friendId,
855
+ channel: ctx.currentSession.channel,
856
+ key: ctx.currentSession.key,
857
+ ...(delegatingBridgeId ? { bridgeId: delegatingBridgeId } : {}),
858
+ }
859
+ : undefined;
783
860
  const envelope = {
784
861
  from: agentName,
785
862
  friendId,
@@ -787,6 +864,7 @@ exports.baseToolDefinitions = [
787
864
  key,
788
865
  content,
789
866
  timestamp: now,
867
+ ...(delegatedFrom ? { delegatedFrom } : {}),
790
868
  };
791
869
  fs.writeFileSync(filePath, JSON.stringify(envelope, null, 2));
792
870
  if (isSelf) {
@@ -803,7 +881,7 @@ exports.baseToolDefinitions = [
803
881
  queueMicrotask(() => {
804
882
  void runInnerDialogTurn({ reason: "instinct" });
805
883
  });
806
- return (0, thoughts_1.formatInnerDialogStatus)({
884
+ return renderInnerProgressStatus({
807
885
  queue: "queued to inner/dialog",
808
886
  wake: "inline scheduled",
809
887
  processing: "pending",
@@ -812,15 +890,16 @@ exports.baseToolDefinitions = [
812
890
  }
813
891
  else {
814
892
  const turnResult = await runInnerDialogTurn({ reason: "instinct" });
815
- return (0, thoughts_1.formatInnerDialogStatus)({
816
- queue: "queued to inner/dialog",
817
- wake: "inline fallback",
818
- processing: "processed",
819
- surfaced: (0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])),
820
- });
893
+ const surfacedPreview = normalizeProgressOutcome((0, thoughts_1.formatSurfacedValue)((0, thoughts_1.extractThoughtResponseFromMessages)(turnResult?.messages ?? [])));
894
+ return (0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
895
+ scope: "inner-delegation",
896
+ phase: "completed",
897
+ objective: "queued to inner/dialog",
898
+ outcomeText: `wake: inline fallback\n${surfacedPreview}`,
899
+ }));
821
900
  }
822
901
  }
823
- return (0, thoughts_1.formatInnerDialogStatus)({
902
+ return renderInnerProgressStatus({
824
903
  queue: "queued to inner/dialog",
825
904
  wake: "daemon requested",
826
905
  processing: "pending",
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.handleBlueBubblesEvent = handleBlueBubblesEvent;
37
37
  exports.recoverMissedBlueBubblesMessages = recoverMissedBlueBubblesMessages;
38
38
  exports.createBlueBubblesWebhookHandler = createBlueBubblesWebhookHandler;
39
+ exports.sendProactiveBlueBubblesMessageToSession = sendProactiveBlueBubblesMessageToSession;
39
40
  exports.drainAndSendPendingBlueBubbles = drainAndSendPendingBlueBubbles;
40
41
  exports.startBlueBubblesApp = startBlueBubblesApp;
41
42
  const fs = __importStar(require("node:fs"));
@@ -620,6 +621,7 @@ async function handleBlueBubblesNormalizedEvent(event, resolvedDeps, source) {
620
621
  hasExistingGroupWithFamily,
621
622
  enforceTrustGate: trust_gate_1.enforceTrustGate,
622
623
  drainPending: pending_1.drainPending,
624
+ drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(resolvedDeps.getAgentName(), deferredFriendId),
623
625
  runAgent: (msgs, cb, channel, sig, opts) => resolvedDeps.runAgent(msgs, cb, channel, sig, {
624
626
  ...opts,
625
627
  toolContext: {
@@ -840,6 +842,114 @@ function findImessageHandle(friend) {
840
842
  }
841
843
  return undefined;
842
844
  }
845
+ function extractChatIdentifierFromSessionKey(sessionKey) {
846
+ if (sessionKey.startsWith("chat:")) {
847
+ const chatGuid = sessionKey.slice("chat:".length).trim();
848
+ const parts = chatGuid.split(";");
849
+ return parts.length >= 3 ? parts[2]?.trim() || undefined : undefined;
850
+ }
851
+ if (sessionKey.startsWith("chat_identifier:")) {
852
+ const identifier = sessionKey.slice("chat_identifier:".length).trim();
853
+ return identifier || undefined;
854
+ }
855
+ return undefined;
856
+ }
857
+ function buildChatRefForSessionKey(friend, sessionKey) {
858
+ if (sessionKey.startsWith("chat:")) {
859
+ const chatGuid = sessionKey.slice("chat:".length).trim();
860
+ if (!chatGuid)
861
+ return null;
862
+ return {
863
+ chatGuid,
864
+ chatIdentifier: extractChatIdentifierFromSessionKey(sessionKey) ?? findImessageHandle(friend),
865
+ isGroup: chatGuid.includes(";+;"),
866
+ sessionKey,
867
+ sendTarget: { kind: "chat_guid", value: chatGuid },
868
+ participantHandles: [],
869
+ };
870
+ }
871
+ const chatIdentifier = extractChatIdentifierFromSessionKey(sessionKey) ?? findImessageHandle(friend);
872
+ if (!chatIdentifier)
873
+ return null;
874
+ return {
875
+ chatIdentifier,
876
+ isGroup: false,
877
+ sessionKey,
878
+ sendTarget: { kind: "chat_identifier", value: chatIdentifier },
879
+ participantHandles: [],
880
+ };
881
+ }
882
+ async function sendProactiveBlueBubblesMessageToSession(params, deps = {}) {
883
+ const resolvedDeps = { ...defaultDeps, ...deps };
884
+ const client = resolvedDeps.createClient();
885
+ const store = resolvedDeps.createFriendStore();
886
+ let friend;
887
+ try {
888
+ friend = await store.get(params.friendId);
889
+ }
890
+ catch {
891
+ friend = null;
892
+ }
893
+ if (!friend) {
894
+ (0, runtime_1.emitNervesEvent)({
895
+ level: "warn",
896
+ component: "senses",
897
+ event: "senses.bluebubbles_proactive_no_friend",
898
+ message: "proactive send skipped: friend not found",
899
+ meta: { friendId: params.friendId, sessionKey: params.sessionKey },
900
+ });
901
+ return { delivered: false, reason: "friend_not_found" };
902
+ }
903
+ if (!types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
904
+ (0, runtime_1.emitNervesEvent)({
905
+ component: "senses",
906
+ event: "senses.bluebubbles_proactive_trust_skip",
907
+ message: "proactive send skipped: trust level not allowed",
908
+ meta: { friendId: params.friendId, sessionKey: params.sessionKey, trustLevel: friend.trustLevel ?? "unknown" },
909
+ });
910
+ return { delivered: false, reason: "trust_skip" };
911
+ }
912
+ const chat = buildChatRefForSessionKey(friend, params.sessionKey);
913
+ if (!chat) {
914
+ (0, runtime_1.emitNervesEvent)({
915
+ level: "warn",
916
+ component: "senses",
917
+ event: "senses.bluebubbles_proactive_no_handle",
918
+ message: "proactive send skipped: no iMessage handle found",
919
+ meta: { friendId: params.friendId, sessionKey: params.sessionKey },
920
+ });
921
+ return { delivered: false, reason: "missing_target" };
922
+ }
923
+ try {
924
+ await client.sendText({ chat, text: params.text });
925
+ (0, runtime_1.emitNervesEvent)({
926
+ component: "senses",
927
+ event: "senses.bluebubbles_proactive_sent",
928
+ message: "proactive bluebubbles message sent",
929
+ meta: {
930
+ friendId: params.friendId,
931
+ sessionKey: params.sessionKey,
932
+ chatGuid: chat.chatGuid ?? null,
933
+ chatIdentifier: chat.chatIdentifier ?? null,
934
+ },
935
+ });
936
+ return { delivered: true };
937
+ }
938
+ catch (error) {
939
+ (0, runtime_1.emitNervesEvent)({
940
+ level: "error",
941
+ component: "senses",
942
+ event: "senses.bluebubbles_proactive_send_error",
943
+ message: "proactive bluebubbles send failed",
944
+ meta: {
945
+ friendId: params.friendId,
946
+ sessionKey: params.sessionKey,
947
+ reason: error instanceof Error ? error.message : String(error),
948
+ },
949
+ });
950
+ return { delivered: false, reason: "send_error" };
951
+ }
952
+ }
843
953
  function scanPendingBlueBubblesFiles(pendingRoot) {
844
954
  const results = [];
845
955
  let friendIds;
@@ -740,11 +740,12 @@ async function main(agentName, options) {
740
740
  : [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", undefined, resolvedContext) }];
741
741
  // Per-turn pipeline input: CLI capabilities and pending dir
742
742
  const cliCapabilities = (0, channel_1.getChannelCapabilities)("cli");
743
- const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, "cli", "session");
743
+ const currentAgentName = (0, identity_1.getAgentName)();
744
+ const pendingDir = (0, pending_1.getPendingDir)(currentAgentName, friendId, "cli", "session");
744
745
  const summarize = (0, core_1.createSummarize)();
745
746
  try {
746
747
  await runCliSession({
747
- agentName: (0, identity_1.getAgentName)(),
748
+ agentName: currentAgentName,
748
749
  pasteDebounceMs,
749
750
  messages: sessionMessages,
750
751
  runTurn: async (messages, userInput, callbacks, signal) => {
@@ -765,6 +766,7 @@ async function main(agentName, options) {
765
766
  externalId: localExternalId,
766
767
  enforceTrustGate: trust_gate_1.enforceTrustGate,
767
768
  drainPending: pending_1.drainPending,
769
+ drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(currentAgentName, deferredFriendId),
768
770
  runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
769
771
  ...opts,
770
772
  toolContext: {
@@ -4,6 +4,7 @@ exports.createDebugActivityController = createDebugActivityController;
4
4
  const format_1 = require("../mind/format");
5
5
  const phrases_1 = require("../mind/phrases");
6
6
  const runtime_1 = require("../nerves/runtime");
7
+ const progress_story_1 = require("../heart/progress-story");
7
8
  function createDebugActivityController(options) {
8
9
  let queue = Promise.resolve();
9
10
  let statusMessageGuid;
@@ -81,19 +82,31 @@ function createDebugActivityController(options) {
81
82
  nextPhrase(pool);
82
83
  return;
83
84
  }
84
- setStatus(`${nextPhrase(pool)}...`);
85
+ setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
86
+ scope: "shared-work",
87
+ phase: "processing",
88
+ objective: `${nextPhrase(pool)}...`,
89
+ })));
85
90
  },
86
91
  onToolStart(name, args) {
87
92
  hadToolRun = true;
88
93
  followupShown = false;
89
94
  const argSummary = Object.values(args).join(", ");
90
95
  const detail = argSummary ? ` (${argSummary})` : "";
91
- setStatus(`running ${name}${detail}...`);
96
+ setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
97
+ scope: "shared-work",
98
+ phase: "processing",
99
+ objective: `running ${name}${detail}...`,
100
+ })));
92
101
  },
93
102
  onToolEnd(name, summary, success) {
94
103
  hadToolRun = true;
95
104
  followupShown = false;
96
- setStatus((0, format_1.formatToolResult)(name, summary, success));
105
+ setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
106
+ scope: "shared-work",
107
+ phase: "processing",
108
+ objective: (0, format_1.formatToolResult)(name, summary, success),
109
+ })));
97
110
  },
98
111
  onTextChunk(text) {
99
112
  if (!text || !hadToolRun || followupShown) {
@@ -103,10 +116,18 @@ function createDebugActivityController(options) {
103
116
  if (options.suppressFollowupPhraseStatus) {
104
117
  return;
105
118
  }
106
- setStatus(`${nextPhrase(options.followupPhrases)}...`);
119
+ setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
120
+ scope: "shared-work",
121
+ phase: "processing",
122
+ objective: `${nextPhrase(options.followupPhrases)}...`,
123
+ })));
107
124
  },
108
125
  onError(error) {
109
- setStatus((0, format_1.formatError)(error));
126
+ setStatus((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
127
+ scope: "shared-work",
128
+ phase: "errored",
129
+ outcomeText: (0, format_1.formatError)(error),
130
+ })));
110
131
  this.finish();
111
132
  },
112
133
  async drain() {