@ouro.bot/cli 0.1.0-alpha.101 → 0.1.0-alpha.102

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.102",
6
+ "changes": [
7
+ "Live obligations now persist a concrete current artifact and next action, so status answers can anchor on the actual active lane, artifact, and immediate step instead of drifting into broad mission statements.",
8
+ "Obligation-bound coding feedback now turns PR milestones into structured report-backs and lifecycle updates, which keeps the originating live conversation informed when child work opens a PR, waits for merge, or needs a runtime update."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.101",
6
13
  "changes": [
@@ -63,6 +63,62 @@ function formatObligationSurface(obligation) {
63
63
  return ` (${obligation.currentSurface.label})`;
64
64
  }
65
65
  }
66
+ function findPrimaryOpenObligation(frame) {
67
+ return (frame.pendingObligations ?? []).find((ob) => ob.status !== "pending" && ob.status !== "fulfilled")
68
+ ?? (frame.pendingObligations ?? []).find(obligations_1.isOpenObligation)
69
+ ?? null;
70
+ }
71
+ function formatActiveLane(frame, obligation) {
72
+ const liveCodingSession = frame.codingSessions?.[0];
73
+ if (liveCodingSession) {
74
+ return `${formatCodingLaneLabel(liveCodingSession)}${describeCodingSessionScope(liveCodingSession, frame.currentSession)}`;
75
+ }
76
+ if (obligation?.currentSurface?.label) {
77
+ return obligation.currentSurface.label;
78
+ }
79
+ if (frame.inner?.job?.status === "running") {
80
+ return "inner dialog";
81
+ }
82
+ return null;
83
+ }
84
+ function formatCurrentArtifact(frame, obligation) {
85
+ if (obligation?.currentArtifact?.trim()) {
86
+ return obligation.currentArtifact.trim();
87
+ }
88
+ if (obligation?.currentSurface?.kind === "merge" && obligation.currentSurface.label.trim()) {
89
+ return obligation.currentSurface.label.trim();
90
+ }
91
+ if ((frame.codingSessions ?? []).length > 0) {
92
+ return "no PR or merge artifact yet";
93
+ }
94
+ return null;
95
+ }
96
+ function formatNextAction(frame, obligation) {
97
+ if (obligation?.nextAction?.trim()) {
98
+ return obligation.nextAction.trim();
99
+ }
100
+ const liveCodingSession = frame.codingSessions?.[0];
101
+ if (liveCodingSession?.status === "waiting_input") {
102
+ return `answer ${formatCodingLaneLabel(liveCodingSession)} and continue`;
103
+ }
104
+ if (liveCodingSession?.status === "stalled") {
105
+ return `unstick ${formatCodingLaneLabel(liveCodingSession)} and continue`;
106
+ }
107
+ if (liveCodingSession) {
108
+ return "finish the coding pass and bring the result back here";
109
+ }
110
+ if (obligation?.status === "waiting_for_merge") {
111
+ const artifact = formatCurrentArtifact(frame, obligation) ?? "the fix";
112
+ return `wait for checks, merge ${artifact}, then update runtime`;
113
+ }
114
+ if (obligation?.status === "updating_runtime") {
115
+ return "update runtime, verify version/changelog, then re-observe";
116
+ }
117
+ if (obligation) {
118
+ return "continue the active loop and bring the result back here";
119
+ }
120
+ return null;
121
+ }
66
122
  function suggestBridgeForActiveWork(input) {
67
123
  const targetCandidates = (input.targetCandidates ?? [])
68
124
  .filter((candidate) => {
@@ -173,6 +229,10 @@ function buildActiveWorkFrame(input) {
173
229
  }
174
230
  function formatActiveWorkFrame(frame) {
175
231
  const lines = ["## what i'm holding"];
232
+ const primaryObligation = findPrimaryOpenObligation(frame);
233
+ const activeLane = formatActiveLane(frame, primaryObligation);
234
+ const currentArtifact = formatCurrentArtifact(frame, primaryObligation);
235
+ const nextAction = formatNextAction(frame, primaryObligation);
176
236
  // Session line
177
237
  if (frame.currentSession) {
178
238
  let sessionLine = `i'm in a conversation on ${formatSessionLabel(frame.currentSession)}.`;
@@ -189,6 +249,22 @@ function formatActiveWorkFrame(frame) {
189
249
  lines.push("");
190
250
  lines.push("i'm not in a conversation right now.");
191
251
  }
252
+ if (activeLane || currentArtifact || nextAction) {
253
+ lines.push("");
254
+ lines.push("## current concrete state");
255
+ if (frame.currentSession) {
256
+ lines.push(`- live conversation: ${formatSessionLabel(frame.currentSession)}`);
257
+ }
258
+ if (activeLane) {
259
+ lines.push(`- active lane: ${activeLane}`);
260
+ }
261
+ if (currentArtifact) {
262
+ lines.push(`- current artifact: ${currentArtifact}`);
263
+ }
264
+ if (nextAction) {
265
+ lines.push(`- next action: ${nextAction}`);
266
+ }
267
+ }
192
268
  // Inner status block
193
269
  const job = frame.inner?.job;
194
270
  if (job) {
@@ -140,6 +140,12 @@ function advanceObligation(agentRoot, obligationId, update) {
140
140
  if (update.currentSurface) {
141
141
  obligation.currentSurface = update.currentSurface;
142
142
  }
143
+ if (typeof update.currentArtifact === "string") {
144
+ obligation.currentArtifact = update.currentArtifact;
145
+ }
146
+ if (typeof update.nextAction === "string") {
147
+ obligation.nextAction = update.nextAction;
148
+ }
143
149
  if (typeof update.latestNote === "string") {
144
150
  obligation.latestNote = update.latestNote;
145
151
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.findActivePersistentObligation = findActivePersistentObligation;
4
4
  exports.renderActiveObligationSteering = renderActiveObligationSteering;
5
+ exports.renderConcreteStatusGuidance = renderConcreteStatusGuidance;
5
6
  const runtime_1 = require("../nerves/runtime");
6
7
  function findActivePersistentObligation(frame) {
7
8
  if (!frame)
@@ -29,3 +30,58 @@ i'm already working on something i owe ${name}.${surfaceLine}
29
30
 
30
31
  i should close that loop before i act like this is a fresh blank turn.`;
31
32
  }
33
+ function formatActiveLane(frame, obligation) {
34
+ const liveCodingSession = frame.codingSessions?.[0];
35
+ if (liveCodingSession) {
36
+ const sameThread = frame.currentSession
37
+ && liveCodingSession.originSession
38
+ && liveCodingSession.originSession.friendId === frame.currentSession.friendId
39
+ && liveCodingSession.originSession.channel === frame.currentSession.channel
40
+ && liveCodingSession.originSession.key === frame.currentSession.key;
41
+ return sameThread
42
+ ? `${liveCodingSession.runner} ${liveCodingSession.id} for this same thread`
43
+ : liveCodingSession.originSession
44
+ ? `${liveCodingSession.runner} ${liveCodingSession.id} for ${liveCodingSession.originSession.channel}/${liveCodingSession.originSession.key}`
45
+ : `${liveCodingSession.runner} ${liveCodingSession.id}`;
46
+ }
47
+ return obligation.currentSurface?.label || "this live loop";
48
+ }
49
+ function formatCurrentArtifact(frame, obligation) {
50
+ return obligation.currentArtifact
51
+ || (obligation.currentSurface?.kind === "merge" ? obligation.currentSurface.label : "")
52
+ || ((frame.codingSessions ?? []).length > 0 ? "no PR or merge artifact yet" : "no explicit artifact yet");
53
+ }
54
+ function formatNextAction(frame, obligation) {
55
+ if (obligation.nextAction)
56
+ return obligation.nextAction;
57
+ const liveCodingSession = frame.codingSessions?.[0];
58
+ if (liveCodingSession?.status === "waiting_input") {
59
+ return `answer ${liveCodingSession.runner} ${liveCodingSession.id} and continue`;
60
+ }
61
+ if (liveCodingSession?.status === "stalled") {
62
+ return `unstick ${liveCodingSession.runner} ${liveCodingSession.id} and continue`;
63
+ }
64
+ if (liveCodingSession) {
65
+ return "finish the coding pass and bring the result back here";
66
+ }
67
+ if (obligation.status === "waiting_for_merge") {
68
+ return `wait for checks, merge ${formatCurrentArtifact(frame, obligation)}, then update runtime`;
69
+ }
70
+ if (obligation.status === "updating_runtime") {
71
+ return "update runtime, verify version/changelog, then re-observe";
72
+ }
73
+ return "continue the active loop and bring the result back here";
74
+ }
75
+ function renderConcreteStatusGuidance(frame, obligation) {
76
+ if (!obligation)
77
+ return "";
78
+ const activeLane = formatActiveLane(frame, obligation);
79
+ const currentArtifact = formatCurrentArtifact(frame, obligation);
80
+ const nextAction = formatNextAction(frame, obligation);
81
+ return `if someone asks what i'm doing or for status, i answer from the concrete state:
82
+ - active lane: ${activeLane}
83
+ - current artifact: ${currentArtifact}
84
+ - next action: ${nextAction}
85
+
86
+ i don't replace that with a broad mission statement if this concrete state is available.`;
87
+ }
@@ -470,7 +470,9 @@ function centerOfGravitySteeringSection(channel, options) {
470
470
  const activeObligation = (0, obligation_steering_1.findActivePersistentObligation)(frame);
471
471
  if (cog === "inward-work") {
472
472
  if (activeObligation) {
473
- return (0, obligation_steering_1.renderActiveObligationSteering)(activeObligation);
473
+ return `${(0, obligation_steering_1.renderActiveObligationSteering)(activeObligation)}
474
+
475
+ ${(0, obligation_steering_1.renderConcreteStatusGuidance)(frame, activeObligation)}`;
474
476
  }
475
477
  if (job?.status === "queued" || job?.status === "running") {
476
478
  const originClause = job.origin
@@ -14,6 +14,8 @@ const OBLIGATION_WAKE_UPDATE_KINDS = new Set([
14
14
  "failed",
15
15
  "killed",
16
16
  ]);
17
+ const PULL_REQUEST_NUMBER_PATTERN = /\bPR\s*#(\d+)\b/i;
18
+ const PULL_REQUEST_URL_PATTERN = /\/pull\/(\d+)(?:\b|\/)?/i;
17
19
  function clip(text, maxLength = 280) {
18
20
  const trimmed = text.trim();
19
21
  if (trimmed.length <= maxLength)
@@ -57,6 +59,61 @@ function formatSessionLabel(session) {
57
59
  : "";
58
60
  return `${session.runner} ${session.id}${origin}`;
59
61
  }
62
+ function extractPullRequestLabel(snippet) {
63
+ if (!snippet)
64
+ return null;
65
+ const numberMatch = snippet.match(PULL_REQUEST_NUMBER_PATTERN);
66
+ if (numberMatch)
67
+ return `PR #${numberMatch[1]}`;
68
+ const urlMatch = snippet.match(PULL_REQUEST_URL_PATTERN);
69
+ if (urlMatch)
70
+ return `PR #${urlMatch[1]}`;
71
+ return null;
72
+ }
73
+ function isMergedPullRequestSnippet(snippet) {
74
+ return /\bmerged\b/i.test(snippet) || /\blanded\b/i.test(snippet);
75
+ }
76
+ function deriveObligationMilestone(update) {
77
+ const snippet = pickUpdateSnippet(update);
78
+ const pullRequest = extractPullRequestLabel(snippet);
79
+ if (update.kind === "completed" && snippet && pullRequest && isMergedPullRequestSnippet(snippet)) {
80
+ return {
81
+ status: "updating_runtime",
82
+ currentSurface: { kind: "runtime", label: "ouro up" },
83
+ currentArtifact: pullRequest,
84
+ nextAction: "update runtime, verify version/changelog, then re-observe",
85
+ };
86
+ }
87
+ if (update.kind === "completed" && pullRequest) {
88
+ return {
89
+ status: "waiting_for_merge",
90
+ currentSurface: { kind: "merge", label: pullRequest },
91
+ currentArtifact: pullRequest,
92
+ nextAction: `wait for checks, merge ${pullRequest}, then update runtime`,
93
+ };
94
+ }
95
+ if (update.kind === "waiting_input") {
96
+ return {
97
+ status: "investigating",
98
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
99
+ nextAction: `answer ${update.session.runner} ${update.session.id} and continue`,
100
+ };
101
+ }
102
+ if (update.kind === "stalled") {
103
+ return {
104
+ status: "investigating",
105
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
106
+ nextAction: `unstick ${update.session.runner} ${update.session.id} and continue`,
107
+ };
108
+ }
109
+ if (update.kind === "progress" || update.kind === "spawned" || update.kind === "failed" || update.kind === "killed" || update.kind === "completed") {
110
+ return {
111
+ status: "investigating",
112
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
113
+ };
114
+ }
115
+ return null;
116
+ }
60
117
  function isSafeProgressSnippet(snippet) {
61
118
  const wordCount = snippet.split(/\s+/).filter(Boolean).length;
62
119
  return (snippet.length <= 80
@@ -94,6 +151,22 @@ function formatUpdateMessage(update) {
94
151
  return `${label} started`;
95
152
  }
96
153
  }
154
+ function formatReportBackMessage(update, baseMessage) {
155
+ if (!baseMessage)
156
+ return null;
157
+ if (!update.session.obligationId || !update.session.originSession) {
158
+ return baseMessage;
159
+ }
160
+ const milestone = deriveObligationMilestone(update);
161
+ const extraLines = [];
162
+ if (milestone?.currentArtifact) {
163
+ extraLines.push(`current artifact: ${milestone.currentArtifact}`);
164
+ }
165
+ if (milestone?.nextAction) {
166
+ extraLines.push(`next: ${milestone.nextAction}`);
167
+ }
168
+ return extraLines.length > 0 ? `${baseMessage}\n${extraLines.join("\n")}` : baseMessage;
169
+ }
97
170
  function obligationNoteFromUpdate(update) {
98
171
  const snippet = pickUpdateSnippet(update);
99
172
  switch (update.kind) {
@@ -121,10 +194,13 @@ function syncObligationFromUpdate(update) {
121
194
  const obligationId = update.session.obligationId;
122
195
  if (!obligationId)
123
196
  return;
197
+ const milestone = deriveObligationMilestone(update);
124
198
  try {
125
199
  (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), obligationId, {
126
- status: "investigating",
127
- currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
200
+ status: milestone?.status ?? "investigating",
201
+ currentSurface: milestone?.currentSurface ?? { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
202
+ currentArtifact: milestone?.currentArtifact,
203
+ nextAction: milestone?.nextAction,
128
204
  latestNote: obligationNoteFromUpdate(update) ?? undefined,
129
205
  });
130
206
  }
@@ -193,10 +269,10 @@ function attachCodingSessionFeedback(manager, session, target) {
193
269
  };
194
270
  const spawnedUpdate = { kind: "spawned", session };
195
271
  syncObligationFromUpdate(spawnedUpdate);
196
- sendMessage(formatUpdateMessage(spawnedUpdate));
272
+ sendMessage(formatReportBackMessage(spawnedUpdate, formatUpdateMessage(spawnedUpdate)));
197
273
  unsubscribe = manager.subscribe(session.id, async (update) => {
198
274
  syncObligationFromUpdate(update);
199
- sendMessage(formatUpdateMessage(update));
275
+ sendMessage(formatReportBackMessage(update, formatUpdateMessage(update)));
200
276
  await wakeInnerDialogForObligation(update);
201
277
  if (TERMINAL_UPDATE_KINDS.has(update.kind)) {
202
278
  closed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.101",
3
+ "version": "0.1.0-alpha.102",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",