orchestrar 0.3.2 → 0.3.3
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/orchestrator.js +105 -16
- package/package.json +1 -1
package/orchestrator.js
CHANGED
|
@@ -50,23 +50,23 @@ async function runWorkInstance(createOpencode, docs, root) {
|
|
|
50
50
|
const sessionID = extractSessionID(session, "session.create (work instance)");
|
|
51
51
|
const promptPaths = buildPromptPaths(docs, root);
|
|
52
52
|
|
|
53
|
-
await sendPrompt(
|
|
53
|
+
const workMessageID = await sendPrompt(
|
|
54
54
|
client,
|
|
55
55
|
sessionID,
|
|
56
56
|
buildMilestonePrompt(promptPaths),
|
|
57
57
|
root
|
|
58
58
|
);
|
|
59
|
-
await
|
|
59
|
+
await waitForMessageComplete(client, sessionID, workMessageID, root);
|
|
60
60
|
|
|
61
61
|
await runReviewLoop(createOpencode, client, sessionID, root);
|
|
62
62
|
|
|
63
|
-
await sendPrompt(
|
|
63
|
+
const markTasksMessageID = await sendPrompt(
|
|
64
64
|
client,
|
|
65
65
|
sessionID,
|
|
66
66
|
buildMarkTasksPrompt(promptPaths.plan),
|
|
67
67
|
root
|
|
68
68
|
);
|
|
69
|
-
await
|
|
69
|
+
await waitForMessageComplete(client, sessionID, markTasksMessageID, root);
|
|
70
70
|
} finally {
|
|
71
71
|
await disposeInstance(client, server, root);
|
|
72
72
|
}
|
|
@@ -86,7 +86,7 @@ async function runCommitInstance(createOpencode, root) {
|
|
|
86
86
|
);
|
|
87
87
|
|
|
88
88
|
const sessionID = extractSessionID(session, "session.create (commit instance)");
|
|
89
|
-
await sendPrompt(
|
|
89
|
+
const commitMessageID = await sendPrompt(
|
|
90
90
|
client,
|
|
91
91
|
sessionID,
|
|
92
92
|
buildCommitPrompt(),
|
|
@@ -94,7 +94,7 @@ async function runCommitInstance(createOpencode, root) {
|
|
|
94
94
|
COMMIT_MODEL,
|
|
95
95
|
COMMIT_AGENT
|
|
96
96
|
);
|
|
97
|
-
await
|
|
97
|
+
await waitForMessageComplete(client, sessionID, commitMessageID, root);
|
|
98
98
|
} finally {
|
|
99
99
|
await disposeInstance(client, server, root);
|
|
100
100
|
}
|
|
@@ -118,13 +118,13 @@ async function runReviewLoop(createOpencode, client, sessionID, root) {
|
|
|
118
118
|
? reviewResult.findings.length
|
|
119
119
|
: "unknown";
|
|
120
120
|
logStep(`Review found ${findingsCount} issues; requesting fixes.`);
|
|
121
|
-
await sendPrompt(
|
|
121
|
+
const fixMessageID = await sendPrompt(
|
|
122
122
|
client,
|
|
123
123
|
sessionID,
|
|
124
124
|
buildFindingsPrompt(reviewResult),
|
|
125
125
|
root
|
|
126
126
|
);
|
|
127
|
-
await
|
|
127
|
+
await waitForMessageComplete(client, sessionID, fixMessageID, root);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
throw new Error(
|
|
@@ -171,10 +171,17 @@ async function runReviewCommand(createOpencode, root) {
|
|
|
171
171
|
"session.command"
|
|
172
172
|
);
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
const commandMessageID = extractMessageID(commandResult);
|
|
175
|
+
await waitForMessageComplete(
|
|
176
|
+
client,
|
|
177
|
+
sessionID,
|
|
178
|
+
commandMessageID,
|
|
179
|
+
root,
|
|
180
|
+
timeoutMs
|
|
181
|
+
);
|
|
175
182
|
|
|
176
183
|
let parts = commandResult?.parts ?? [];
|
|
177
|
-
const messageID = commandResult
|
|
184
|
+
const messageID = extractMessageID(commandResult);
|
|
178
185
|
if (messageID) {
|
|
179
186
|
const message = await unwrap(
|
|
180
187
|
client.session.message({
|
|
@@ -205,7 +212,7 @@ async function sendPrompt(
|
|
|
205
212
|
agentSpec = DEFAULT_AGENT
|
|
206
213
|
) {
|
|
207
214
|
const model = parseModelSpec(modelSpec);
|
|
208
|
-
await unwrap(
|
|
215
|
+
const response = await unwrap(
|
|
209
216
|
client.session.prompt({
|
|
210
217
|
path: { id: sessionID },
|
|
211
218
|
query: { directory: root },
|
|
@@ -217,6 +224,8 @@ async function sendPrompt(
|
|
|
217
224
|
}),
|
|
218
225
|
"session.prompt"
|
|
219
226
|
);
|
|
227
|
+
|
|
228
|
+
return extractMessageID(response);
|
|
220
229
|
}
|
|
221
230
|
|
|
222
231
|
async function waitForSessionIdle(client, sessionID, root, timeoutOverrideMs) {
|
|
@@ -232,26 +241,79 @@ async function waitForSessionIdle(client, sessionID, root, timeoutOverrideMs) {
|
|
|
232
241
|
);
|
|
233
242
|
|
|
234
243
|
const start = Date.now();
|
|
244
|
+
let lastKnownSessions = [];
|
|
235
245
|
while (Date.now() - start < timeoutMs) {
|
|
236
246
|
const statusMap = await unwrap(
|
|
237
247
|
client.session.status({ query: { directory: root } }),
|
|
238
248
|
"session.status"
|
|
239
249
|
);
|
|
250
|
+
if (statusMap && typeof statusMap === "object") {
|
|
251
|
+
lastKnownSessions = Object.keys(statusMap);
|
|
252
|
+
}
|
|
240
253
|
const status = statusMap?.[sessionID];
|
|
241
254
|
if (!status) {
|
|
242
|
-
|
|
243
|
-
|
|
255
|
+
await delay(pollIntervalMs);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (status.type === "idle") {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
await delay(pollIntervalMs);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const knownList = lastKnownSessions.length
|
|
265
|
+
? lastKnownSessions.join(", ")
|
|
266
|
+
: "none";
|
|
267
|
+
throw new Error(
|
|
268
|
+
`Timed out waiting for session ${sessionID} to go idle. Known sessions: ${knownList}.`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function waitForMessageComplete(
|
|
273
|
+
client,
|
|
274
|
+
sessionID,
|
|
275
|
+
messageID,
|
|
276
|
+
root,
|
|
277
|
+
timeoutOverrideMs
|
|
278
|
+
) {
|
|
279
|
+
if (!messageID) {
|
|
280
|
+
await waitForSessionIdle(client, sessionID, root, timeoutOverrideMs);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const timeoutMs =
|
|
285
|
+
timeoutOverrideMs ??
|
|
286
|
+
parseNumber(
|
|
287
|
+
process.env.ORCHESTRATOR_SESSION_TIMEOUT_MS,
|
|
288
|
+
DEFAULT_SESSION_TIMEOUT_MS
|
|
289
|
+
);
|
|
290
|
+
const pollIntervalMs = parseNumber(
|
|
291
|
+
process.env.ORCHESTRATOR_STATUS_POLL_INTERVAL_MS,
|
|
292
|
+
DEFAULT_STATUS_POLL_INTERVAL_MS
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const start = Date.now();
|
|
296
|
+
while (Date.now() - start < timeoutMs) {
|
|
297
|
+
const message = await unwrap(
|
|
298
|
+
client.session.message({
|
|
299
|
+
path: { id: sessionID, messageID },
|
|
300
|
+
query: { directory: root },
|
|
301
|
+
}),
|
|
302
|
+
"session.message"
|
|
303
|
+
);
|
|
304
|
+
const info = message?.info ?? message;
|
|
305
|
+
if (info?.error) {
|
|
244
306
|
throw new Error(
|
|
245
|
-
`Session
|
|
307
|
+
`Session message ${messageID} failed: ${formatError(info.error)}`
|
|
246
308
|
);
|
|
247
309
|
}
|
|
248
|
-
if (
|
|
310
|
+
if (info?.time?.completed || info?.finish) {
|
|
249
311
|
return;
|
|
250
312
|
}
|
|
251
313
|
await delay(pollIntervalMs);
|
|
252
314
|
}
|
|
253
315
|
|
|
254
|
-
throw new Error(`Timed out waiting for
|
|
316
|
+
throw new Error(`Timed out waiting for message ${messageID} to complete.`);
|
|
255
317
|
}
|
|
256
318
|
|
|
257
319
|
async function resolveDocs(root) {
|
|
@@ -506,6 +568,33 @@ function extractSessionID(session, context) {
|
|
|
506
568
|
);
|
|
507
569
|
}
|
|
508
570
|
|
|
571
|
+
function extractMessageID(message) {
|
|
572
|
+
if (!message || typeof message !== "object") {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const candidates = [
|
|
577
|
+
message.info?.id,
|
|
578
|
+
message.info?.messageID,
|
|
579
|
+
message.id,
|
|
580
|
+
message.messageID,
|
|
581
|
+
message.data?.info?.id,
|
|
582
|
+
message.data?.info?.messageID,
|
|
583
|
+
message.data?.id,
|
|
584
|
+
message.data?.messageID,
|
|
585
|
+
message.properties?.info?.id,
|
|
586
|
+
message.properties?.info?.messageID,
|
|
587
|
+
message.properties?.id,
|
|
588
|
+
message.properties?.messageID,
|
|
589
|
+
];
|
|
590
|
+
|
|
591
|
+
for (const candidate of candidates) {
|
|
592
|
+
if (typeof candidate === "string" && candidate.trim()) {
|
|
593
|
+
return candidate;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
509
598
|
function safeStringify(value, maxLength = 1000) {
|
|
510
599
|
try {
|
|
511
600
|
const json = JSON.stringify(value);
|