@standardagents/builder 0.25.5 → 0.25.6
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/built-in-routes.js +4 -1
- package/dist/built-in-routes.js.map +1 -1
- package/dist/index.js +91 -9
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.js +91 -9
- package/dist/runtime.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -2394,8 +2394,11 @@ var init_LLMRequest = __esm({
|
|
|
2394
2394
|
*/
|
|
2395
2395
|
static async logError(state, error, errorType, modelId, startTime, existingLogId) {
|
|
2396
2396
|
try {
|
|
2397
|
-
|
|
2397
|
+
let rawErrorMessage = error instanceof Error ? error.message : String(error);
|
|
2398
2398
|
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2399
|
+
if (error instanceof Error && error.name === "AbortError" && /operation was aborted/i.test(rawErrorMessage)) {
|
|
2400
|
+
rawErrorMessage = state.abortController?.signal?.aborted ? "Aborted: the execution this request belonged to was cancelled (user stop, watchdog takeover, or a restart) \u2014 retries and fallbacks of a cancelled execution fail immediately." : "Aborted: the request's connection was cancelled mid-flight (provider disconnect or executor restart).";
|
|
2401
|
+
}
|
|
2399
2402
|
let errorMessage = rawErrorMessage;
|
|
2400
2403
|
let parsedProviderBody = null;
|
|
2401
2404
|
const trimmedRaw = rawErrorMessage.trim();
|
|
@@ -23329,6 +23332,38 @@ var AlarmQueue = class {
|
|
|
23329
23332
|
async ensureAlarmScheduled() {
|
|
23330
23333
|
await this.rescheduleAlarm();
|
|
23331
23334
|
}
|
|
23335
|
+
/**
|
|
23336
|
+
* Recover queue items orphaned in 'processing' by a dead predecessor
|
|
23337
|
+
* instance.
|
|
23338
|
+
*
|
|
23339
|
+
* processNext marks an item 'processing' before executing it. If the DO is
|
|
23340
|
+
* evicted or a deploy restarts it mid-execution, that row used to stay
|
|
23341
|
+
* 'processing' FOREVER — only 'pending' rows are ever picked up again — so
|
|
23342
|
+
* queued subagent executions, queued messages, and scheduled effects that
|
|
23343
|
+
* were mid-flight at a restart were silently lost (a subagent that "just
|
|
23344
|
+
* died half way through" with nothing to revive it).
|
|
23345
|
+
*
|
|
23346
|
+
* MUST ONLY be called from the once-per-instance wake path (DurableThread's
|
|
23347
|
+
* alarmQueueRearmed guard): at that moment this fresh instance is executing
|
|
23348
|
+
* nothing, so ANY 'processing' row provably belongs to a dead predecessor.
|
|
23349
|
+
* Calling it at any other time would re-queue items that are legitimately
|
|
23350
|
+
* mid-execution on this very instance.
|
|
23351
|
+
*/
|
|
23352
|
+
async recoverOrphanedProcessing(maxAttempts = 3) {
|
|
23353
|
+
const now = Date.now() * 1e3;
|
|
23354
|
+
await this.sql.exec(
|
|
23355
|
+
`UPDATE alarm_queue
|
|
23356
|
+
SET status = 'failed',
|
|
23357
|
+
error = 'Lost mid-execution (the executor restarted) and retry attempts were exhausted.',
|
|
23358
|
+
completed_at = ?
|
|
23359
|
+
WHERE status = 'processing' AND attempts >= ?`,
|
|
23360
|
+
now,
|
|
23361
|
+
maxAttempts
|
|
23362
|
+
);
|
|
23363
|
+
await this.sql.exec(
|
|
23364
|
+
`UPDATE alarm_queue SET status = 'pending' WHERE status = 'processing'`
|
|
23365
|
+
);
|
|
23366
|
+
}
|
|
23332
23367
|
// ============================================================
|
|
23333
23368
|
// Effect-specific Methods
|
|
23334
23369
|
// ============================================================
|
|
@@ -25251,6 +25286,9 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
25251
25286
|
}
|
|
25252
25287
|
this.migratedToVersion = LATEST_SCHEMA_VERSION;
|
|
25253
25288
|
if (!this.alarmQueueRearmed && typeof this.alarmQueue.ensureAlarmScheduled === "function") {
|
|
25289
|
+
if (typeof this.alarmQueue.recoverOrphanedProcessing === "function") {
|
|
25290
|
+
await this.alarmQueue.recoverOrphanedProcessing();
|
|
25291
|
+
}
|
|
25254
25292
|
await this.alarmQueue.ensureAlarmScheduled();
|
|
25255
25293
|
this.alarmQueueRearmed = true;
|
|
25256
25294
|
}
|
|
@@ -27956,14 +27994,14 @@ ${resultContent}${attachmentSummary}`;
|
|
|
27956
27994
|
`SELECT id FROM logs WHERE is_complete = 0`
|
|
27957
27995
|
)).toArray();
|
|
27958
27996
|
let incomplete = await readIncomplete();
|
|
27997
|
+
const ceiling = resolveWatchdogStaleCeilingMs(this.env);
|
|
27998
|
+
const silenceMs = this.lastProgressAt === 0 ? Number.POSITIVE_INFINITY : Date.now() - this.lastProgressAt;
|
|
27959
27999
|
if (this.isExecuting) {
|
|
27960
28000
|
if (incomplete.length === 0) {
|
|
27961
28001
|
this.watchdogWedgeStrikes = 0;
|
|
27962
28002
|
await rearm();
|
|
27963
28003
|
return;
|
|
27964
28004
|
}
|
|
27965
|
-
const ceiling = resolveWatchdogStaleCeilingMs(this.env);
|
|
27966
|
-
const silenceMs = Date.now() - this.lastProgressAt;
|
|
27967
28005
|
if (silenceMs <= ceiling) {
|
|
27968
28006
|
this.watchdogWedgeStrikes = 0;
|
|
27969
28007
|
await rearm();
|
|
@@ -27972,20 +28010,23 @@ ${resultContent}${attachmentSummary}`;
|
|
|
27972
28010
|
this.watchdogWedgeStrikes++;
|
|
27973
28011
|
if (this.watchdogWedgeStrikes < 2) {
|
|
27974
28012
|
console.warn(
|
|
27975
|
-
`[DurableThread] Watchdog: execution silent for ${Math.round(silenceMs / 1e3)}s (ceiling ${Math.round(ceiling / 1e3)}s) \u2014
|
|
28013
|
+
`[DurableThread] Watchdog: execution silent for ${Math.round(silenceMs / 1e3)}s (ceiling ${Math.round(ceiling / 1e3)}s) \u2014 observing; takeover next cycle if still silent`
|
|
27976
28014
|
);
|
|
27977
|
-
try {
|
|
27978
|
-
this.currentAbortController?.abort();
|
|
27979
|
-
} catch {
|
|
27980
|
-
}
|
|
27981
28015
|
await rearm();
|
|
27982
28016
|
return;
|
|
27983
28017
|
}
|
|
27984
28018
|
console.error(
|
|
27985
|
-
|
|
28019
|
+
`[DurableThread] Watchdog: wedged execution (${Math.round(silenceMs / 1e3)}s silent across two cycles) \u2014 forcing takeover`
|
|
27986
28020
|
);
|
|
28021
|
+
try {
|
|
28022
|
+
this.currentAbortController?.abort();
|
|
28023
|
+
} catch {
|
|
28024
|
+
}
|
|
27987
28025
|
this.isExecuting = false;
|
|
27988
28026
|
this.currentAbortController = null;
|
|
28027
|
+
} else if (incomplete.length > 0 && silenceMs <= ceiling) {
|
|
28028
|
+
await rearm();
|
|
28029
|
+
return;
|
|
27989
28030
|
}
|
|
27990
28031
|
incomplete = await readIncomplete();
|
|
27991
28032
|
if (this.isExecuting) {
|
|
@@ -28013,6 +28054,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28013
28054
|
console.error(
|
|
28014
28055
|
`[DurableThread] Watchdog: resume budget exhausted for thread ${threadId} \u2014 leaving thread stopped`
|
|
28015
28056
|
);
|
|
28057
|
+
await this.notifyParentOfLostExecution(threadId);
|
|
28016
28058
|
}
|
|
28017
28059
|
await this.disarmExecutionWatchdog();
|
|
28018
28060
|
} catch (error) {
|
|
@@ -28040,6 +28082,46 @@ ${resultContent}${attachmentSummary}`;
|
|
|
28040
28082
|
});
|
|
28041
28083
|
}
|
|
28042
28084
|
}
|
|
28085
|
+
/**
|
|
28086
|
+
* A subagent whose execution the watchdog gave up on leaves its parent
|
|
28087
|
+
* waiting forever for a completion message. Deliver the failure the same way
|
|
28088
|
+
* a sessionFail would, so the parent's turn wakes and can route around it.
|
|
28089
|
+
* Explicit-communication children (e.g. the compaction agent) stay quiet by
|
|
28090
|
+
* contract — only the registry status updates for those.
|
|
28091
|
+
*/
|
|
28092
|
+
async notifyParentOfLostExecution(threadId) {
|
|
28093
|
+
try {
|
|
28094
|
+
const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
|
|
28095
|
+
const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
|
|
28096
|
+
const meta = await agentBuilder.getThread(threadId);
|
|
28097
|
+
const parentThreadId = meta?.parent ?? null;
|
|
28098
|
+
if (!parentThreadId) return;
|
|
28099
|
+
const parentId = this.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
|
|
28100
|
+
const parentStub = this.env.AGENT_BUILDER_THREAD.get(parentId);
|
|
28101
|
+
const parentCommunication = await this.getParentCommunicationModeForChild(
|
|
28102
|
+
parentStub,
|
|
28103
|
+
parentThreadId,
|
|
28104
|
+
threadId
|
|
28105
|
+
);
|
|
28106
|
+
if (parentCommunication !== "explicit") {
|
|
28107
|
+
await parentStub.queueMessage(parentThreadId, {
|
|
28108
|
+
role: "user",
|
|
28109
|
+
content: `Subagent (reference: ${threadId}) has reported a failure:
|
|
28110
|
+
|
|
28111
|
+
Its execution was lost (the executor restarted or hung) and automatic recovery was exhausted. Treat this subagent's task as failed \u2014 do not wait for it; retry the work another way or report the failure.`,
|
|
28112
|
+
attachments: [],
|
|
28113
|
+
silent: true,
|
|
28114
|
+
metadata: { subagent_id: threadId }
|
|
28115
|
+
});
|
|
28116
|
+
}
|
|
28117
|
+
await parentStub.updateChildRegistryStatus(parentThreadId, threadId, "idle");
|
|
28118
|
+
} catch (error) {
|
|
28119
|
+
console.error(
|
|
28120
|
+
"[DurableThread] Watchdog: failed to notify parent of lost execution (non-fatal):",
|
|
28121
|
+
error
|
|
28122
|
+
);
|
|
28123
|
+
}
|
|
28124
|
+
}
|
|
28043
28125
|
/** At most N auto-resumes per rolling window, tracked durably. */
|
|
28044
28126
|
async consumeWatchdogResumeBudget() {
|
|
28045
28127
|
const now = Date.now();
|