@ouro.bot/cli 0.1.0-alpha.100 → 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 +14 -0
- package/dist/heart/active-work.js +101 -1
- package/dist/heart/obligations.js +6 -0
- package/dist/mind/obligation-steering.js +56 -0
- package/dist/mind/prompt.js +20 -1
- package/dist/repertoire/coding/feedback.js +80 -4
- package/dist/repertoire/coding/tools.js +41 -2
- package/dist/senses/pipeline.js +21 -0
- package/package.json +1 -1
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.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
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"version": "0.1.0-alpha.101",
|
|
13
|
+
"changes": [
|
|
14
|
+
"Live coding-session status now prefers active work from the current thread instead of resurfacing stale global history, so agents stop anchoring on ancient sessions like `coding-001` when newer coding lanes are already in flight.",
|
|
15
|
+
"Active-work rendering and prompt steering now surface same-thread live coding lanes explicitly, giving the agent a clearer center of gravity before it answers questions about ongoing work."
|
|
16
|
+
]
|
|
17
|
+
},
|
|
4
18
|
{
|
|
5
19
|
"version": "0.1.0-alpha.100",
|
|
6
20
|
"changes": [
|
|
@@ -32,6 +32,20 @@ function hasSharedObligationPressure(input) {
|
|
|
32
32
|
&& input.currentObligation.trim().length > 0) || input.mustResolveBeforeHandoff
|
|
33
33
|
|| summarizeLiveTasks(input.taskBoard).length > 0;
|
|
34
34
|
}
|
|
35
|
+
function formatCodingLaneLabel(session) {
|
|
36
|
+
return `${session.runner} ${session.id}`;
|
|
37
|
+
}
|
|
38
|
+
function describeCodingSessionScope(session, currentSession) {
|
|
39
|
+
if (!session.originSession)
|
|
40
|
+
return "";
|
|
41
|
+
if (currentSession
|
|
42
|
+
&& session.originSession.friendId === currentSession.friendId
|
|
43
|
+
&& session.originSession.channel === currentSession.channel
|
|
44
|
+
&& session.originSession.key === currentSession.key) {
|
|
45
|
+
return " for this thread";
|
|
46
|
+
}
|
|
47
|
+
return ` for ${session.originSession.channel}/${session.originSession.key}`;
|
|
48
|
+
}
|
|
35
49
|
function activeObligationCount(obligations) {
|
|
36
50
|
return (obligations ?? []).filter((ob) => (0, obligations_1.isOpenObligationStatus)(ob.status)).length;
|
|
37
51
|
}
|
|
@@ -49,6 +63,62 @@ function formatObligationSurface(obligation) {
|
|
|
49
63
|
return ` (${obligation.currentSurface.label})`;
|
|
50
64
|
}
|
|
51
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
|
+
}
|
|
52
122
|
function suggestBridgeForActiveWork(input) {
|
|
53
123
|
const targetCandidates = (input.targetCandidates ?? [])
|
|
54
124
|
.filter((candidate) => {
|
|
@@ -106,9 +176,10 @@ function buildActiveWorkFrame(input) {
|
|
|
106
176
|
const liveTaskNames = summarizeLiveTasks(input.taskBoard);
|
|
107
177
|
const activeBridgePresent = input.bridges.some(isActiveBridge);
|
|
108
178
|
const openObligations = activeObligationCount(input.pendingObligations);
|
|
179
|
+
const liveCodingSessions = input.codingSessions ?? [];
|
|
109
180
|
const centerOfGravity = activeBridgePresent
|
|
110
181
|
? "shared-work"
|
|
111
|
-
: (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff || openObligations > 0)
|
|
182
|
+
: (input.inner.status === "running" || input.inner.hasPending || input.mustResolveBeforeHandoff || openObligations > 0 || liveCodingSessions.length > 0)
|
|
112
183
|
? "inward-work"
|
|
113
184
|
: "local-turn";
|
|
114
185
|
const frame = {
|
|
@@ -127,6 +198,7 @@ function buildActiveWorkFrame(input) {
|
|
|
127
198
|
freshestForCurrentFriend: friendSessions[0] ?? null,
|
|
128
199
|
otherLiveSessionsForCurrentFriend: friendSessions,
|
|
129
200
|
},
|
|
201
|
+
codingSessions: liveCodingSessions,
|
|
130
202
|
pendingObligations: input.pendingObligations ?? [],
|
|
131
203
|
targetCandidates: input.targetCandidates ?? [],
|
|
132
204
|
bridgeSuggestion: suggestBridgeForActiveWork({
|
|
@@ -148,6 +220,7 @@ function buildActiveWorkFrame(input) {
|
|
|
148
220
|
bridges: frame.bridges.length,
|
|
149
221
|
liveTasks: frame.taskPressure.liveTaskNames.length,
|
|
150
222
|
liveSessions: frame.friendActivity.otherLiveSessionsForCurrentFriend.length,
|
|
223
|
+
codingSessions: frame.codingSessions.length,
|
|
151
224
|
pendingObligations: openObligations,
|
|
152
225
|
hasBridgeSuggestion: frame.bridgeSuggestion !== null,
|
|
153
226
|
},
|
|
@@ -156,6 +229,10 @@ function buildActiveWorkFrame(input) {
|
|
|
156
229
|
}
|
|
157
230
|
function formatActiveWorkFrame(frame) {
|
|
158
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);
|
|
159
236
|
// Session line
|
|
160
237
|
if (frame.currentSession) {
|
|
161
238
|
let sessionLine = `i'm in a conversation on ${formatSessionLabel(frame.currentSession)}.`;
|
|
@@ -172,6 +249,22 @@ function formatActiveWorkFrame(frame) {
|
|
|
172
249
|
lines.push("");
|
|
173
250
|
lines.push("i'm not in a conversation right now.");
|
|
174
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
|
+
}
|
|
175
268
|
// Inner status block
|
|
176
269
|
const job = frame.inner?.job;
|
|
177
270
|
if (job) {
|
|
@@ -205,6 +298,13 @@ function formatActiveWorkFrame(frame) {
|
|
|
205
298
|
}
|
|
206
299
|
// idle, returned, abandoned: omitted
|
|
207
300
|
}
|
|
301
|
+
if ((frame.codingSessions ?? []).length > 0) {
|
|
302
|
+
lines.push("");
|
|
303
|
+
lines.push("## live coding work");
|
|
304
|
+
for (const session of frame.codingSessions) {
|
|
305
|
+
lines.push(`- [${session.status}] ${formatCodingLaneLabel(session)}${describeCodingSessionScope(session, frame.currentSession)}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
208
308
|
// Task pressure
|
|
209
309
|
if ((frame.taskPressure?.liveTaskNames ?? []).length > 0) {
|
|
210
310
|
lines.push("");
|
|
@@ -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
|
+
}
|
package/dist/mind/prompt.js
CHANGED
|
@@ -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
|
|
@@ -494,6 +496,23 @@ if it's separate, i can be fully present here -- my inner work will wait.`;
|
|
|
494
496
|
i've been thinking privately and reached something.${originClause}
|
|
495
497
|
|
|
496
498
|
i should bring my answer back to the conversation it came from.`;
|
|
499
|
+
}
|
|
500
|
+
const liveCodingSession = frame.codingSessions?.[0];
|
|
501
|
+
if (liveCodingSession) {
|
|
502
|
+
const sameThread = frame.currentSession
|
|
503
|
+
&& liveCodingSession.originSession
|
|
504
|
+
&& liveCodingSession.originSession.friendId === frame.currentSession.friendId
|
|
505
|
+
&& liveCodingSession.originSession.channel === frame.currentSession.channel
|
|
506
|
+
&& liveCodingSession.originSession.key === frame.currentSession.key;
|
|
507
|
+
const scopeClause = sameThread
|
|
508
|
+
? " for this same thread"
|
|
509
|
+
: liveCodingSession.originSession
|
|
510
|
+
? ` for ${liveCodingSession.originSession.channel}/${liveCodingSession.originSession.key}`
|
|
511
|
+
: "";
|
|
512
|
+
return `## where my attention is
|
|
513
|
+
i already have coding work running in ${liveCodingSession.runner} ${liveCodingSession.id}${scopeClause}.
|
|
514
|
+
|
|
515
|
+
i should orient around that live lane first, then decide what still needs to come back here.`;
|
|
497
516
|
}
|
|
498
517
|
return `## where my attention is
|
|
499
518
|
i have unfinished work that needs attention before i move on.
|
|
@@ -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;
|
|
@@ -60,6 +60,45 @@ function findReusableCodingSession(sessions, request) {
|
|
|
60
60
|
const matches = sessions.filter((session) => matchesReusableCodingSession(session, request)).sort(latestSessionFirst);
|
|
61
61
|
return matches[0] ?? null;
|
|
62
62
|
}
|
|
63
|
+
function isLiveCodingStatus(status) {
|
|
64
|
+
return status === "spawning" || status === "running" || status === "waiting_input" || status === "stalled";
|
|
65
|
+
}
|
|
66
|
+
function rankCodingStatusSession(session, currentSession) {
|
|
67
|
+
return sameOriginSession({
|
|
68
|
+
friendId: currentSession.friendId,
|
|
69
|
+
channel: currentSession.channel,
|
|
70
|
+
key: currentSession.key,
|
|
71
|
+
}, session.originSession)
|
|
72
|
+
? 0
|
|
73
|
+
: 1;
|
|
74
|
+
}
|
|
75
|
+
function selectCodingStatusSessions(sessions, currentSession) {
|
|
76
|
+
if (sessions.length === 0)
|
|
77
|
+
return [];
|
|
78
|
+
if (!currentSession) {
|
|
79
|
+
return sessions;
|
|
80
|
+
}
|
|
81
|
+
const activeSessions = sessions.filter((session) => isLiveCodingStatus(session.status)).sort(latestSessionFirst);
|
|
82
|
+
if (activeSessions.length > 0) {
|
|
83
|
+
return activeSessions.sort((left, right) => {
|
|
84
|
+
const rankDelta = rankCodingStatusSession(left, currentSession) - rankCodingStatusSession(right, currentSession);
|
|
85
|
+
if (rankDelta !== 0)
|
|
86
|
+
return rankDelta;
|
|
87
|
+
return latestSessionFirst(left, right);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const matchingClosedSessions = sessions
|
|
91
|
+
.filter((session) => sameOriginSession({
|
|
92
|
+
friendId: currentSession.friendId,
|
|
93
|
+
channel: currentSession.channel,
|
|
94
|
+
key: currentSession.key,
|
|
95
|
+
}, session.originSession))
|
|
96
|
+
.sort(latestSessionFirst);
|
|
97
|
+
if (matchingClosedSessions.length > 0) {
|
|
98
|
+
return matchingClosedSessions;
|
|
99
|
+
}
|
|
100
|
+
return [...sessions].sort(latestSessionFirst);
|
|
101
|
+
}
|
|
63
102
|
const codingSpawnTool = {
|
|
64
103
|
type: "function",
|
|
65
104
|
function: {
|
|
@@ -218,12 +257,12 @@ exports.codingToolDefinitions = [
|
|
|
218
257
|
},
|
|
219
258
|
{
|
|
220
259
|
tool: codingStatusTool,
|
|
221
|
-
handler: (args) => {
|
|
260
|
+
handler: (args, ctx) => {
|
|
222
261
|
emitCodingToolEvent("coding_status");
|
|
223
262
|
const manager = (0, index_1.getCodingSessionManager)();
|
|
224
263
|
const sessionId = requireArg(args, "sessionId");
|
|
225
264
|
if (!sessionId) {
|
|
226
|
-
return JSON.stringify(manager.listSessions());
|
|
265
|
+
return JSON.stringify(selectCodingStatusSessions(manager.listSessions(), ctx?.currentSession));
|
|
227
266
|
}
|
|
228
267
|
const session = manager.getSession(sessionId);
|
|
229
268
|
if (!session)
|
package/dist/senses/pipeline.js
CHANGED
|
@@ -11,6 +11,7 @@ const continuity_1 = require("./continuity");
|
|
|
11
11
|
const manager_1 = require("../heart/bridges/manager");
|
|
12
12
|
const identity_1 = require("../heart/identity");
|
|
13
13
|
const tasks_1 = require("../repertoire/tasks");
|
|
14
|
+
const coding_1 = require("../repertoire/coding");
|
|
14
15
|
const session_activity_1 = require("../heart/session-activity");
|
|
15
16
|
const active_work_1 = require("../heart/active-work");
|
|
16
17
|
const delegation_1 = require("../heart/delegation");
|
|
@@ -186,12 +187,32 @@ async function handleInboundTurn(input) {
|
|
|
186
187
|
catch {
|
|
187
188
|
pendingObligations = [];
|
|
188
189
|
}
|
|
190
|
+
let codingSessions = [];
|
|
191
|
+
try {
|
|
192
|
+
codingSessions = (0, coding_1.getCodingSessionManager)()
|
|
193
|
+
.listSessions()
|
|
194
|
+
.filter((session) => {
|
|
195
|
+
if (session.status !== "spawning" && session.status !== "running" && session.status !== "waiting_input" && session.status !== "stalled") {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (!session.originSession) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
return session.originSession.friendId === currentSession.friendId
|
|
202
|
+
&& session.originSession.channel === currentSession.channel
|
|
203
|
+
&& session.originSession.key === currentSession.key;
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
codingSessions = [];
|
|
208
|
+
}
|
|
189
209
|
const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
|
|
190
210
|
currentSession,
|
|
191
211
|
currentObligation,
|
|
192
212
|
mustResolveBeforeHandoff,
|
|
193
213
|
inner: readInnerWorkState(),
|
|
194
214
|
bridges: activeBridges,
|
|
215
|
+
codingSessions,
|
|
195
216
|
pendingObligations,
|
|
196
217
|
taskBoard: (() => {
|
|
197
218
|
try {
|