@ouro.bot/cli 0.1.0-alpha.82 → 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 +8 -0
- package/dist/heart/active-work.js +34 -1
- package/dist/heart/commitments.js +21 -1
- package/dist/heart/daemon/daemon-cli.js +10 -0
- package/dist/heart/daemon/ouro-version-manager.js +7 -0
- package/dist/heart/obligations.js +55 -5
- package/dist/mind/obligation-steering.js +31 -0
- package/dist/mind/prompt.js +11 -0
- package/dist/repertoire/coding/feedback.js +48 -2
- package/dist/repertoire/coding/manager.js +7 -0
- package/dist/repertoire/coding/tools.js +22 -0
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
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
|
+
},
|
|
4
12
|
{
|
|
5
13
|
"version": "0.1.0-alpha.82",
|
|
6
14
|
"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
|
-
|
|
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) {
|
|
@@ -1828,6 +1828,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1828
1828
|
await deps.installCliVersion(updateResult.latestVersion);
|
|
1829
1829
|
deps.activateCliVersion(updateResult.latestVersion);
|
|
1830
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
|
+
}
|
|
1831
1836
|
pendingReExec = true;
|
|
1832
1837
|
}
|
|
1833
1838
|
/* v8 ignore start -- update check error: tested via daemon-cli-update-flow.test.ts @preserve */
|
|
@@ -1873,6 +1878,11 @@ async function runOuroCli(args, deps = createDefaultOuroCliDeps()) {
|
|
|
1873
1878
|
/* v8 ignore start -- CLI update detection: tested via daemon-cli-version-detect.test.ts @preserve */
|
|
1874
1879
|
if (previousCliVersion && previousCliVersion !== currentVersion) {
|
|
1875
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
|
+
}
|
|
1876
1886
|
}
|
|
1877
1887
|
/* v8 ignore stop */
|
|
1878
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:
|
|
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(
|
|
121
|
+
return readObligations(agentRoot).filter(isOpenObligation);
|
|
111
122
|
}
|
|
112
|
-
function
|
|
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
|
|
123
|
-
|
|
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
|
+
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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",
|