@mjasnikovs/pi-task 0.13.15 → 0.13.16
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.
|
@@ -304,7 +304,11 @@ export async function runAutoLoop(ctx, cwd, id, deps) {
|
|
|
304
304
|
}
|
|
305
305
|
if (!res.ok) {
|
|
306
306
|
await updateTaskFrontMatter(cwd, id, { state: 'failed' });
|
|
307
|
-
|
|
307
|
+
// res.reason is set when the implementation turn itself died
|
|
308
|
+
// (e.g. a context-overflow 400) — surface it so the real cause
|
|
309
|
+
// isn't lost behind the generic "stopped" message.
|
|
310
|
+
const why = res.reason ? ` — ${res.reason.slice(0, 160)}` : '';
|
|
311
|
+
announceDone(active, `${id} stopped at "${next.title}"${why} — fix and run /task-auto-resume.`, 'error');
|
|
308
312
|
return;
|
|
309
313
|
}
|
|
310
314
|
// res.ok === true means runner.run() completed, so res.taskId is the
|
|
@@ -108,6 +108,14 @@ export interface RunSingleTaskResult {
|
|
|
108
108
|
* uninterrupted.
|
|
109
109
|
*/
|
|
110
110
|
interrupted?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Why the run is not ok, when known. Set when a waitForImplementation turn
|
|
113
|
+
* ended with stopReason "error" (the model/provider died mid-implementation —
|
|
114
|
+
* e.g. a context-overflow 400 — after the task file was already marked
|
|
115
|
+
* `completed` at spec-handoff). The /task-auto loop surfaces this in its
|
|
116
|
+
* "stopped at …" message so the real cause isn't lost. Undefined otherwise.
|
|
117
|
+
*/
|
|
118
|
+
reason?: string;
|
|
111
119
|
}
|
|
112
120
|
/**
|
|
113
121
|
* Run one prompt through the full single-task pipeline in a fresh session and
|
|
@@ -288,6 +288,29 @@ function wasInterrupted(ctx) {
|
|
|
288
288
|
}
|
|
289
289
|
return false;
|
|
290
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* The error cause when the most recent assistant turn ended with stopReason
|
|
293
|
+
* "error" — the model/provider failed mid-implementation (context overflow,
|
|
294
|
+
* disconnect, provider 5xx) after pi exhausted its own retries. Returns the
|
|
295
|
+
* provider's errorMessage, or undefined if the turn ended cleanly ("stop") or
|
|
296
|
+
* was user-aborted ("aborted", handled by wasInterrupted).
|
|
297
|
+
*
|
|
298
|
+
* The /task-auto loop reads this AFTER the implementation wait: a task's file is
|
|
299
|
+
* marked `completed` at spec-handoff (before implementation), so without this
|
|
300
|
+
* check an implementation turn that died — e.g. "400 ... exceeds the available
|
|
301
|
+
* context size" — would still read as ok and get checked off and committed.
|
|
302
|
+
*/
|
|
303
|
+
function implementationError(ctx) {
|
|
304
|
+
const entries = ctx.sessionManager.getEntries();
|
|
305
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
306
|
+
const e = entries[i];
|
|
307
|
+
if ('message' in e && 'role' in e.message && e.message.role === 'assistant') {
|
|
308
|
+
const m = e.message;
|
|
309
|
+
return m.stopReason === 'error' ? (m.errorMessage ?? 'model error') : undefined;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return undefined;
|
|
313
|
+
}
|
|
291
314
|
/**
|
|
292
315
|
* After the implementation turn settles, honour a user ESC by letting them steer.
|
|
293
316
|
*
|
|
@@ -326,6 +349,11 @@ export async function runSingleTask(ctx, cwd, rawPrompt, opts = {}) {
|
|
|
326
349
|
// cancellation path (where no replacement occurs).
|
|
327
350
|
let freshCtx = ctx;
|
|
328
351
|
let interrupted = false;
|
|
352
|
+
// The implementation turn's failure cause, when it ended with stopReason
|
|
353
|
+
// "error" (only meaningful in the waitForImplementation path). The task file
|
|
354
|
+
// was already marked `completed` at spec-handoff, so this is the only signal
|
|
355
|
+
// that the implementation itself died and the task must not be checked off.
|
|
356
|
+
let implError;
|
|
329
357
|
const result = await ctx.newSession({
|
|
330
358
|
withSession: async (newCtx) => {
|
|
331
359
|
freshCtx = newCtx;
|
|
@@ -335,6 +363,10 @@ export async function runSingleTask(ctx, cwd, rawPrompt, opts = {}) {
|
|
|
335
363
|
if (opts.waitForImplementation) {
|
|
336
364
|
await newCtx.waitForIdle();
|
|
337
365
|
interrupted = await steerUntilDone(newCtx, opts.promptSteer);
|
|
366
|
+
// A user-declined steer (interrupted) is its own paused
|
|
367
|
+
// path; otherwise inspect how the turn actually ended.
|
|
368
|
+
if (!interrupted)
|
|
369
|
+
implError = implementationError(newCtx);
|
|
338
370
|
}
|
|
339
371
|
}, opts.spawnFn, opts.onStart);
|
|
340
372
|
await runner.run();
|
|
@@ -360,13 +392,18 @@ export async function runSingleTask(ctx, cwd, rawPrompt, opts = {}) {
|
|
|
360
392
|
ok = false;
|
|
361
393
|
}
|
|
362
394
|
}
|
|
395
|
+
// The file reads `completed` from spec-handoff even when the implementation
|
|
396
|
+
// turn then died. Demote to a failure so /task-auto stops here (leaving the
|
|
397
|
+
// entry unchecked and resumable) instead of committing and advancing.
|
|
398
|
+
if (ok && implError)
|
|
399
|
+
ok = false;
|
|
363
400
|
if (opts.notifyFinish) {
|
|
364
401
|
// One push per top-level /task or /task-resume, on any terminal end. The
|
|
365
402
|
// file state is the source of truth: 'completed' on success, 'failed' /
|
|
366
403
|
// 'cancelled' otherwise; an unreadable/absent file falls back to 'ended'.
|
|
367
404
|
void pushNotify('Task finished', `${taskId || 'Task'} ${state ?? 'ended'}.`, 'pi-end').catch(() => { });
|
|
368
405
|
}
|
|
369
|
-
return { taskId, ok, sessionCancelled: false, ctx: freshCtx, interrupted };
|
|
406
|
+
return { taskId, ok, sessionCancelled: false, ctx: freshCtx, interrupted, reason: implError };
|
|
370
407
|
}
|
|
371
408
|
// ─── Command handlers ────────────────────────────────────────────────────────
|
|
372
409
|
async function handleTask(args, ctx) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.16",
|
|
4
4
|
"description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|