@ouro.bot/cli 0.1.0-alpha.81 → 0.1.0-alpha.83

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,20 @@
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.83",
6
+ "changes": [
7
+ "Visible OODA loop substrate: persistent return obligations now surface where work is happening, whether the agent is still investigating, and what still needs to come back to the originating session.",
8
+ "Coding-session provenance now carries the originating human-facing session and obligation through spawn, persistence, and feedback, so hidden coding work is legible in active work instead of disappearing behind a tool session.",
9
+ "Runtime closure is explicit after self-fix updates: `ouro up` now prints a changelog follow-up command, and the runtime prompt reminds the agent to report that it updated and review what changed before treating the loop as closed."
10
+ ]
11
+ },
12
+ {
13
+ "version": "0.1.0-alpha.82",
14
+ "changes": [
15
+ "Fix: Wire default implementations for CLI update flow. `ouro up` now actually checks the npm registry for newer versions, installs them, and re-execs. Previously the update check deps were declared but never wired in createDefaultOuroCliDeps. Also wires rollback and versions command defaults."
16
+ ]
17
+ },
4
18
  {
5
19
  "version": "0.1.0-alpha.81",
6
20
  "changes": [
@@ -5,6 +5,7 @@ exports.buildActiveWorkFrame = buildActiveWorkFrame;
5
5
  exports.formatActiveWorkFrame = formatActiveWorkFrame;
6
6
  const runtime_1 = require("../nerves/runtime");
7
7
  const state_machine_1 = require("./bridges/state-machine");
8
+ const obligations_1 = require("./obligations");
8
9
  const target_resolution_1 = require("./target-resolution");
9
10
  function activityPriority(source) {
10
11
  return source === "friend-facing" ? 0 : 1;
@@ -31,6 +32,23 @@ function hasSharedObligationPressure(input) {
31
32
  && input.currentObligation.trim().length > 0) || input.mustResolveBeforeHandoff
32
33
  || summarizeLiveTasks(input.taskBoard).length > 0;
33
34
  }
35
+ function activeObligationCount(obligations) {
36
+ return (obligations ?? []).filter((ob) => (0, obligations_1.isOpenObligationStatus)(ob.status)).length;
37
+ }
38
+ function formatObligationSurface(obligation) {
39
+ if (!obligation.currentSurface?.label)
40
+ return "";
41
+ switch (obligation.status) {
42
+ case "investigating":
43
+ return ` (working in ${obligation.currentSurface.label})`;
44
+ case "waiting_for_merge":
45
+ return ` (waiting at ${obligation.currentSurface.label})`;
46
+ case "updating_runtime":
47
+ return ` (updating via ${obligation.currentSurface.label})`;
48
+ default:
49
+ return ` (${obligation.currentSurface.label})`;
50
+ }
51
+ }
34
52
  function suggestBridgeForActiveWork(input) {
35
53
  const targetCandidates = (input.targetCandidates ?? [])
36
54
  .filter((candidate) => {
@@ -87,9 +105,10 @@ function buildActiveWorkFrame(input) {
87
105
  : [];
88
106
  const liveTaskNames = summarizeLiveTasks(input.taskBoard);
89
107
  const activeBridgePresent = input.bridges.some(isActiveBridge);
108
+ const openObligations = activeObligationCount(input.pendingObligations);
90
109
  const centerOfGravity = activeBridgePresent
91
110
  ? "shared-work"
92
- : (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff)
111
+ : (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff || openObligations > 0)
93
112
  ? "inward-work"
94
113
  : "local-turn";
95
114
  const frame = {
@@ -129,6 +148,7 @@ function buildActiveWorkFrame(input) {
129
148
  bridges: frame.bridges.length,
130
149
  liveTasks: frame.taskPressure.liveTaskNames.length,
131
150
  liveSessions: frame.friendActivity.otherLiveSessionsForCurrentFriend.length,
151
+ pendingObligations: openObligations,
132
152
  hasBridgeSuggestion: frame.bridgeSuggestion !== null,
133
153
  },
134
154
  });
@@ -204,6 +224,19 @@ function formatActiveWorkFrame(frame) {
204
224
  lines.push("");
205
225
  lines.push(targetCandidatesBlock);
206
226
  }
227
+ if ((frame.pendingObligations ?? []).length > 0) {
228
+ lines.push("");
229
+ lines.push("## return obligations");
230
+ for (const obligation of frame.pendingObligations) {
231
+ if (!(0, obligations_1.isOpenObligationStatus)(obligation.status))
232
+ continue;
233
+ let obligationLine = `- [${obligation.status}] ${obligation.origin.friendId}/${obligation.origin.channel}/${obligation.origin.key}: ${obligation.content}${formatObligationSurface(obligation)}`;
234
+ if (obligation.latestNote?.trim()) {
235
+ obligationLine += `\n latest: ${obligation.latestNote.trim()}`;
236
+ }
237
+ lines.push(obligationLine);
238
+ }
239
+ }
207
240
  // Bridge suggestion
208
241
  if (frame.bridgeSuggestion) {
209
242
  lines.push("");
@@ -2,17 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.deriveCommitments = deriveCommitments;
4
4
  exports.formatCommitments = formatCommitments;
5
+ const obligations_1 = require("./obligations");
5
6
  const runtime_1 = require("../nerves/runtime");
7
+ function describeActiveObligation(obligation) {
8
+ if (obligation.status === "pending") {
9
+ return `i owe ${obligation.origin.friendId}: ${obligation.content}`;
10
+ }
11
+ const surface = obligation.currentSurface?.label;
12
+ const statusText = obligation.status.replaceAll("_", " ");
13
+ if (surface) {
14
+ return `i owe ${obligation.origin.friendId}: ${obligation.content} (${statusText} in ${surface})`;
15
+ }
16
+ return `i owe ${obligation.origin.friendId}: ${obligation.content} (${statusText})`;
17
+ }
6
18
  function deriveCommitments(activeWorkFrame, innerJob, pendingObligations) {
7
19
  const committedTo = [];
8
20
  const completionCriteria = [];
9
21
  const safeToIgnore = [];
10
22
  // Persistent obligations from the obligation store
11
23
  if (pendingObligations && pendingObligations.length > 0) {
24
+ let hasAdvancedObligation = false;
12
25
  for (const ob of pendingObligations) {
13
- committedTo.push(`i owe ${ob.origin.friendId}: ${ob.content}`);
26
+ if (!(0, obligations_1.isOpenObligationStatus)(ob.status))
27
+ continue;
28
+ committedTo.push(describeActiveObligation(ob));
29
+ if (ob.status !== "pending")
30
+ hasAdvancedObligation = true;
14
31
  }
15
32
  completionCriteria.push("fulfill my outstanding obligations");
33
+ if (hasAdvancedObligation) {
34
+ completionCriteria.push("close my active obligation loops");
35
+ }
16
36
  }
17
37
  // Obligation (from current turn -- kept for backward compat)
18
38
  if (typeof activeWorkFrame.currentObligation === "string" && activeWorkFrame.currentObligation.trim().length > 0) {
@@ -1368,6 +1368,28 @@ function createDefaultOuroCliDeps(socketPath = socket_client_1.DEFAULT_DAEMON_SO
1368
1368
  (0, ouro_version_manager_1.activateVersion)(version, {});
1369
1369
  },
1370
1370
  /* v8 ignore stop */
1371
+ /* v8 ignore start -- CLI version management defaults: integration code @preserve */
1372
+ checkForCliUpdate: async () => {
1373
+ const { checkForUpdate } = await Promise.resolve().then(() => __importStar(require("./update-checker")));
1374
+ return checkForUpdate((0, bundle_manifest_1.getPackageVersion)(), {
1375
+ fetchRegistryJson: async () => {
1376
+ const res = await fetch("https://registry.npmjs.org/@ouro.bot/cli");
1377
+ return res.json();
1378
+ },
1379
+ distTag: "alpha",
1380
+ });
1381
+ },
1382
+ installCliVersion: async (version) => { (0, ouro_version_manager_1.installVersion)(version, {}); },
1383
+ activateCliVersion: (version) => { (0, ouro_version_manager_1.activateVersion)(version, {}); },
1384
+ getCurrentCliVersion: () => (0, ouro_version_manager_1.getCurrentVersion)({}),
1385
+ getPreviousCliVersion: () => (0, ouro_version_manager_1.getPreviousVersion)({}),
1386
+ listCliVersions: () => (0, ouro_version_manager_1.listInstalledVersions)({}),
1387
+ reExecFromNewVersion: (reArgs) => {
1388
+ const entry = path.join((0, ouro_version_manager_1.getOuroCliHome)(), "CurrentVersion", "node_modules", "@ouro.bot", "cli", "dist", "heart", "daemon", "ouro-entry.js");
1389
+ require("child_process").execFileSync("node", [entry, ...reArgs], { stdio: "inherit" });
1390
+ process.exit(0);
1391
+ },
1392
+ /* v8 ignore stop */
1371
1393
  syncGlobalOuroBotWrapper: ouro_bot_global_installer_1.syncGlobalOuroBotWrapper,
1372
1394
  ensureSkillManagement: skill_management_installer_1.ensureSkillManagement,
1373
1395
  ensureDaemonBootPersistence: defaultEnsureDaemonBootPersistence,
@@ -1806,6 +1828,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
1806
1828
  await deps.installCliVersion(updateResult.latestVersion);
1807
1829
  deps.activateCliVersion(updateResult.latestVersion);
1808
1830
  deps.writeStdout(`ouro updated to ${updateResult.latestVersion} (was ${currentVersion})`);
1831
+ const changelogCommand = (0, ouro_version_manager_1.buildChangelogCommand)(currentVersion, updateResult.latestVersion);
1832
+ /* v8 ignore next -- buildChangelogCommand is non-null when an actual newer version is installed @preserve */
1833
+ if (changelogCommand) {
1834
+ deps.writeStdout(`review changes with: ${changelogCommand}`);
1835
+ }
1809
1836
  pendingReExec = true;
1810
1837
  }
1811
1838
  /* v8 ignore start -- update check error: tested via daemon-cli-update-flow.test.ts @preserve */
@@ -1851,6 +1878,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
1851
1878
  /* v8 ignore start -- CLI update detection: tested via daemon-cli-version-detect.test.ts @preserve */
1852
1879
  if (previousCliVersion && previousCliVersion !== currentVersion) {
1853
1880
  deps.writeStdout(`ouro updated to ${currentVersion} (was ${previousCliVersion})`);
1881
+ const changelogCommand = (0, ouro_version_manager_1.buildChangelogCommand)(previousCliVersion, currentVersion);
1882
+ /* v8 ignore next -- buildChangelogCommand is non-null when previous/current runtime versions differ @preserve */
1883
+ if (changelogCommand) {
1884
+ deps.writeStdout(`review changes with: ${changelogCommand}`);
1885
+ }
1854
1886
  }
1855
1887
  /* v8 ignore stop */
1856
1888
  if (updateSummary.updated.length > 0) {
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.getOuroCliHome = getOuroCliHome;
37
37
  exports.getCurrentVersion = getCurrentVersion;
38
38
  exports.getPreviousVersion = getPreviousVersion;
39
+ exports.buildChangelogCommand = buildChangelogCommand;
39
40
  exports.listInstalledVersions = listInstalledVersions;
40
41
  exports.installVersion = installVersion;
41
42
  exports.activateVersion = activateVersion;
@@ -73,6 +74,12 @@ function getPreviousVersion(deps) {
73
74
  return null;
74
75
  }
75
76
  }
77
+ function buildChangelogCommand(previousVersion, currentVersion) {
78
+ if (!previousVersion || !currentVersion || previousVersion === currentVersion) {
79
+ return null;
80
+ }
81
+ return `ouro changelog --from ${previousVersion}`;
82
+ }
76
83
  function listInstalledVersions(deps) {
77
84
  const cliHome = getOuroCliHome(deps.homeDir);
78
85
  /* v8 ignore next -- dep default: tests always inject @preserve */
@@ -33,9 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.isOpenObligationStatus = isOpenObligationStatus;
37
+ exports.isOpenObligation = isOpenObligation;
36
38
  exports.createObligation = createObligation;
37
39
  exports.readObligations = readObligations;
38
40
  exports.readPendingObligations = readPendingObligations;
41
+ exports.advanceObligation = advanceObligation;
39
42
  exports.fulfillObligation = fulfillObligation;
40
43
  exports.findPendingObligationForOrigin = findPendingObligationForOrigin;
41
44
  const fs = __importStar(require("fs"));
@@ -52,7 +55,14 @@ function generateId() {
52
55
  const random = Math.random().toString(36).slice(2, 10);
53
56
  return `${timestamp}-${random}`;
54
57
  }
58
+ function isOpenObligationStatus(status) {
59
+ return status !== "fulfilled";
60
+ }
61
+ function isOpenObligation(obligation) {
62
+ return isOpenObligationStatus(obligation.status);
63
+ }
55
64
  function createObligation(agentRoot, input) {
65
+ const now = new Date().toISOString();
56
66
  const id = generateId();
57
67
  const obligation = {
58
68
  id,
@@ -60,7 +70,8 @@ function createObligation(agentRoot, input) {
60
70
  ...(input.bridgeId ? { bridgeId: input.bridgeId } : {}),
61
71
  content: input.content,
62
72
  status: "pending",
63
- createdAt: new Date().toISOString(),
73
+ createdAt: now,
74
+ updatedAt: now,
64
75
  };
65
76
  const dir = obligationsDir(agentRoot);
66
77
  fs.mkdirSync(dir, { recursive: true });
@@ -107,9 +118,9 @@ function readObligations(agentRoot) {
107
118
  return obligations;
108
119
  }
109
120
  function readPendingObligations(agentRoot) {
110
- return readObligations(agentRoot).filter((ob) => ob.status === "pending");
121
+ return readObligations(agentRoot).filter(isOpenObligation);
111
122
  }
112
- function fulfillObligation(agentRoot, obligationId) {
123
+ function advanceObligation(agentRoot, obligationId, update) {
113
124
  const filePath = obligationFilePath(agentRoot, obligationId);
114
125
  let obligation;
115
126
  try {
@@ -119,9 +130,48 @@ function fulfillObligation(agentRoot, obligationId) {
119
130
  catch {
120
131
  return;
121
132
  }
122
- obligation.status = "fulfilled";
123
- obligation.fulfilledAt = new Date().toISOString();
133
+ const previousStatus = obligation.status;
134
+ if (update.status) {
135
+ obligation.status = update.status;
136
+ if (update.status === "fulfilled") {
137
+ obligation.fulfilledAt = new Date().toISOString();
138
+ }
139
+ }
140
+ if (update.currentSurface) {
141
+ obligation.currentSurface = update.currentSurface;
142
+ }
143
+ if (typeof update.latestNote === "string") {
144
+ obligation.latestNote = update.latestNote;
145
+ }
146
+ obligation.updatedAt = new Date().toISOString();
124
147
  fs.writeFileSync(filePath, JSON.stringify(obligation, null, 2), "utf-8");
148
+ (0, runtime_1.emitNervesEvent)({
149
+ component: "engine",
150
+ event: "engine.obligation_advanced",
151
+ message: "obligation advanced",
152
+ meta: {
153
+ obligationId,
154
+ previousStatus,
155
+ status: obligation.status,
156
+ friendId: obligation.origin.friendId,
157
+ channel: obligation.origin.channel,
158
+ key: obligation.origin.key,
159
+ surfaceKind: obligation.currentSurface?.kind ?? null,
160
+ surfaceLabel: obligation.currentSurface?.label ?? null,
161
+ },
162
+ });
163
+ }
164
+ function fulfillObligation(agentRoot, obligationId) {
165
+ advanceObligation(agentRoot, obligationId, { status: "fulfilled" });
166
+ const filePath = obligationFilePath(agentRoot, obligationId);
167
+ let obligation;
168
+ try {
169
+ const raw = fs.readFileSync(filePath, "utf-8");
170
+ obligation = JSON.parse(raw);
171
+ }
172
+ catch {
173
+ return;
174
+ }
125
175
  (0, runtime_1.emitNervesEvent)({
126
176
  component: "engine",
127
177
  event: "engine.obligation_fulfilled",
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findActivePersistentObligation = findActivePersistentObligation;
4
+ exports.renderActiveObligationSteering = renderActiveObligationSteering;
5
+ const runtime_1 = require("../nerves/runtime");
6
+ function findActivePersistentObligation(frame) {
7
+ if (!frame)
8
+ return null;
9
+ return (frame.pendingObligations ?? []).find((ob) => ob.status !== "pending" && ob.status !== "fulfilled") ?? null;
10
+ }
11
+ function renderActiveObligationSteering(obligation) {
12
+ (0, runtime_1.emitNervesEvent)({
13
+ component: "mind",
14
+ event: "mind.obligation_steering_rendered",
15
+ message: "rendered active obligation steering",
16
+ meta: {
17
+ hasObligation: Boolean(obligation),
18
+ hasSurface: Boolean(obligation?.currentSurface?.label),
19
+ },
20
+ });
21
+ if (!obligation)
22
+ return "";
23
+ const name = obligation.origin.friendId;
24
+ const surfaceLine = obligation.currentSurface?.label
25
+ ? `\nright now that work is happening in ${obligation.currentSurface.label}.`
26
+ : "";
27
+ return `## where my attention is
28
+ i'm already working on something i owe ${name}.${surfaceLine}
29
+
30
+ i should close that loop before i act like this is a fresh blank turn.`;
31
+ }
@@ -52,6 +52,7 @@ exports.buildSystem = buildSystem;
52
52
  const fs = __importStar(require("fs"));
53
53
  const path = __importStar(require("path"));
54
54
  const core_1 = require("../heart/core");
55
+ const ouro_version_manager_1 = require("../heart/daemon/ouro-version-manager");
55
56
  const tools_1 = require("../repertoire/tools");
56
57
  const skills_1 = require("../repertoire/skills");
57
58
  const identity_1 = require("../heart/identity");
@@ -65,6 +66,7 @@ const tasks_1 = require("../repertoire/tasks");
65
66
  const session_activity_1 = require("../heart/session-activity");
66
67
  const active_work_1 = require("../heart/active-work");
67
68
  const commitments_1 = require("../heart/commitments");
69
+ const obligation_steering_1 = require("./obligation-steering");
68
70
  // Lazy-loaded psyche text cache
69
71
  let _psycheCache = null;
70
72
  let _senseStatusLinesCache = null;
@@ -253,6 +255,11 @@ function runtimeInfoSection(channel) {
253
255
  const bundleMeta = readBundleMeta();
254
256
  if (bundleMeta?.previousRuntimeVersion && bundleMeta.previousRuntimeVersion !== currentVersion) {
255
257
  lines.push(`previously: ${bundleMeta.previousRuntimeVersion}`);
258
+ const changelogCommand = (0, ouro_version_manager_1.buildChangelogCommand)(bundleMeta.previousRuntimeVersion, currentVersion);
259
+ /* v8 ignore next -- buildChangelogCommand is non-null when previous/current runtime versions differ @preserve */
260
+ if (changelogCommand) {
261
+ lines.push(`if i'm closing a self-fix loop, i should tell them i updated and review changes with \`${changelogCommand}\`.`);
262
+ }
256
263
  }
257
264
  lines.push(`changelog available at: ${(0, bundle_manifest_1.getChangelogPath)()}`);
258
265
  lines.push(`cwd: ${process.cwd()}`);
@@ -460,7 +467,11 @@ function centerOfGravitySteeringSection(channel, options) {
460
467
  if (cog === "local-turn")
461
468
  return "";
462
469
  const job = frame.inner?.job;
470
+ const activeObligation = (0, obligation_steering_1.findActivePersistentObligation)(frame);
463
471
  if (cog === "inward-work") {
472
+ if (activeObligation) {
473
+ return (0, obligation_steering_1.renderActiveObligationSteering)(activeObligation);
474
+ }
464
475
  if (job?.status === "queued" || job?.status === "running") {
465
476
  const originClause = job.origin
466
477
  ? ` ${job.origin.friendName ?? job.origin.friendId} asked about something and i wanted to give it real thought before responding.`
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatCodingTail = formatCodingTail;
4
4
  exports.attachCodingSessionFeedback = attachCodingSessionFeedback;
5
+ const identity_1 = require("../../heart/identity");
6
+ const obligations_1 = require("../../heart/obligations");
5
7
  const runtime_1 = require("../../nerves/runtime");
6
8
  const TERMINAL_UPDATE_KINDS = new Set(["completed", "failed", "killed"]);
7
9
  function clip(text, maxLength = 280) {
@@ -42,7 +44,10 @@ function lastMeaningfulLine(text) {
42
44
  return clip(lines.at(-1));
43
45
  }
44
46
  function formatSessionLabel(session) {
45
- return `${session.runner} ${session.id}`;
47
+ const origin = session.originSession
48
+ ? ` for ${session.originSession.channel}/${session.originSession.key}`
49
+ : "";
50
+ return `${session.runner} ${session.id}${origin}`;
46
51
  }
47
52
  function isSafeProgressSnippet(snippet) {
48
53
  const wordCount = snippet.split(/\s+/).filter(Boolean).length;
@@ -81,6 +86,44 @@ function formatUpdateMessage(update) {
81
86
  return `${label} started`;
82
87
  }
83
88
  }
89
+ function obligationNoteFromUpdate(update) {
90
+ const snippet = pickUpdateSnippet(update);
91
+ switch (update.kind) {
92
+ case "spawned":
93
+ return update.session.originSession
94
+ ? `coding session started for ${update.session.originSession.channel}/${update.session.originSession.key}`
95
+ : "coding session started";
96
+ case "progress":
97
+ return snippet ? `coding session progress: ${snippet}` : null;
98
+ case "waiting_input":
99
+ return snippet ? `coding session waiting: ${snippet}` : "coding session waiting for input";
100
+ case "stalled":
101
+ return snippet ? `coding session stalled: ${snippet}` : "coding session stalled";
102
+ case "completed":
103
+ return snippet
104
+ ? `coding session completed: ${snippet}; merge/update still pending`
105
+ : "coding session completed; merge/update still pending";
106
+ case "failed":
107
+ return snippet ? `coding session failed: ${snippet}` : "coding session failed";
108
+ case "killed":
109
+ return "coding session killed";
110
+ }
111
+ }
112
+ function syncObligationFromUpdate(update) {
113
+ const obligationId = update.session.obligationId;
114
+ if (!obligationId)
115
+ return;
116
+ try {
117
+ (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), obligationId, {
118
+ status: "investigating",
119
+ currentSurface: { kind: "coding", label: `${update.session.runner} ${update.session.id}` },
120
+ latestNote: obligationNoteFromUpdate(update) ?? undefined,
121
+ });
122
+ }
123
+ catch {
124
+ // Detached feedback should still reach the human even if obligation sync is unavailable.
125
+ }
126
+ }
84
127
  function formatCodingTail(session) {
85
128
  const stdout = session.stdoutTail.trim() || "(empty)";
86
129
  const stderr = session.stderrTail.trim() || "(empty)";
@@ -119,8 +162,11 @@ function attachCodingSessionFeedback(manager, session, target) {
119
162
  });
120
163
  });
121
164
  };
122
- sendMessage(formatUpdateMessage({ kind: "spawned", session }));
165
+ const spawnedUpdate = { kind: "spawned", session };
166
+ syncObligationFromUpdate(spawnedUpdate);
167
+ sendMessage(formatUpdateMessage(spawnedUpdate));
123
168
  unsubscribe = manager.subscribe(session.id, async (update) => {
169
+ syncObligationFromUpdate(update);
124
170
  sendMessage(formatUpdateMessage(update));
125
171
  if (TERMINAL_UPDATE_KINDS.has(update.kind)) {
126
172
  closed = true;
@@ -62,6 +62,7 @@ function isPidAlive(pid) {
62
62
  function cloneSession(session) {
63
63
  return {
64
64
  ...session,
65
+ originSession: session.originSession ? { ...session.originSession } : undefined,
65
66
  stdoutTail: session.stdoutTail,
66
67
  stderrTail: session.stderrTail,
67
68
  failure: session.failure
@@ -157,6 +158,8 @@ class CodingSessionManager {
157
158
  runner: normalizedRequest.runner,
158
159
  workdir: normalizedRequest.workdir,
159
160
  taskRef: normalizedRequest.taskRef,
161
+ originSession: normalizedRequest.originSession ? { ...normalizedRequest.originSession } : undefined,
162
+ obligationId: normalizedRequest.obligationId,
160
163
  scopeFile: normalizedRequest.scopeFile,
161
164
  stateFile: normalizedRequest.stateFile,
162
165
  status: "spawning",
@@ -482,12 +485,16 @@ class CodingSessionManager {
482
485
  }
483
486
  const normalizedRequest = {
484
487
  ...request,
488
+ originSession: request.originSession ? { ...request.originSession } : undefined,
485
489
  sessionId: request.sessionId ?? session.id,
490
+ obligationId: request.obligationId,
486
491
  parentAgent: request.parentAgent ?? this.agentName,
487
492
  };
488
493
  const normalizedSession = {
489
494
  ...session,
490
495
  taskRef: session.taskRef ?? normalizedRequest.taskRef,
496
+ originSession: session.originSession ?? normalizedRequest.originSession,
497
+ obligationId: session.obligationId ?? normalizedRequest.obligationId,
491
498
  failure: session.failure ?? null,
492
499
  stdoutTail: session.stdoutTail ?? session.failure?.stdoutTail ?? "",
493
500
  stderrTail: session.stderrTail ?? session.failure?.stderrTail ?? "",
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.codingToolDefinitions = void 0;
4
4
  const index_1 = require("./index");
5
+ const identity_1 = require("../../heart/identity");
6
+ const obligations_1 = require("../../heart/obligations");
5
7
  const runtime_1 = require("../../nerves/runtime");
6
8
  const RUNNERS = ["claude", "codex"];
7
9
  function requireArg(args, key) {
@@ -130,6 +132,17 @@ exports.codingToolDefinitions = [
130
132
  prompt,
131
133
  taskRef,
132
134
  };
135
+ if (ctx?.currentSession && ctx.currentSession.channel !== "inner") {
136
+ request.originSession = {
137
+ friendId: ctx.currentSession.friendId,
138
+ channel: ctx.currentSession.channel,
139
+ key: ctx.currentSession.key,
140
+ };
141
+ const obligation = (0, obligations_1.findPendingObligationForOrigin)((0, identity_1.getAgentRoot)(), request.originSession);
142
+ if (obligation) {
143
+ request.obligationId = obligation.id;
144
+ }
145
+ }
133
146
  const scopeFile = optionalArg(args, "scopeFile");
134
147
  if (scopeFile)
135
148
  request.scopeFile = scopeFile;
@@ -138,6 +151,15 @@ exports.codingToolDefinitions = [
138
151
  request.stateFile = stateFile;
139
152
  const manager = (0, index_1.getCodingSessionManager)();
140
153
  const session = await manager.spawnSession(request);
154
+ if (session.obligationId) {
155
+ (0, obligations_1.advanceObligation)((0, identity_1.getAgentRoot)(), session.obligationId, {
156
+ status: "investigating",
157
+ currentSurface: { kind: "coding", label: `${session.runner} ${session.id}` },
158
+ latestNote: session.originSession
159
+ ? `coding session started for ${session.originSession.channel}/${session.originSession.key}`
160
+ : "coding session started",
161
+ });
162
+ }
141
163
  if (args.runner === "codex" && args.taskRef) {
142
164
  (0, runtime_1.emitNervesEvent)({
143
165
  component: "repertoire",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.81",
3
+ "version": "0.1.0-alpha.83",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",