@ouro.bot/cli 0.1.0-alpha.103 → 0.1.0-alpha.104

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
@@ -1,6 +1,13 @@
1
1
  {
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
+ {
5
+ "version": "0.1.0-alpha.104",
6
+ "changes": [
7
+ "Family-scoped status checks now keep the five-line header anchored on the current live conversation while appending an `other active sessions:` block for every other live lane the agent is actively working.",
8
+ "The inbound pipeline, active-work frame, prompt steering, and status retry logic now preserve non-current live coding lanes and obligations so family status answers stop hiding parallel work in other sessions."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.103",
6
13
  "changes": [
@@ -203,6 +203,9 @@ function buildActiveWorkFrame(input) {
203
203
  const activeBridgePresent = input.bridges.some(isActiveBridge);
204
204
  const openObligations = activeObligationCount(input.pendingObligations);
205
205
  const liveCodingSessions = input.codingSessions ?? [];
206
+ const allOtherLiveSessions = [...input.friendActivity].sort(compareActivity);
207
+ const otherCodingSessions = input.otherCodingSessions ?? [];
208
+ const pendingObligations = input.pendingObligations ?? [];
206
209
  const centerOfGravity = activeBridgePresent
207
210
  ? "shared-work"
208
211
  : (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff || openObligations > 0 || liveCodingSessions.length > 0)
@@ -223,9 +226,11 @@ function buildActiveWorkFrame(input) {
223
226
  friendActivity: {
224
227
  freshestForCurrentFriend: friendSessions[0] ?? null,
225
228
  otherLiveSessionsForCurrentFriend: friendSessions,
229
+ allOtherLiveSessions,
226
230
  },
227
231
  codingSessions: liveCodingSessions,
228
- pendingObligations: input.pendingObligations ?? [],
232
+ otherCodingSessions,
233
+ pendingObligations,
229
234
  targetCandidates: input.targetCandidates ?? [],
230
235
  bridgeSuggestion: suggestBridgeForActiveWork({
231
236
  currentSession: input.currentSession,
@@ -247,6 +252,8 @@ function buildActiveWorkFrame(input) {
247
252
  liveTasks: frame.taskPressure.liveTaskNames.length,
248
253
  liveSessions: frame.friendActivity.otherLiveSessionsForCurrentFriend.length,
249
254
  codingSessions: frame.codingSessions.length,
255
+ otherLiveSessions: allOtherLiveSessions.length,
256
+ otherCodingSessions: otherCodingSessions.length,
250
257
  pendingObligations: openObligations,
251
258
  hasBridgeSuggestion: frame.bridgeSuggestion !== null,
252
259
  },
@@ -259,6 +266,8 @@ function formatActiveWorkFrame(frame) {
259
266
  const activeLane = formatActiveLane(frame, primaryObligation);
260
267
  const currentArtifact = formatCurrentArtifact(frame, primaryObligation);
261
268
  const nextAction = formatNextAction(frame, primaryObligation);
269
+ const otherCodingSessions = frame.otherCodingSessions ?? [];
270
+ const otherLiveSessions = frame.friendActivity?.allOtherLiveSessions ?? [];
262
271
  // Session line
263
272
  if (frame.currentSession) {
264
273
  let sessionLine = `i'm in a conversation on ${formatSessionLabel(frame.currentSession)}.`;
@@ -331,6 +340,23 @@ function formatActiveWorkFrame(frame) {
331
340
  lines.push(`- [${session.status}] ${formatCodingLaneLabel(session)}${describeCodingSessionScope(session, frame.currentSession)}`);
332
341
  }
333
342
  }
343
+ if (otherCodingSessions.length > 0) {
344
+ lines.push("");
345
+ lines.push("## other live coding work");
346
+ for (const session of otherCodingSessions) {
347
+ const origin = session.originSession
348
+ ? `${session.originSession.friendId}/${session.originSession.channel}/${session.originSession.key}`
349
+ : "another session";
350
+ lines.push(`- [${session.status}] ${formatCodingLaneLabel(session)} for ${origin}`);
351
+ }
352
+ }
353
+ if (otherLiveSessions.length > 0) {
354
+ lines.push("");
355
+ lines.push("## other live sessions");
356
+ for (const session of otherLiveSessions) {
357
+ lines.push(`- ${session.friendName}/${session.channel}/${session.key}`);
358
+ }
359
+ }
334
360
  // Task pressure
335
361
  if ((frame.taskPressure?.liveTaskNames ?? []).length > 0) {
336
362
  lines.push("");
@@ -273,24 +273,32 @@ function getFinalAnswerRetryError(mustResolveBeforeHandoff, intent, sawSteeringF
273
273
  }
274
274
  return null;
275
275
  }
276
- function hasExactStatusReplyShape(answer) {
276
+ function hasExactStatusReplyShape(answer, statusCheckScope) {
277
277
  const lines = answer.trimEnd().split(/\r?\n/);
278
- if (lines.length !== 5)
279
- return false;
280
- return (/^live conversation:\s+\S.+$/i.test(lines[0])
278
+ const hasFiveLineHeader = (/^live conversation:\s+\S.+$/i.test(lines[0])
281
279
  && /^active lane:\s+\S.+$/i.test(lines[1])
282
280
  && /^current artifact:\s+\S.+$/i.test(lines[2])
283
281
  && /^latest checkpoint:\s+\S.+$/i.test(lines[3])
284
282
  && /^next action:\s+\S.+$/i.test(lines[4]));
283
+ if (!hasFiveLineHeader)
284
+ return false;
285
+ if (statusCheckScope !== "all-sessions-family") {
286
+ return lines.length === 5;
287
+ }
288
+ if (lines.length < 7)
289
+ return false;
290
+ if (!/^other active sessions:\s*$/i.test(lines[5]))
291
+ return false;
292
+ return lines.slice(6).every((line) => /^-\s+\S.+$/i.test(line));
285
293
  }
286
294
  function extractLatestCheckpoint(answer) {
287
295
  const latestLine = answer.trimEnd().split(/\r?\n/)[3];
288
296
  return latestLine.replace(/^latest checkpoint:\s*/i, "").trim();
289
297
  }
290
- function getStatusReplyRetryError(answer, statusCheckRequested, activeWorkFrame) {
298
+ function getStatusReplyRetryError(answer, statusCheckRequested, activeWorkFrame, statusCheckScope) {
291
299
  if (!statusCheckRequested || answer == null)
292
300
  return null;
293
- if (hasExactStatusReplyShape(answer))
301
+ if (hasExactStatusReplyShape(answer, statusCheckScope))
294
302
  return null;
295
303
  if (!activeWorkFrame) {
296
304
  return `the user asked for current status. call final_answer again using exactly these five non-empty lines and nothing else:
@@ -301,8 +309,8 @@ latest checkpoint: ...
301
309
  next action: ...`;
302
310
  }
303
311
  return `the user asked for current status right now.
304
- ${(0, obligation_steering_1.renderExactStatusReplyContract)(activeWorkFrame, (0, obligation_steering_1.findStatusObligation)(activeWorkFrame))}
305
- call final_answer again using that exact five-line shape and nothing else.`;
312
+ ${(0, obligation_steering_1.renderExactStatusReplyContract)(activeWorkFrame, (0, obligation_steering_1.findStatusObligation)(activeWorkFrame), statusCheckScope)}
313
+ call final_answer again using that exact status shape and nothing else.`;
306
314
  }
307
315
  // Re-export kick utilities for backward compat
308
316
  var kicks_1 = require("./kicks");
@@ -632,10 +640,10 @@ async function runAgent(messages, callbacks, channel, signal, options) {
632
640
  // Supports: {"answer":"text","intent":"..."} or "text" (JSON string).
633
641
  const { answer, intent } = parseFinalAnswerPayload(result.toolCalls[0].arguments);
634
642
  const retryError = getFinalAnswerRetryError(mustResolveBeforeHandoffActive, intent, sawSteeringFollowUp, options?.delegationDecision, sawSendMessageSelf, sawGoInward, sawQuerySession, options?.currentObligation ?? null, options?.activeWorkFrame?.inner?.job, sawExternalStateQuery);
635
- const statusReplyRetryError = getStatusReplyRetryError(answer, options?.statusCheckRequested, options?.activeWorkFrame);
643
+ const statusReplyRetryError = getStatusReplyRetryError(answer, options?.statusCheckRequested, options?.activeWorkFrame, options?.statusCheckScope);
636
644
  const exactStatusReplyAccepted = Boolean(options?.statusCheckRequested) && !statusReplyRetryError && answer != null;
637
645
  const deliveredAnswer = exactStatusReplyAccepted && options?.activeWorkFrame
638
- ? (0, obligation_steering_1.buildExactStatusReply)(options.activeWorkFrame, (0, obligation_steering_1.findStatusObligation)(options.activeWorkFrame), extractLatestCheckpoint(answer))
646
+ ? (0, obligation_steering_1.buildExactStatusReply)(options.activeWorkFrame, (0, obligation_steering_1.findStatusObligation)(options.activeWorkFrame), extractLatestCheckpoint(answer), options?.statusCheckScope)
639
647
  : answer;
640
648
  const validDirectReply = mustResolveBeforeHandoffActive && intent === "direct_reply" && sawSteeringFollowUp;
641
649
  const validTerminalIntent = intent === "complete" || intent === "blocked";
@@ -20,10 +20,102 @@ function newestObligationFirst(left, right) {
20
20
  return obligationTimestampMs(right) - obligationTimestampMs(left);
21
21
  }
22
22
  function matchesCurrentSession(frame, obligation) {
23
+ return matchesSessionOrigin(frame, obligation.origin);
24
+ }
25
+ function matchesSessionOrigin(frame, origin) {
23
26
  return Boolean(frame.currentSession
24
- && obligation.origin.friendId === frame.currentSession.friendId
25
- && obligation.origin.channel === frame.currentSession.channel
26
- && obligation.origin.key === frame.currentSession.key);
27
+ && origin.friendId === frame.currentSession.friendId
28
+ && origin.channel === frame.currentSession.channel
29
+ && origin.key === frame.currentSession.key);
30
+ }
31
+ function sessionOriginKey(origin) {
32
+ return `${origin.friendId}/${origin.channel}/${origin.key}`;
33
+ }
34
+ function codingSessionTimestampMs(session) {
35
+ return Date.parse(session.lastActivityAt ?? session.startedAt);
36
+ }
37
+ function formatCodingLaneLabel(session) {
38
+ return `${session.runner} ${session.id}`;
39
+ }
40
+ function formatOtherSessionArtifact(obligation, codingSession) {
41
+ if (obligation?.currentArtifact?.trim())
42
+ return obligation.currentArtifact.trim();
43
+ if (obligation?.currentSurface?.kind === "merge" && obligation.currentSurface.label.trim()) {
44
+ return obligation.currentSurface.label.trim();
45
+ }
46
+ if (codingSession)
47
+ return "no PR or merge artifact yet";
48
+ return "no artifact yet";
49
+ }
50
+ function formatOtherSessionNextAction(obligation, codingSession) {
51
+ if (obligation?.nextAction?.trim())
52
+ return obligation.nextAction.trim();
53
+ if (obligation?.status === "waiting_for_merge") {
54
+ return `wait for checks, merge ${formatMergeArtifact(obligation)}, then update runtime`;
55
+ }
56
+ if (obligation?.status === "updating_runtime") {
57
+ return "update runtime, verify version/changelog, then re-observe";
58
+ }
59
+ if (codingSession?.status === "waiting_input") {
60
+ return `answer ${formatCodingLaneLabel(codingSession)} and continue`;
61
+ }
62
+ if (codingSession?.status === "stalled") {
63
+ return `unstick ${formatCodingLaneLabel(codingSession)} and continue`;
64
+ }
65
+ if (codingSession) {
66
+ return "finish the coding pass and bring the result back there";
67
+ }
68
+ if (obligation) {
69
+ return "continue the active loop and bring the result back there";
70
+ }
71
+ return "check this session and bring back the latest concrete state";
72
+ }
73
+ function findFriendNameForOrigin(frame, origin) {
74
+ return (frame.friendActivity?.allOtherLiveSessions ?? []).find((entry) => entry.friendId === origin.friendId
75
+ && entry.channel === origin.channel
76
+ && entry.key === origin.key)?.friendName ?? origin.friendId;
77
+ }
78
+ function buildOtherActiveSessionLines(frame) {
79
+ const originMap = new Map();
80
+ for (const session of frame.friendActivity?.allOtherLiveSessions ?? []) {
81
+ originMap.set(sessionOriginKey(session), {
82
+ friendId: session.friendId,
83
+ channel: session.channel,
84
+ key: session.key,
85
+ });
86
+ }
87
+ for (const session of frame.otherCodingSessions ?? []) {
88
+ if (!session.originSession || matchesSessionOrigin(frame, session.originSession))
89
+ continue;
90
+ originMap.set(sessionOriginKey(session.originSession), session.originSession);
91
+ }
92
+ for (const obligation of frame.pendingObligations ?? []) {
93
+ if (obligation.status === "fulfilled" || matchesSessionOrigin(frame, obligation.origin))
94
+ continue;
95
+ originMap.set(sessionOriginKey(obligation.origin), obligation.origin);
96
+ }
97
+ const summaries = [...originMap.values()].map((origin) => {
98
+ const obligation = [...(frame.pendingObligations ?? [])]
99
+ .filter((candidate) => candidate.status !== "fulfilled" && sessionOriginKey(candidate.origin) === sessionOriginKey(origin))
100
+ .sort(newestObligationFirst)[0] ?? null;
101
+ const codingSession = [...(frame.otherCodingSessions ?? [])]
102
+ .filter((candidate) => candidate.originSession && sessionOriginKey(candidate.originSession) === sessionOriginKey(origin))
103
+ .sort((left, right) => codingSessionTimestampMs(right) - codingSessionTimestampMs(left))[0] ?? null;
104
+ const liveSession = (frame.friendActivity?.allOtherLiveSessions ?? []).find((candidate) => sessionOriginKey(candidate) === sessionOriginKey(origin)) ?? null;
105
+ const timestampMs = Math.max(liveSession?.lastActivityMs ?? 0, codingSession ? codingSessionTimestampMs(codingSession) : 0, obligation ? obligationTimestampMs(obligation) : 0);
106
+ const activeLane = codingSession
107
+ ? formatCodingLaneLabel(codingSession)
108
+ : obligation?.currentSurface?.label?.trim() || "this live thread";
109
+ const artifact = formatOtherSessionArtifact(obligation, codingSession);
110
+ const nextAction = formatOtherSessionNextAction(obligation, codingSession);
111
+ const status = obligation?.status ?? codingSession?.status ?? "active";
112
+ const friendName = findFriendNameForOrigin(frame, origin);
113
+ return {
114
+ timestampMs,
115
+ line: `- ${friendName}/${origin.channel}/${origin.key}: [${status}] ${activeLane}; artifact ${artifact}; next ${nextAction}`,
116
+ };
117
+ }).sort((left, right) => right.timestampMs - left.timestampMs);
118
+ return summaries.length > 0 ? summaries.map((entry) => entry.line) : ["- none"];
27
119
  }
28
120
  function findStatusObligation(frame) {
29
121
  if (!frame)
@@ -36,6 +128,12 @@ function findStatusObligation(frame) {
36
128
  return sameSession;
37
129
  return openObligations[0] ?? null;
38
130
  }
131
+ function findCurrentSessionStatusObligation(frame) {
132
+ const openObligations = [...(frame.pendingObligations ?? [])]
133
+ .filter((obligation) => obligation.status !== "fulfilled")
134
+ .sort(newestObligationFirst);
135
+ return openObligations.find((obligation) => matchesCurrentSession(frame, obligation)) ?? null;
136
+ }
39
137
  function renderActiveObligationSteering(obligation) {
40
138
  (0, runtime_1.emitNervesEvent)({
41
139
  component: "mind",
@@ -168,25 +266,46 @@ no option list.
168
266
  present tense only.
169
267
  if a finished step matters, i label it "just finished" instead of presenting it as current work.`;
170
268
  }
171
- function buildExactStatusReply(frame, obligation, latestCheckpoint) {
269
+ function buildExactStatusReply(frame, obligation, latestCheckpoint, statusCheckScope) {
270
+ const headerObligation = statusCheckScope === "all-sessions-family"
271
+ ? findCurrentSessionStatusObligation(frame)
272
+ : obligation;
172
273
  const liveConversation = frame.currentSession
173
274
  ? `${frame.currentSession.channel}/${frame.currentSession.key}`
174
275
  : "not in a live conversation";
175
- const activeLane = obligation
176
- ? formatActiveLane(frame, obligation)
276
+ const activeLane = headerObligation
277
+ ? formatActiveLane(frame, headerObligation)
177
278
  : (frame.currentSession ? "this same thread" : "this live loop");
178
- const currentArtifact = formatCurrentArtifact(frame, obligation) || 'no artifact yet';
179
- const nextAction = formatNextAction(frame, obligation) || "continue the active loop and bring the result back here";
279
+ const currentArtifact = formatCurrentArtifact(frame, headerObligation) || "no artifact yet";
280
+ const nextAction = formatNextAction(frame, headerObligation) || "continue the active loop and bring the result back here";
180
281
  const latest = latestCheckpoint.trim() || "<freshest concrete thing i just finished or verified>";
181
- return [
282
+ const lines = [
182
283
  `live conversation: ${liveConversation}`,
183
284
  `active lane: ${activeLane}`,
184
285
  `current artifact: ${currentArtifact}`,
185
286
  `latest checkpoint: ${latest}`,
186
287
  `next action: ${nextAction}`,
187
- ].join("\n");
288
+ ];
289
+ if (statusCheckScope === "all-sessions-family") {
290
+ lines.push("other active sessions:");
291
+ lines.push(...buildOtherActiveSessionLines(frame));
292
+ }
293
+ return lines.join("\n");
188
294
  }
189
- function renderExactStatusReplyContract(frame, obligation) {
295
+ function renderExactStatusReplyContract(frame, obligation, statusCheckScope) {
296
+ const headerObligation = statusCheckScope === "all-sessions-family"
297
+ ? findCurrentSessionStatusObligation(frame)
298
+ : obligation;
299
+ if (statusCheckScope === "all-sessions-family") {
300
+ return `reply using exactly this status shape and nothing else:
301
+ live conversation: ${frame.currentSession ? `${frame.currentSession.channel}/${frame.currentSession.key}` : "not in a live conversation"}
302
+ active lane: ${headerObligation ? formatActiveLane(frame, headerObligation) : (frame.currentSession ? "this same thread" : "this live loop")}
303
+ current artifact: ${formatCurrentArtifact(frame, headerObligation) || "no artifact yet"}
304
+ latest checkpoint: <freshest concrete thing i just finished or verified>
305
+ next action: ${formatNextAction(frame, headerObligation) || "continue the active loop and bring the result back here"}
306
+ other active sessions:
307
+ - <session label>: <what i'm doing there right now>`;
308
+ }
190
309
  return `reply using exactly these five lines and nothing else:
191
310
  ${buildExactStatusReply(frame, obligation, "<freshest concrete thing i just finished or verified>")}
192
311
  `;
@@ -544,7 +544,7 @@ function statusCheckSection(channel, options) {
544
544
  const activeObligation = (0, obligation_steering_1.findStatusObligation)(frame);
545
545
  return `## status question on this turn
546
546
  the user is asking for current status right now.
547
- ${(0, obligation_steering_1.renderExactStatusReplyContract)(frame, activeObligation)}`;
547
+ ${(0, obligation_steering_1.renderExactStatusReplyContract)(frame, activeObligation, options.statusCheckScope)}`;
548
548
  }
549
549
  function commitmentsSection(options) {
550
550
  if (!options?.activeWorkFrame)
@@ -55,6 +55,12 @@ function isStatusCheckRequested(ingressTexts) {
55
55
  return false;
56
56
  return STATUS_CHECK_PATTERNS.some((pattern) => pattern.test(latest));
57
57
  }
58
+ function isLiveCodingSessionStatus(status) {
59
+ return status === "spawning"
60
+ || status === "running"
61
+ || status === "waiting_input"
62
+ || status === "stalled";
63
+ }
58
64
  function readInnerWorkState() {
59
65
  const defaultJob = {
60
66
  status: "idle",
@@ -205,23 +211,21 @@ async function handleInboundTurn(input) {
205
211
  pendingObligations = [];
206
212
  }
207
213
  let codingSessions = [];
214
+ let otherCodingSessions = [];
208
215
  try {
209
- codingSessions = (0, coding_1.getCodingSessionManager)()
216
+ const liveCodingSessions = (0, coding_1.getCodingSessionManager)()
210
217
  .listSessions()
211
- .filter((session) => {
212
- if (session.status !== "spawning" && session.status !== "running" && session.status !== "waiting_input" && session.status !== "stalled") {
213
- return false;
214
- }
215
- if (!session.originSession) {
216
- return false;
217
- }
218
- return session.originSession.friendId === currentSession.friendId
219
- && session.originSession.channel === currentSession.channel
220
- && session.originSession.key === currentSession.key;
221
- });
218
+ .filter((session) => isLiveCodingSessionStatus(session.status) && Boolean(session.originSession));
219
+ codingSessions = liveCodingSessions.filter((session) => session.originSession?.friendId === currentSession.friendId
220
+ && session.originSession.channel === currentSession.channel
221
+ && session.originSession.key === currentSession.key);
222
+ otherCodingSessions = liveCodingSessions.filter((session) => !(session.originSession?.friendId === currentSession.friendId
223
+ && session.originSession.channel === currentSession.channel
224
+ && session.originSession.key === currentSession.key));
222
225
  }
223
226
  catch {
224
227
  codingSessions = [];
228
+ otherCodingSessions = [];
225
229
  }
226
230
  const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
227
231
  currentSession,
@@ -230,6 +234,7 @@ async function handleInboundTurn(input) {
230
234
  inner: readInnerWorkState(),
231
235
  bridges: activeBridges,
232
236
  codingSessions,
237
+ otherCodingSessions,
233
238
  pendingObligations,
234
239
  taskBoard: (() => {
235
240
  try {
@@ -289,6 +294,9 @@ async function handleInboundTurn(input) {
289
294
  // Step 5: runAgent
290
295
  const existingToolContext = input.runAgentOptions?.toolContext;
291
296
  const statusCheckRequested = isStatusCheckRequested(input.continuityIngressTexts);
297
+ const statusCheckScope = statusCheckRequested && resolvedContext.friend.trustLevel === "family"
298
+ ? "all-sessions-family"
299
+ : undefined;
292
300
  const runAgentOptions = {
293
301
  ...input.runAgentOptions,
294
302
  bridgeContext,
@@ -297,6 +305,7 @@ async function handleInboundTurn(input) {
297
305
  currentSessionKey: currentSession.key,
298
306
  currentObligation,
299
307
  statusCheckRequested,
308
+ statusCheckScope,
300
309
  toolChoiceRequired: statusCheckRequested ? true : input.runAgentOptions?.toolChoiceRequired,
301
310
  mustResolveBeforeHandoff,
302
311
  setMustResolveBeforeHandoff: (value) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.103",
3
+ "version": "0.1.0-alpha.104",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",