opencode-auto-resume 1.0.6 → 1.0.7
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/dist/index.js +78 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -211,28 +211,36 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
211
211
|
}
|
|
212
212
|
w.continuing = true;
|
|
213
213
|
try {
|
|
214
|
-
let
|
|
215
|
-
let
|
|
216
|
-
let providerID;
|
|
214
|
+
let agent2;
|
|
215
|
+
let model2;
|
|
217
216
|
const msgResp = await ctx.client.session.messages({ path: { id: sid } });
|
|
218
217
|
const msgs = extractMessages(msgResp);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const info =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
218
|
+
for (let i = msgs.length - 1;i >= 0; i--) {
|
|
219
|
+
const msg = msgs[i];
|
|
220
|
+
const info = msg.info;
|
|
221
|
+
if (info?.role === "user") {
|
|
222
|
+
const rawAgent = info.agent;
|
|
223
|
+
if (typeof rawAgent === "string")
|
|
224
|
+
agent2 = rawAgent;
|
|
225
|
+
const rawModel = info.model;
|
|
226
|
+
if (rawModel && typeof rawModel.providerID === "string" && typeof rawModel.modelID === "string") {
|
|
227
|
+
model2 = {
|
|
228
|
+
providerID: rawModel.providerID,
|
|
229
|
+
modelID: rawModel.modelID
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
226
233
|
}
|
|
227
234
|
}
|
|
228
235
|
await ctx.client.session.prompt({
|
|
229
236
|
path: { id: sid },
|
|
230
|
-
body: {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
237
|
+
body: {
|
|
238
|
+
parts: [{ type: "text", text }],
|
|
239
|
+
agent: agent2,
|
|
240
|
+
model: model2
|
|
241
|
+
}
|
|
234
242
|
});
|
|
235
|
-
await log("debug", `${short(sid)} - prompt sent with agent: ${
|
|
243
|
+
await log("debug", `${short(sid)} - prompt sent with agent: ${agent2 ?? "(default)"}, model: ${model2 ? `${model2.providerID}/${model2.modelID}` : "(default)"}`);
|
|
236
244
|
recordContinue(sid);
|
|
237
245
|
w.lastRetryAt = Date.now();
|
|
238
246
|
} catch (err) {
|
|
@@ -241,7 +249,7 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
241
249
|
try {
|
|
242
250
|
await ctx.client.session.prompt({
|
|
243
251
|
path: { id: sid },
|
|
244
|
-
body: { parts: [{ type: "text", text }] }
|
|
252
|
+
body: { parts: [{ type: "text", text }], agent, model }
|
|
245
253
|
});
|
|
246
254
|
recordContinue(sid);
|
|
247
255
|
w.lastRetryAt = Date.now();
|
|
@@ -263,32 +271,37 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
263
271
|
return response.messages;
|
|
264
272
|
return [];
|
|
265
273
|
}
|
|
266
|
-
async function
|
|
274
|
+
async function checkSubagentStatus(parentSid) {
|
|
267
275
|
try {
|
|
268
276
|
const response = await ctx.client.session.list();
|
|
269
|
-
const
|
|
270
|
-
|
|
277
|
+
const allSessions = extractMessages(response);
|
|
278
|
+
let hasBusySubagent = false;
|
|
279
|
+
for (const s of allSessions) {
|
|
271
280
|
const sId = s.id;
|
|
272
281
|
if (!sId || sId === parentSid)
|
|
273
282
|
continue;
|
|
274
283
|
const status = s.status;
|
|
275
284
|
if (status === "busy") {
|
|
285
|
+
hasBusySubagent = true;
|
|
276
286
|
const msgResponse = await ctx.client.session.messages({ path: { id: sId } });
|
|
277
287
|
const messages = extractMessages(msgResponse);
|
|
278
288
|
const lastMsg = messages[messages.length - 1];
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return true;
|
|
289
|
+
const rawRole = lastMsg?.role ?? lastMsg?.info?.role;
|
|
290
|
+
if (lastMsg && rawRole === "assistant" && (("error" in lastMsg) || lastMsg.info && ("error" in lastMsg.info))) {
|
|
291
|
+
await log("debug", `Subagent ${short(sId)} appears crashed`);
|
|
292
|
+
return "crashed";
|
|
284
293
|
}
|
|
285
294
|
}
|
|
286
295
|
}
|
|
296
|
+
if (!hasBusySubagent) {
|
|
297
|
+
return "idle";
|
|
298
|
+
}
|
|
299
|
+
return "busy";
|
|
287
300
|
} catch (err) {
|
|
288
301
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
289
|
-
await log("debug", `
|
|
302
|
+
await log("debug", `checkSubagentStatus failed: ${errMsg}`);
|
|
303
|
+
return "unknown";
|
|
290
304
|
}
|
|
291
|
-
return false;
|
|
292
305
|
}
|
|
293
306
|
function resetSessionFlags(w) {
|
|
294
307
|
w.userCancelled = false;
|
|
@@ -328,9 +341,10 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
328
341
|
});
|
|
329
342
|
const messages = extractMessages(response);
|
|
330
343
|
const recent = messages.slice(-3);
|
|
344
|
+
let bestCandidate = null;
|
|
331
345
|
for (const msg of recent) {
|
|
332
|
-
const
|
|
333
|
-
if (
|
|
346
|
+
const rawRole = msg.role ?? msg.info?.role;
|
|
347
|
+
if (rawRole !== "assistant")
|
|
334
348
|
continue;
|
|
335
349
|
const parts = msg.parts;
|
|
336
350
|
if (!parts)
|
|
@@ -348,45 +362,44 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
348
362
|
continue;
|
|
349
363
|
}
|
|
350
364
|
if (containsToolCallAsText(text)) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
|
|
358
|
-
await tryAbortAndResume(sid, w);
|
|
359
|
-
} else {
|
|
360
|
-
try {
|
|
361
|
-
await sendContinuePrompt(sid, prompt, w);
|
|
362
|
-
await log("info", `${short(sid)} - tool-call-as-text recovery sent (attempt ${w.toolTextAttempts})`);
|
|
363
|
-
} catch (err) {
|
|
364
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
365
|
-
await log("warn", `${short(sid)} - tool-call-as-text recovery failed: ${errMsg}`);
|
|
366
|
-
}
|
|
365
|
+
const candidate = {
|
|
366
|
+
prompt: isReasoning ? THINKING_TOOL_RECOVERY_PROMPT : TOOL_TEXT_RECOVERY_PROMPT,
|
|
367
|
+
source: isReasoning ? "reasoning" : "text",
|
|
368
|
+
priority: 0
|
|
369
|
+
};
|
|
370
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
371
|
+
bestCandidate = candidate;
|
|
367
372
|
}
|
|
368
|
-
return;
|
|
369
373
|
}
|
|
370
374
|
if (containsReadyToContinuePattern(text)) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
try {
|
|
379
|
-
await sendContinuePrompt(sid, "continue", w);
|
|
380
|
-
await log("info", `${short(sid)} - ready-to-continue recovery sent (attempt ${w.toolTextAttempts})`);
|
|
381
|
-
} catch (err) {
|
|
382
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
383
|
-
await log("warn", `${short(sid)} - ready-to-continue recovery failed: ${errMsg}`);
|
|
384
|
-
}
|
|
375
|
+
const candidate = {
|
|
376
|
+
prompt: "continue",
|
|
377
|
+
source: "ready-to-continue",
|
|
378
|
+
priority: 1
|
|
379
|
+
};
|
|
380
|
+
if (!bestCandidate || candidate.priority < bestCandidate.priority) {
|
|
381
|
+
bestCandidate = candidate;
|
|
385
382
|
}
|
|
386
|
-
return;
|
|
387
383
|
}
|
|
388
384
|
}
|
|
389
385
|
}
|
|
386
|
+
if (!bestCandidate)
|
|
387
|
+
return;
|
|
388
|
+
w.toolTextRecovered = true;
|
|
389
|
+
w.toolTextAttempts++;
|
|
390
|
+
await log("info", `${bestCandidate.source} detected on ${short(sid)}! ` + `Attempt ${w.toolTextAttempts}/${maxRetries}. Sending recovery prompt...`);
|
|
391
|
+
if (isHallucinationLoop(sid)) {
|
|
392
|
+
await log("warn", `Hallucination loop detected on ${short(sid)} \u2014 aborting instead`);
|
|
393
|
+
await tryAbortAndResume(sid, w);
|
|
394
|
+
} else {
|
|
395
|
+
try {
|
|
396
|
+
await sendContinuePrompt(sid, bestCandidate.prompt, w);
|
|
397
|
+
await log("info", `${short(sid)} - ${bestCandidate.source} recovery sent (attempt ${w.toolTextAttempts})`);
|
|
398
|
+
} catch (err) {
|
|
399
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
400
|
+
await log("warn", `${short(sid)} - ${bestCandidate.source} recovery failed: ${errMsg}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
390
403
|
} catch (err) {
|
|
391
404
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
392
405
|
log("debug", `${short(sid)} - could not fetch messages: ${errMsg}`);
|
|
@@ -497,10 +510,13 @@ var AutoResumePlugin = async (ctx, options) => {
|
|
|
497
510
|
const orphanIdle = now - w.orphanWatchStartAt;
|
|
498
511
|
if (orphanIdle >= subagentWaitMs + gracePeriodMs) {
|
|
499
512
|
if (w.resumeAttempts < maxRetries) {
|
|
500
|
-
const
|
|
501
|
-
if (crashed) {
|
|
513
|
+
const subStatus = await checkSubagentStatus(sid);
|
|
514
|
+
if (subStatus === "crashed") {
|
|
502
515
|
await log("info", `Subagent crashed, triggering abort+resume on ${short(sid)}`);
|
|
503
516
|
tryAbortAndResume(sid, w);
|
|
517
|
+
} else if (subStatus === "idle") {
|
|
518
|
+
await log("info", `All subagents idle, parent ${short(sid)} stuck. Triggering abort+resume.`);
|
|
519
|
+
tryAbortAndResume(sid, w);
|
|
504
520
|
} else {
|
|
505
521
|
await log("debug", `Subagent still running, waiting...`);
|
|
506
522
|
}
|
package/package.json
CHANGED