@standardagents/builder 0.25.5 → 0.25.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 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
- const rawErrorMessage = error instanceof Error ? error.message : String(error);
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();
@@ -7813,6 +7816,25 @@ This usually points to something I can't fix from here \u2014 for example the wo
7813
7816
  return null;
7814
7817
  }
7815
7818
  }
7819
+ /**
7820
+ * Registry entries the model may see (and therefore manage). Hidden
7821
+ * subagent relationships (`SubagentToolConfig.hidden` — infrastructure
7822
+ * children like background compaction) are excluded from the injected
7823
+ * "Active subagent instances" message: a listed instance reads as the
7824
+ * model's to clean up, and terminate_subagent on an infrastructure child
7825
+ * kills work the runtime depends on. Static and pure for direct testing.
7826
+ */
7827
+ static filterModelVisibleSubagents(registry, promptTools) {
7828
+ if (registry.length === 0) return registry;
7829
+ const hidden = /* @__PURE__ */ new Set();
7830
+ for (const config of promptTools ?? []) {
7831
+ if (typeof config === "object" && config !== null && "name" in config && config.hidden === true) {
7832
+ hidden.add(config.name);
7833
+ }
7834
+ }
7835
+ if (hidden.size === 0) return registry;
7836
+ return registry.filter((entry) => !hidden.has(entry.name));
7837
+ }
7816
7838
  static async getSubagentRegistry(state) {
7817
7839
  if (typeof state.thread.instance.getChildrenRegistry === "function") {
7818
7840
  try {
@@ -8071,7 +8093,10 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
8071
8093
  });
8072
8094
  }
8073
8095
  }
8074
- const subagentRegistry = await this.getSubagentRegistry(state);
8096
+ const subagentRegistry = _FlowEngine.filterModelVisibleSubagents(
8097
+ await this.getSubagentRegistry(state),
8098
+ state.prompt?._tools
8099
+ );
8075
8100
  if (subagentRegistry.length > 0) {
8076
8101
  const fallbackNameMap = await this.resolveRegistryThreadNameMap(state, subagentRegistry);
8077
8102
  const lines = subagentRegistry.map((entry) => {
@@ -23329,6 +23354,38 @@ var AlarmQueue = class {
23329
23354
  async ensureAlarmScheduled() {
23330
23355
  await this.rescheduleAlarm();
23331
23356
  }
23357
+ /**
23358
+ * Recover queue items orphaned in 'processing' by a dead predecessor
23359
+ * instance.
23360
+ *
23361
+ * processNext marks an item 'processing' before executing it. If the DO is
23362
+ * evicted or a deploy restarts it mid-execution, that row used to stay
23363
+ * 'processing' FOREVER — only 'pending' rows are ever picked up again — so
23364
+ * queued subagent executions, queued messages, and scheduled effects that
23365
+ * were mid-flight at a restart were silently lost (a subagent that "just
23366
+ * died half way through" with nothing to revive it).
23367
+ *
23368
+ * MUST ONLY be called from the once-per-instance wake path (DurableThread's
23369
+ * alarmQueueRearmed guard): at that moment this fresh instance is executing
23370
+ * nothing, so ANY 'processing' row provably belongs to a dead predecessor.
23371
+ * Calling it at any other time would re-queue items that are legitimately
23372
+ * mid-execution on this very instance.
23373
+ */
23374
+ async recoverOrphanedProcessing(maxAttempts = 3) {
23375
+ const now = Date.now() * 1e3;
23376
+ await this.sql.exec(
23377
+ `UPDATE alarm_queue
23378
+ SET status = 'failed',
23379
+ error = 'Lost mid-execution (the executor restarted) and retry attempts were exhausted.',
23380
+ completed_at = ?
23381
+ WHERE status = 'processing' AND attempts >= ?`,
23382
+ now,
23383
+ maxAttempts
23384
+ );
23385
+ await this.sql.exec(
23386
+ `UPDATE alarm_queue SET status = 'pending' WHERE status = 'processing'`
23387
+ );
23388
+ }
23332
23389
  // ============================================================
23333
23390
  // Effect-specific Methods
23334
23391
  // ============================================================
@@ -25251,6 +25308,9 @@ var DurableThread = class _DurableThread extends DurableObject {
25251
25308
  }
25252
25309
  this.migratedToVersion = LATEST_SCHEMA_VERSION;
25253
25310
  if (!this.alarmQueueRearmed && typeof this.alarmQueue.ensureAlarmScheduled === "function") {
25311
+ if (typeof this.alarmQueue.recoverOrphanedProcessing === "function") {
25312
+ await this.alarmQueue.recoverOrphanedProcessing();
25313
+ }
25254
25314
  await this.alarmQueue.ensureAlarmScheduled();
25255
25315
  this.alarmQueueRearmed = true;
25256
25316
  }
@@ -27956,14 +28016,14 @@ ${resultContent}${attachmentSummary}`;
27956
28016
  `SELECT id FROM logs WHERE is_complete = 0`
27957
28017
  )).toArray();
27958
28018
  let incomplete = await readIncomplete();
28019
+ const ceiling = resolveWatchdogStaleCeilingMs(this.env);
28020
+ const silenceMs = this.lastProgressAt === 0 ? Number.POSITIVE_INFINITY : Date.now() - this.lastProgressAt;
27959
28021
  if (this.isExecuting) {
27960
28022
  if (incomplete.length === 0) {
27961
28023
  this.watchdogWedgeStrikes = 0;
27962
28024
  await rearm();
27963
28025
  return;
27964
28026
  }
27965
- const ceiling = resolveWatchdogStaleCeilingMs(this.env);
27966
- const silenceMs = Date.now() - this.lastProgressAt;
27967
28027
  if (silenceMs <= ceiling) {
27968
28028
  this.watchdogWedgeStrikes = 0;
27969
28029
  await rearm();
@@ -27972,20 +28032,23 @@ ${resultContent}${attachmentSummary}`;
27972
28032
  this.watchdogWedgeStrikes++;
27973
28033
  if (this.watchdogWedgeStrikes < 2) {
27974
28034
  console.warn(
27975
- `[DurableThread] Watchdog: execution silent for ${Math.round(silenceMs / 1e3)}s (ceiling ${Math.round(ceiling / 1e3)}s) \u2014 aborting in-flight request`
28035
+ `[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
28036
  );
27977
- try {
27978
- this.currentAbortController?.abort();
27979
- } catch {
27980
- }
27981
28037
  await rearm();
27982
28038
  return;
27983
28039
  }
27984
28040
  console.error(
27985
- "[DurableThread] Watchdog: wedged execution did not unwind after abort \u2014 forcing takeover"
28041
+ `[DurableThread] Watchdog: wedged execution (${Math.round(silenceMs / 1e3)}s silent across two cycles) \u2014 forcing takeover`
27986
28042
  );
28043
+ try {
28044
+ this.currentAbortController?.abort();
28045
+ } catch {
28046
+ }
27987
28047
  this.isExecuting = false;
27988
28048
  this.currentAbortController = null;
28049
+ } else if (incomplete.length > 0 && silenceMs <= ceiling) {
28050
+ await rearm();
28051
+ return;
27989
28052
  }
27990
28053
  incomplete = await readIncomplete();
27991
28054
  if (this.isExecuting) {
@@ -28013,6 +28076,7 @@ ${resultContent}${attachmentSummary}`;
28013
28076
  console.error(
28014
28077
  `[DurableThread] Watchdog: resume budget exhausted for thread ${threadId} \u2014 leaving thread stopped`
28015
28078
  );
28079
+ await this.notifyParentOfLostExecution(threadId);
28016
28080
  }
28017
28081
  await this.disarmExecutionWatchdog();
28018
28082
  } catch (error) {
@@ -28040,6 +28104,46 @@ ${resultContent}${attachmentSummary}`;
28040
28104
  });
28041
28105
  }
28042
28106
  }
28107
+ /**
28108
+ * A subagent whose execution the watchdog gave up on leaves its parent
28109
+ * waiting forever for a completion message. Deliver the failure the same way
28110
+ * a sessionFail would, so the parent's turn wakes and can route around it.
28111
+ * Explicit-communication children (e.g. the compaction agent) stay quiet by
28112
+ * contract — only the registry status updates for those.
28113
+ */
28114
+ async notifyParentOfLostExecution(threadId) {
28115
+ try {
28116
+ const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
28117
+ const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
28118
+ const meta = await agentBuilder.getThread(threadId);
28119
+ const parentThreadId = meta?.parent ?? null;
28120
+ if (!parentThreadId) return;
28121
+ const parentId = this.env.AGENT_BUILDER_THREAD.idFromName(parentThreadId);
28122
+ const parentStub = this.env.AGENT_BUILDER_THREAD.get(parentId);
28123
+ const parentCommunication = await this.getParentCommunicationModeForChild(
28124
+ parentStub,
28125
+ parentThreadId,
28126
+ threadId
28127
+ );
28128
+ if (parentCommunication !== "explicit") {
28129
+ await parentStub.queueMessage(parentThreadId, {
28130
+ role: "user",
28131
+ content: `Subagent (reference: ${threadId}) has reported a failure:
28132
+
28133
+ 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.`,
28134
+ attachments: [],
28135
+ silent: true,
28136
+ metadata: { subagent_id: threadId }
28137
+ });
28138
+ }
28139
+ await parentStub.updateChildRegistryStatus(parentThreadId, threadId, "idle");
28140
+ } catch (error) {
28141
+ console.error(
28142
+ "[DurableThread] Watchdog: failed to notify parent of lost execution (non-fatal):",
28143
+ error
28144
+ );
28145
+ }
28146
+ }
28043
28147
  /** At most N auto-resumes per rolling window, tracked durably. */
28044
28148
  async consumeWatchdogResumeBudget() {
28045
28149
  const now = Date.now();