@ouro.bot/cli 0.1.0-alpha.687 → 0.1.0-alpha.689

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,18 @@
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.689",
6
+ "changes": [
7
+ "Avoid self-mutating npm@latest in the trusted-publishing workflow; verify the npm toolchain floor and use a pinned fallback so publish can complete reliably after merge."
8
+ ]
9
+ },
10
+ {
11
+ "version": "0.1.0-alpha.688",
12
+ "changes": [
13
+ "Render freshest friend-facing contact timing in protected turn orientation and active-work status so private/self turns cannot mistake stale background timestamps for current contact."
14
+ ]
15
+ },
4
16
  {
5
17
  "version": "0.1.0-alpha.687",
6
18
  "changes": [
@@ -11,6 +11,7 @@ exports.formatActiveWorkChanges = formatActiveWorkChanges;
11
11
  const runtime_1 = require("../nerves/runtime");
12
12
  const state_machine_1 = require("./bridges/state-machine");
13
13
  const obligations_1 = require("../arc/obligations");
14
+ const session_activity_1 = require("./session-activity");
14
15
  const target_resolution_1 = require("./target-resolution");
15
16
  const config_1 = require("./config");
16
17
  const RECENT_ACTIVE_OBLIGATION_WINDOW_MS = 60 * 60 * 1000;
@@ -683,6 +684,12 @@ function formatActiveWorkFrame(frame, options) {
683
684
  const lines = ["## what i'm holding"];
684
685
  lines.push("this is my top-level live world-state right now. private-runtime work, coding lanes, other sessions, and return obligations all belong inside this picture.");
685
686
  lines.push("if older checkpoints elsewhere in the transcript disagree with this picture, this picture wins.");
687
+ const friendContactTiming = (0, session_activity_1.describeSessionActivityTiming)((0, session_activity_1.selectFreshestFriendFacingActivity)(frame.friendActivity?.allOtherLiveSessions ?? []), { prefix: "freshest friend-facing contact" });
688
+ if (friendContactTiming) {
689
+ lines.push("");
690
+ lines.push("## contact timing");
691
+ lines.push(`- ${friendContactTiming}`);
692
+ }
686
693
  const primaryObligation = findPrimaryOpenObligation(frame);
687
694
  const currentSessionObligation = findCurrentSessionOpenObligation(frame);
688
695
  const activeLane = formatActiveLane(frame, primaryObligation);
@@ -34,6 +34,8 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.listSessionActivity = listSessionActivity;
37
+ exports.selectFreshestFriendFacingActivity = selectFreshestFriendFacingActivity;
38
+ exports.describeSessionActivityTiming = describeSessionActivityTiming;
37
39
  exports.findFreshestFriendSession = findFreshestFriendSession;
38
40
  const fs = __importStar(require("fs"));
39
41
  const path = __importStar(require("path"));
@@ -44,6 +46,16 @@ const DEFAULT_ACTIVE_THRESHOLD_MS = 24 * 60 * 60 * 1000;
44
46
  function activityPriority(source) {
45
47
  return source === "friend-facing" ? 0 : 1;
46
48
  }
49
+ function formatElapsed(ms) {
50
+ const minutes = Math.max(0, Math.floor(ms / 60000));
51
+ if (minutes < 60)
52
+ return `${minutes}m ago`;
53
+ const hours = Math.floor(minutes / 60);
54
+ if (hours < 24)
55
+ return `${hours}h ago`;
56
+ const days = Math.floor(hours / 24);
57
+ return `${days}d ago`;
58
+ }
47
59
  function resolveFriendName(friendId, friendsDir, agentName) {
48
60
  if (friendId === "self")
49
61
  return agentName;
@@ -183,6 +195,36 @@ function listSessionActivity(query) {
183
195
  return b.lastActivityMs - a.lastActivityMs;
184
196
  });
185
197
  }
198
+ function selectFreshestFriendFacingActivity(records) {
199
+ const candidates = records
200
+ .filter((record) => record.friendId !== "self" && record.channel !== "inner" && record.activitySource === "friend-facing")
201
+ .sort((a, b) => b.lastActivityMs - a.lastActivityMs);
202
+ return candidates[0] ?? null;
203
+ }
204
+ function describeSessionActivityTiming(record, options = {}) {
205
+ if (!record)
206
+ return "";
207
+ const nowMs = options.nowMs ?? Date.now();
208
+ const parts = [];
209
+ const inboundMs = record.lastInboundAt ? Date.parse(record.lastInboundAt) : NaN;
210
+ const outboundMs = record.lastOutboundAt ? Date.parse(record.lastOutboundAt) : NaN;
211
+ if (Number.isFinite(inboundMs)) {
212
+ parts.push(`last inbound ${formatElapsed(nowMs - inboundMs)}`);
213
+ }
214
+ if (Number.isFinite(outboundMs)) {
215
+ parts.push(`i last replied ${formatElapsed(nowMs - outboundMs)}`);
216
+ }
217
+ if (record.unansweredInboundCount > 0) {
218
+ const count = record.unansweredInboundCount;
219
+ parts.push(`${count} unanswered inbound message${count === 1 ? "" : "s"}`);
220
+ }
221
+ if (parts.length === 0) {
222
+ parts.push(`last activity ${formatElapsed(nowMs - record.lastActivityMs)}`);
223
+ }
224
+ const label = `${record.friendName}/${record.channel}/${record.key}`;
225
+ const prefix = options.prefix ?? "friend-facing contact";
226
+ return `${prefix}: ${label}; ${parts.join("; ")}`;
227
+ }
186
228
  function findFreshestFriendSession(query) {
187
229
  const { activeOnly = false, activeThresholdMs = DEFAULT_ACTIVE_THRESHOLD_MS, nowMs, ...rest } = query;
188
230
  const currentSession = rest.currentSession ?? null;
@@ -197,6 +197,7 @@ function buildStartOfTurnPacket(view, opts) {
197
197
  recoverySentinel: opts?.recoverySentinel,
198
198
  resumeHint: buildResumeHint(view, opts?.canonicalObligations ? effectiveObligations : undefined),
199
199
  currentSessionTiming: opts?.currentSessionTiming,
200
+ friendContactTiming: opts?.friendContactTiming,
200
201
  tempo,
201
202
  tokenBudget,
202
203
  assembledAt: new Date().toISOString(),
@@ -238,6 +239,7 @@ function renderStartOfTurnPacket(packet) {
238
239
  { label: "arc", content: packet.arcResume ?? "", priority: 7 },
239
240
  { label: "recoverySentinel", content: packet.recoverySentinel ? (0, context_loss_sentinel_1.formatContextLossSentinelText)(packet.recoverySentinel) : "", priority: 7 },
240
241
  { label: "resume", content: packet.resumeHint, priority: 6 },
242
+ { label: "friendContact", content: packet.friendContactTiming ?? "", priority: 6 },
241
243
  { label: "sessionTiming", content: packet.currentSessionTiming ?? "", priority: 5 },
242
244
  { label: "obligations", content: packet.obligations, priority: 5 },
243
245
  { label: "cares", content: packet.cares, priority: 4 },
@@ -263,7 +265,7 @@ function renderStartOfTurnPacket(packet) {
263
265
  if (tokens <= budget.max)
264
266
  break;
265
267
  // Skip continuity sections — they are protected.
266
- if (section.label === "resume" || section.label === "arc" || section.label === "recoverySentinel" || section.label === "sessionTiming")
268
+ if (section.label === "resume" || section.label === "arc" || section.label === "recoverySentinel" || section.label === "friendContact" || section.label === "sessionTiming")
267
269
  continue;
268
270
  // Remove this section entirely
269
271
  const idx = sections.findIndex((s) => s.label === section.label);
@@ -300,6 +302,9 @@ function formatSections(sections) {
300
302
  case "sessionTiming":
301
303
  parts.push(`**Thread timing:** ${section.content}`);
302
304
  break;
305
+ case "friendContact":
306
+ parts.push(`**Recent contact:** ${section.content}`);
307
+ break;
303
308
  case "obligations":
304
309
  parts.push(`**Owed:**\n${section.content}`);
305
310
  break;
@@ -62,6 +62,7 @@ const bundle_state_1 = require("../heart/bundle-state");
62
62
  const sync_1 = require("../heart/sync");
63
63
  const config_1 = require("../heart/config");
64
64
  const session_events_1 = require("../heart/session-events");
65
+ const session_activity_1 = require("../heart/session-activity");
65
66
  const presence_1 = require("../arc/presence");
66
67
  const episodes_1 = require("../arc/episodes");
67
68
  const turn_context_1 = require("../heart/turn-context");
@@ -660,6 +661,7 @@ async function handleInboundTurn(input) {
660
661
  targetCandidates: ctx.targetCandidates,
661
662
  innerReturnObligations: ctx.returnObligations,
662
663
  });
664
+ const friendContactTiming = (0, session_activity_1.describeSessionActivityTiming)((0, session_activity_1.selectFreshestFriendFacingActivity)(sessionActivity), { prefix: "freshest friend-facing contact" });
663
665
  const delegationDecision = (0, delegation_1.decideDelegation)({
664
666
  channel: input.channel,
665
667
  ingressTexts: input.continuityIngressTexts ?? [],
@@ -731,6 +733,7 @@ async function handleInboundTurn(input) {
731
733
  all: activeWorkFrame.pendingObligations,
732
734
  },
733
735
  currentSessionTiming,
736
+ friendContactTiming,
734
737
  flightRecorderResume: ctx.flightRecorderResume,
735
738
  recoverySentinel: ctx.recoverySentinel,
736
739
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.687",
3
+ "version": "0.1.0-alpha.689",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",