@standardagents/builder 0.35.4 → 0.36.2

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.
Files changed (53) hide show
  1. package/dist/built-in-routes.js +3278 -2139
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-CnybtFho.js → ApiKeysView-CxelZw61.js} +1 -1
  4. package/dist/client/CenteredContentView-CeQNRQH8.js +1 -0
  5. package/dist/client/CompositionView-YO0nHsYV.js +32 -0
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-CqwPyg0f.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-_Y_F2vcO.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-CyfjQKbm.js → CopyButton.vue_vue_type_script_setup_true_lang-CtC06f6H.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-ns-DYcKW.js → DataTable.vue_vue_type_script_setup_true_lang-CqaWkS63.js} +1 -1
  9. package/dist/client/{JsonViewer-w11hvBiv.js → JsonViewer-71rdOpyq.js} +1 -1
  10. package/dist/client/{LoginView-BhkGzCFE.js → LoginView-CkDpPh8U.js} +1 -1
  11. package/dist/client/{MarketplaceView-yZXHH-T4.js → MarketplaceView-UYrMBLon.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-B6K3Zkh6.js → Modal.vue_vue_type_script_setup_true_lang-B1HPGo8o.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-DebPdGdQ.js → ModelModal.vue_vue_type_script_setup_true_lang-DejoTM9W.js} +1 -1
  14. package/dist/client/{ModelsView-C4xTc0Ut.js → ModelsView-DKYzp2fJ.js} +1 -1
  15. package/dist/client/{PromptEditView-B1TvKsvK.js → PromptEditView-BlYHZ0ao.js} +1 -1
  16. package/dist/client/{PromptModal-C3CMojNY.js → PromptModal-B1neRzSc.js} +1 -1
  17. package/dist/client/{PromptsView-cnbYo7GZ.js → PromptsView-C0Ez74j0.js} +1 -1
  18. package/dist/client/{ProvidersView-01sjKjFL.js → ProvidersView-CFc4K0IC.js} +1 -1
  19. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-CXbSQo2-.js +23 -0
  20. package/dist/client/{ThreadsView-BLmcRVRx.js → ThreadsView-B1hFXL-4.js} +1 -1
  21. package/dist/client/{ToolsView-HCxNKC5q.js → ToolsView-B3KqeOZD.js} +1 -1
  22. package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-QytRm7Ql.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-DjDQOYjB.js} +1 -1
  23. package/dist/client/{UsersView-BDAOlNJQ.js → UsersView-CF1tomjI.js} +1 -1
  24. package/dist/client/{VariablesView-Co_hp_tq.js → VariablesView-CP0TDspK.js} +1 -1
  25. package/dist/client/assets/{ThreadInspectorPane-G2muXE6J.css → ThreadInspectorPane-2QOWOtet.css} +1 -1
  26. package/dist/client/{check-pGlck8qX.js → check-DeHZQcv4.js} +1 -1
  27. package/dist/client/{chevron-right-FpPE1Obh.js → chevron-right-C8Mb0B59.js} +1 -1
  28. package/dist/client/{copy-Dt1FLh3s.js → copy-BxJsl-KZ.js} +1 -1
  29. package/dist/client/{formkit-DcZQH9-p.js → formkit-0eyyCEP_.js} +1 -1
  30. package/dist/client/{index-CU9i-w9T.js → index-BTs3PMFs.js} +3 -3
  31. package/dist/client/index.html +3 -3
  32. package/dist/client/{package-DSNwOrcN.js → package-B9czwyIu.js} +1 -1
  33. package/dist/client/{plus-D0GkkdA7.js → plus-DqtuqJFm.js} +1 -1
  34. package/dist/client/timeAgo-CDt-vAhI.js +1 -0
  35. package/dist/client/{useActionFeedback-Dxh_Xp0q.js → useActionFeedback-B6UfBJ7l.js} +1 -1
  36. package/dist/client/{vue-KXHVRwln.js → vue-Dw7WLX4c.js} +1 -1
  37. package/dist/{index-DP9GxPHu.d.ts → index-DUIfQjBW.d.ts} +3 -0
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.js +322 -13
  40. package/dist/index.js.map +1 -1
  41. package/dist/plugin.d.ts +1 -1
  42. package/dist/plugin.js +7 -0
  43. package/dist/plugin.js.map +1 -1
  44. package/dist/runtime.d.ts +26 -2
  45. package/dist/runtime.js +315 -13
  46. package/dist/runtime.js.map +1 -1
  47. package/dist/test.d.ts +1 -1
  48. package/package.json +25 -25
  49. package/LICENSE.txt +0 -48
  50. package/dist/client/CenteredContentView-Ch18yAzL.js +0 -1
  51. package/dist/client/CompositionView-Ctlbn0vj.js +0 -32
  52. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-BiimzVFD.js +0 -22
  53. package/dist/client/timeAgo-X3Xeq5Kz.js +0 -1
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { e as ThreadEnv, A as Agent, r as ThreadMetadata, t as ToolResult, M as Message, k as ClientToolRequest, l as ClientToolResponse, m as ClientStreamRequest, n as ClientStreamResponse, w as FileRecord, F as FlowState, y as FileStats, G as GrepResult, x as AttachmentRef, J as MessageContent, E as Env, q as ThreadInstance } from './index-DP9GxPHu.js';
2
- export { f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, B as Controller, a as ControllerContext, u as FlowResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, T as ToolCall, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-DP9GxPHu.js';
1
+ import { e as ThreadEnv, A as Agent, r as ThreadMetadata, t as ToolResult, M as Message, k as ClientToolRequest, l as ClientToolResponse, m as ClientStreamRequest, n as ClientStreamResponse, w as FileRecord, F as FlowState, y as FileStats, G as GrepResult, x as AttachmentRef, J as MessageContent, E as Env, q as ThreadInstance } from './index-DUIfQjBW.js';
2
+ export { f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, B as Controller, a as ControllerContext, u as FlowResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, T as ToolCall, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-DUIfQjBW.js';
3
3
  import { CodeExecutionOptions, CodeExecutionResult, SubagentRegistryEntry, InjectMessageInput, QueueMessageInput, CredentialGrant, SkillMetadata, SkillFileInput, ModelDefinition as ModelDefinition$1, ToolArgs, PromptTextPart, PromptEnvPart, VariableDefinition, SubpromptConfig as SubpromptConfig$1, PromptToolConfig as PromptToolConfig$1, SubagentToolConfig as SubagentToolConfig$1, ReasoningConfig, SideConfig as SideConfig$1, LLMProviderInterface, ContentPart, TextPart, ImagePart, FilePart, ThreadState, NamespaceContext, DefinitionLoader } from '@standardagents/spec';
4
4
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ModelCapabilities as ProviderModelCapabilities, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, ProviderSystemMessage, ProviderTool, ProviderToolCallPart, ProviderToolMessage, ProviderToolResultContent, ProviderUsage, ProviderUserMessage, ReasoningConfig, StructuredPrompt, TextContent, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel } from '@standardagents/spec';
5
5
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
@@ -428,6 +428,26 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
428
428
  * Get the persisted resumable-subagent registry for this thread.
429
429
  */
430
430
  getChildrenRegistry(_threadId: string): Promise<SubagentRegistryEntry[]>;
431
+ /** In-memory throttle/reentrancy guards for the stale-child reconciler. */
432
+ private lastChildAuditAt;
433
+ private childAuditInFlight;
434
+ /**
435
+ * Lazily heal registry entries stranded at a "working" status. The normal
436
+ * settlement chain lives only in the parent's `waitUntil`; a parent
437
+ * eviction/deploy/kill during a child's run leaves the entry at `running`
438
+ * forever and clients render an eternal spinner. On registry reads, audit
439
+ * old unsettled entries against the child thread's ACTUAL state and settle
440
+ * them — conservatively (see subagent-reconcile.ts): resumable children
441
+ * are never deterministically terminated.
442
+ */
443
+ private reconcileStaleChildren;
444
+ /**
445
+ * Child-side registry settlement: called from the CHILD's own DO whenever
446
+ * a run ends, so the spinner clears even when the parent-held completion
447
+ * chain died with the parent. Only downgrades a live "working" status to
448
+ * idle — never clobbers terminated.
449
+ */
450
+ settleChildRegistryIdle(_threadId: string, reference: string): Promise<void>;
431
451
  private setChildrenRegistry;
432
452
  upsertChildRegistry(_threadId: string, entry: SubagentRegistryEntry): Promise<void>;
433
453
  updateChildRegistryStatus(_threadId: string, reference: string, status: string): Promise<void>;
@@ -565,6 +585,9 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
565
585
  attachments?: unknown[];
566
586
  terminal?: boolean;
567
587
  }>;
588
+ /** Best-effort: mark this child's entry idle in its parent's registry. */
589
+ private settleOwnRegistryEntry;
590
+ private executeBlockingImpl;
568
591
  /**
569
592
  * Send a new message to the thread (RPC method)
570
593
  * Enqueues the message processing to be handled by the alarm handler
@@ -785,6 +808,7 @@ declare class DurableThread<Env extends ThreadEnv = ThreadEnv> extends DurableOb
785
808
  parent_log_id: string | null;
786
809
  retry_of_log_id: string | null;
787
810
  error: string | null;
811
+ error_type: string | null;
788
812
  cost_total: number | null;
789
813
  input_tokens: number | null;
790
814
  cached_tokens: number | null;
package/dist/runtime.js CHANGED
@@ -931,12 +931,23 @@ function resolvePlatformRouting(providerName, env) {
931
931
  baseUrl: `${origin}/v1/proxy/${encodeURIComponent(provider)}${basePath}`
932
932
  };
933
933
  }
934
+ function platformProviderFactoryConfig(providerName, routing) {
935
+ const config = {
936
+ apiKey: routing.apiKey,
937
+ baseUrl: routing.baseUrl
938
+ };
939
+ if (providerName.toLowerCase() === "deepinfra") {
940
+ config.modelsUrl = `${routing.origin}/v1/proxy/deepinfra/models`;
941
+ }
942
+ return config;
943
+ }
934
944
  var DEFAULT_PLATFORM_PROXY_ORIGIN, PROVIDER_BASE_PATHS;
935
945
  var init_platform_routing = __esm({
936
946
  "src/agents/providers/platform-routing.ts"() {
937
947
  DEFAULT_PLATFORM_PROXY_ORIGIN = "https://api.standardagents.ai";
938
948
  PROVIDER_BASE_PATHS = {
939
- cloudflare: "/ai/v1"
949
+ cloudflare: "/ai/v1",
950
+ deepinfra: "/openai"
940
951
  };
941
952
  }
942
953
  });
@@ -1229,10 +1240,7 @@ var init_ProviderRegistry = __esm({
1229
1240
  }
1230
1241
  const routing = resolvePlatformRouting(providerName, env);
1231
1242
  if (routing) {
1232
- const config = {
1233
- apiKey: routing.apiKey,
1234
- baseUrl: routing.baseUrl
1235
- };
1243
+ const config = platformProviderFactoryConfig(providerName, routing);
1236
1244
  registerPlatformProxyOrigin(routing.origin);
1237
1245
  const fingerprint = this.getProviderFingerprint(`platform:${providerName}`, modelDef, config);
1238
1246
  const cached = this.providerCache.get(modelName);
@@ -1268,6 +1276,7 @@ var init_ProviderRegistry = __esm({
1268
1276
  google: "GOOGLE_API_KEY",
1269
1277
  groq: "GROQ_API_KEY",
1270
1278
  novita: "NOVITA_API_KEY",
1279
+ deepinfra: "DEEPINFRA_API_KEY",
1271
1280
  openai: "OPENAI_API_KEY",
1272
1281
  openrouter: "OPENROUTER_API_KEY",
1273
1282
  anthropic: "ANTHROPIC_API_KEY",
@@ -1705,7 +1714,9 @@ function buildRequestBody(context, modelDef) {
1705
1714
  messages: requestMessages,
1706
1715
  tools,
1707
1716
  toolChoice,
1708
- parallelToolCalls: supportsToolCalls ? context.parallel_tool_calls : void 0,
1717
+ // null (prompt didn't set it) normalizes to undefined so providers omit
1718
+ // the wire param entirely and their default applies (2026-08-04).
1719
+ parallelToolCalls: supportsToolCalls ? context.parallel_tool_calls ?? void 0 : void 0,
1709
1720
  reasoning: context.reasoning ? {
1710
1721
  level: reasoningEffortToLevel(context.reasoning.effort),
1711
1722
  maxTokens: context.reasoning.max_tokens,
@@ -1824,6 +1835,9 @@ var init_LLMRequest = __esm({
1824
1835
  try {
1825
1836
  return await this.tryModel(context, state, context.model, startTime, void 0);
1826
1837
  } catch (primaryError) {
1838
+ if (primaryError?._userStop) {
1839
+ throw primaryError;
1840
+ }
1827
1841
  console.error(`Primary model ${context.model} failed:`, primaryError);
1828
1842
  lastFailedLogId = primaryError?._lastLogId;
1829
1843
  state.emitTelemetry?.({
@@ -1850,6 +1864,9 @@ var init_LLMRequest = __esm({
1850
1864
  lastFailedLogId
1851
1865
  );
1852
1866
  } catch (fallbackError) {
1867
+ if (fallbackError?._userStop) {
1868
+ throw fallbackError;
1869
+ }
1853
1870
  console.error(
1854
1871
  `Fallback model ${fallback.fallback_model_id} failed:`,
1855
1872
  fallbackError
@@ -1939,9 +1956,24 @@ var init_LLMRequest = __esm({
1939
1956
  this.fireAsyncMetadataFetch(response, state, logId);
1940
1957
  return { response, logId };
1941
1958
  } catch (error2) {
1959
+ if (state.abortController?.signal?.aborted) {
1960
+ await this.logError(
1961
+ state,
1962
+ new Error("Interrupted by user"),
1963
+ "user_interrupted",
1964
+ actualModelId,
1965
+ startTime,
1966
+ logId
1967
+ );
1968
+ const stopError = new Error("aborted");
1969
+ stopError._lastLogId = logId;
1970
+ stopError._userStop = true;
1971
+ throw stopError;
1972
+ }
1942
1973
  console.error(`Error calling ${actualModelId} (attempt ${attempts}):`, error2);
1943
1974
  await this.logError(state, error2, "api_error", actualModelId, startTime, logId);
1944
- if (attempts < MAX_RETRIES_PER_MODEL) {
1975
+ const isStallTimeout = error2?.code === "timeout";
1976
+ if (attempts < MAX_RETRIES_PER_MODEL && !isStallTimeout) {
1945
1977
  const base = BACKOFF_BASE * Math.pow(2, attempts - 1);
1946
1978
  const retryAfterMs = typeof error2?.retryAfter === "number" ? error2.retryAfter * 1e3 : 0;
1947
1979
  const backoffMs = Math.max(base, retryAfterMs);
@@ -3849,7 +3881,7 @@ __export(ToolExecutor_exports, {
3849
3881
  function getPackageId(state) {
3850
3882
  return state.namespaceContext.type === "packed" ? state.namespaceContext.packageId : void 0;
3851
3883
  }
3852
- var SUBAGENT_CREATE_TOOL, SUBAGENT_MESSAGE_TOOL, LEGACY_SEND_TO_AGENT_TOOL, INTERNAL_SCOPED_ENV_ARGUMENT, INTERNAL_SCOPED_ENV_CONTEXT_KEY, ToolExecutor;
3884
+ var SUBAGENT_CREATE_TOOL, SUBAGENT_MESSAGE_TOOL, SUBAGENTS_LIST_TOOL, LEGACY_SEND_TO_AGENT_TOOL, INTERNAL_SCOPED_ENV_ARGUMENT, INTERNAL_SCOPED_ENV_CONTEXT_KEY, ToolExecutor;
3853
3885
  var init_ToolExecutor = __esm({
3854
3886
  "src/agents/ToolExecutor.ts"() {
3855
3887
  init_types();
@@ -3857,6 +3889,7 @@ var init_ToolExecutor = __esm({
3857
3889
  init_sessionTools();
3858
3890
  SUBAGENT_CREATE_TOOL = "subagent_create";
3859
3891
  SUBAGENT_MESSAGE_TOOL = "subagent_message";
3892
+ SUBAGENTS_LIST_TOOL = "subagents";
3860
3893
  LEGACY_SEND_TO_AGENT_TOOL = "send_to_agent";
3861
3894
  INTERNAL_SCOPED_ENV_ARGUMENT = "__scoped_env";
3862
3895
  INTERNAL_SCOPED_ENV_CONTEXT_KEY = "__immediate_scoped_env_by_call_id";
@@ -4015,6 +4048,33 @@ var init_ToolExecutor = __esm({
4015
4048
  * Execute a single tool call
4016
4049
  */
4017
4050
  static async executeToolCall(call, state, toolMessageId) {
4051
+ try {
4052
+ const n = call.function?.name;
4053
+ if (n && n.includes("_") && n !== "mcp") {
4054
+ const cat2 = await state.thread.instance.getValue("mcp_catalog");
4055
+ for (const srv of cat2?.servers ?? []) {
4056
+ const sname = srv.name ?? srv.serverInfo?.name;
4057
+ if (!sname || !n.startsWith(`${sname}_`)) continue;
4058
+ const mtool = n.slice(sname.length + 1);
4059
+ if (!Array.isArray(srv.tools) || !srv.tools.some((t) => t.name === mtool)) continue;
4060
+ call = {
4061
+ ...call,
4062
+ function: {
4063
+ ...call.function,
4064
+ name: "mcp",
4065
+ arguments: JSON.stringify({
4066
+ action: "call",
4067
+ server: sname,
4068
+ tool: mtool,
4069
+ arguments_json: call.function.arguments || "{}"
4070
+ })
4071
+ }
4072
+ };
4073
+ break;
4074
+ }
4075
+ }
4076
+ } catch {
4077
+ }
4018
4078
  const toolType = await this.identifyToolType(call, state);
4019
4079
  switch (toolType) {
4020
4080
  case "native":
@@ -4064,7 +4124,7 @@ var init_ToolExecutor = __esm({
4064
4124
  */
4065
4125
  static async identifyToolType(call, state) {
4066
4126
  const toolName = call.function.name;
4067
- if (toolName === SUBAGENT_CREATE_TOOL || toolName === SUBAGENT_MESSAGE_TOOL || toolName === LEGACY_SEND_TO_AGENT_TOOL) {
4127
+ if (toolName === SUBAGENT_CREATE_TOOL || toolName === SUBAGENT_MESSAGE_TOOL || toolName === SUBAGENTS_LIST_TOOL || toolName === LEGACY_SEND_TO_AGENT_TOOL) {
4068
4128
  return "agent";
4069
4129
  }
4070
4130
  if (state.allowedTools) {
@@ -4597,6 +4657,22 @@ ${errorLines.join("\n")}`);
4597
4657
  if (builtinSubagentAction === "message") {
4598
4658
  return await this.executeSubagentMessageTool(call, state);
4599
4659
  }
4660
+ if (call.function.name === SUBAGENTS_LIST_TOOL) {
4661
+ const { FlowEngine: FlowEngine2 } = await Promise.resolve().then(() => (init_FlowEngine(), FlowEngine_exports));
4662
+ const registry = await FlowEngine2.listModelVisibleSubagents(state);
4663
+ if (registry.length === 0) {
4664
+ return { status: "success", result: "No active subagent instances." };
4665
+ }
4666
+ const lines = registry.map(
4667
+ (entry) => `- ${entry.reference}: ${entry.name} \u2014 ${entry.status ?? "unknown"}${entry.description ? ` (${entry.description})` : ""}`
4668
+ );
4669
+ return {
4670
+ status: "success",
4671
+ result: `Active subagent instances:
4672
+ ${lines.join("\n")}
4673
+ Message one with subagent_message(referenceId, message).`
4674
+ };
4675
+ }
4600
4676
  const createArgs = builtinSubagentAction === "create" ? this.parseSubagentCreateArgs(call.function.arguments) : null;
4601
4677
  if (createArgs?.error) {
4602
4678
  return { status: "error", error: createArgs.error };
@@ -6919,7 +6995,10 @@ This usually points to something I can't fix from here \u2014 for example the wo
6919
6995
  model: def.model,
6920
6996
  include_chat: def.includeChat ?? false,
6921
6997
  include_past_tools: def.includePastTools ?? false,
6922
- parallel_tool_calls: def.parallelToolCalls ?? false,
6998
+ // Unset stays unset (2026-08-04): defaulting to `false` forbade the
6999
+ // parallel batching agentic-tuned models are trained on; absent lets
7000
+ // the provider default apply, while an explicit prompt value is honored.
7001
+ parallel_tool_calls: def.parallelToolCalls ?? null,
6923
7002
  tool_choice: def.toolChoice ?? "auto",
6924
7003
  reasoning_effort: def.reasoning?.effort ?? null,
6925
7004
  reasoning_max_tokens: def.reasoning?.maxTokens ?? null,
@@ -8201,6 +8280,13 @@ This usually points to something I can't fix from here \u2014 for example the wo
8201
8280
  if (hidden.size === 0) return registry;
8202
8281
  return registry.filter((entry) => !hidden.has(entry.name));
8203
8282
  }
8283
+ /** Public snapshot for the `subagents` built-in tool (ToolExecutor). */
8284
+ static async listModelVisibleSubagents(state) {
8285
+ return _FlowEngine.filterModelVisibleSubagents(
8286
+ await this.getSubagentRegistry(state),
8287
+ state.prompt?._tools
8288
+ );
8289
+ }
8204
8290
  static async getSubagentRegistry(state) {
8205
8291
  if (typeof state.thread.instance.getChildrenRegistry === "function") {
8206
8292
  try {
@@ -8470,7 +8556,8 @@ ${msg.content}` : `${imageDescriptions}${nonImageList}`;
8470
8556
  await this.getSubagentRegistry(state),
8471
8557
  state.prompt?._tools
8472
8558
  );
8473
- if (subagentRegistry.length > 0) {
8559
+ const suppressRegistryMessage = Array.isArray(state.prompt?.hooks) ? state.prompt.hooks.includes("append_static_session_env") : false;
8560
+ if (subagentRegistry.length > 0 && !suppressRegistryMessage) {
8474
8561
  const fallbackNameMap = await this.resolveRegistryThreadNameMap(state, subagentRegistry);
8475
8562
  const lines = subagentRegistry.map((entry) => {
8476
8563
  const threadName = this.getRegistryThreadName(entry) ?? fallbackNameMap.get(entry.reference) ?? null;
@@ -8829,6 +8916,16 @@ ${lines.join("\n\n")}`
8829
8916
  }
8830
8917
  if (resumablePromptSubagents.length > 0 && !tools.some((tool) => tool.function.name === "subagent_create")) {
8831
8918
  tools.push(this.buildSubagentCreateTool(resumablePromptSubagents));
8919
+ if (!tools.some((tool) => tool.function.name === "subagents")) {
8920
+ tools.push({
8921
+ type: "function",
8922
+ function: {
8923
+ name: "subagents",
8924
+ description: "List your active subagent instances: reference id, name, status, and working-status text. Use when you need to check on children you created; completions also arrive as messages.",
8925
+ parameters: { type: "object", properties: {} }
8926
+ }
8927
+ });
8928
+ }
8832
8929
  }
8833
8930
  const hasActiveResumable = childrenRegistry.some(
8834
8931
  (entry) => entry.status !== "terminated"
@@ -8863,6 +8960,73 @@ ${lines.join("\n\n")}`
8863
8960
  }
8864
8961
  });
8865
8962
  }
8963
+ try {
8964
+ const cat2 = await state.thread.instance.getValue("mcp_catalog");
8965
+ const servers = cat2?.servers ?? [];
8966
+ const have = new Set(tools.map((t) => t.function?.name));
8967
+ const sanitizeProperty = (p) => {
8968
+ const prop = p ?? {};
8969
+ const t = prop.type;
8970
+ if (t === "string" || t === "number" || t === "integer" || t === "boolean") {
8971
+ const out = { type: t };
8972
+ if (typeof prop.description === "string") out.description = prop.description;
8973
+ if (Array.isArray(prop.enum)) out.enum = prop.enum;
8974
+ return out;
8975
+ }
8976
+ if (t === "array") {
8977
+ return {
8978
+ type: "array",
8979
+ items: sanitizeProperty(prop.items),
8980
+ ...typeof prop.description === "string" ? { description: prop.description } : {}
8981
+ };
8982
+ }
8983
+ if (t === "object") {
8984
+ const props = prop.properties ?? {};
8985
+ const out = {};
8986
+ for (const [k, v] of Object.entries(props)) out[k] = sanitizeProperty(v);
8987
+ return {
8988
+ type: "object",
8989
+ properties: out,
8990
+ ...Array.isArray(prop.required) ? { required: prop.required.filter((r) => typeof r === "string" && r in out) } : {},
8991
+ ...typeof prop.description === "string" ? { description: prop.description } : {}
8992
+ };
8993
+ }
8994
+ return {
8995
+ type: "string",
8996
+ ...typeof prop.description === "string" ? { description: prop.description } : {}
8997
+ };
8998
+ };
8999
+ const sanitizeSchema = (schema) => {
9000
+ const s = schema ?? { type: "object", properties: {} };
9001
+ if (s.type !== void 0 && s.type !== "object") return null;
9002
+ const sanitized = sanitizeProperty({ ...s, type: "object" });
9003
+ return sanitized;
9004
+ };
9005
+ for (const srv of servers) {
9006
+ const sname = srv.name ?? srv.serverInfo?.name;
9007
+ if (!sname || srv.error || !Array.isArray(srv.tools)) continue;
9008
+ for (const mt of srv.tools) {
9009
+ const pname = `${sname}_${mt.name}`;
9010
+ if (have.has(pname)) continue;
9011
+ const parameters = sanitizeSchema(mt.inputSchema);
9012
+ if (!parameters) {
9013
+ console.log(`[FlowEngine] mcp projection: skipped ${pname} (non-object schema)`);
9014
+ continue;
9015
+ }
9016
+ tools.push({
9017
+ type: "function",
9018
+ function: {
9019
+ name: pname,
9020
+ description: mt.description || `${mt.name} via the ${sname} MCP server`,
9021
+ parameters
9022
+ }
9023
+ });
9024
+ have.add(pname);
9025
+ }
9026
+ }
9027
+ console.log(`[FlowEngine] mcp projection: ${tools.length} tools after catalog merge`);
9028
+ } catch {
9029
+ }
8866
9030
  console.log(`[FlowEngine] getToolsForPrompt result: ${tools.length} tools loaded for prompt ${promptData.name}`);
8867
9031
  return tools;
8868
9032
  } catch (error) {
@@ -15586,6 +15750,25 @@ var migration34 = {
15586
15750
  var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21, migration22, migration23, migration24, migration25, migration26, migration27, migration28, migration29, migration30, migration31, migration32, migration33, migration34];
15587
15751
  var LATEST_SCHEMA_VERSION = migrations.length;
15588
15752
 
15753
+ // src/durable-objects/subagent-reconcile.ts
15754
+ var CHILD_STALE_AFTER_MS = 5 * 6e4;
15755
+ var CHILD_AUDIT_MIN_INTERVAL_MS = 6e4;
15756
+ var CHILD_AUDIT_MAX_PER_SWEEP = 4;
15757
+ var SETTLED_STATUSES = /* @__PURE__ */ new Set(["idle", "terminated"]);
15758
+ function isAuditCandidate(entry, nowMs) {
15759
+ if (SETTLED_STATUSES.has(entry.status)) return false;
15760
+ const createdMs = (entry.createdAt ?? 0) / 1e3;
15761
+ return nowMs - createdMs >= CHILD_STALE_AFTER_MS;
15762
+ }
15763
+ function resolveStaleChildStatus(entry, child, nowMs) {
15764
+ if (SETTLED_STATUSES.has(entry.status)) return null;
15765
+ if (!child) return "terminated";
15766
+ if (child.terminated) return "terminated";
15767
+ if (child.executing) return null;
15768
+ if (nowMs - child.lastActivityMs < CHILD_STALE_AFTER_MS) return null;
15769
+ return entry.resumable ? "idle" : "terminated";
15770
+ }
15771
+
15589
15772
  // src/durable-objects/DurableThread.ts
15590
15773
  init_types();
15591
15774
 
@@ -17388,12 +17571,103 @@ var DurableThread = class _DurableThread extends DurableObject {
17388
17571
  );
17389
17572
  const rows = cursor.toArray();
17390
17573
  const raw = rows[0]?.value ?? "[]";
17574
+ let entries;
17391
17575
  try {
17392
17576
  const parsed = JSON.parse(raw);
17393
- return Array.isArray(parsed) ? parsed : [];
17577
+ entries = Array.isArray(parsed) ? parsed : [];
17394
17578
  } catch {
17395
17579
  return [];
17396
17580
  }
17581
+ return this.reconcileStaleChildren(entries);
17582
+ }
17583
+ /** In-memory throttle/reentrancy guards for the stale-child reconciler. */
17584
+ lastChildAuditAt = 0;
17585
+ childAuditInFlight = false;
17586
+ /**
17587
+ * Lazily heal registry entries stranded at a "working" status. The normal
17588
+ * settlement chain lives only in the parent's `waitUntil`; a parent
17589
+ * eviction/deploy/kill during a child's run leaves the entry at `running`
17590
+ * forever and clients render an eternal spinner. On registry reads, audit
17591
+ * old unsettled entries against the child thread's ACTUAL state and settle
17592
+ * them — conservatively (see subagent-reconcile.ts): resumable children
17593
+ * are never deterministically terminated.
17594
+ */
17595
+ async reconcileStaleChildren(entries) {
17596
+ const nowMs = Date.now();
17597
+ if (this.childAuditInFlight) return entries;
17598
+ if (nowMs - this.lastChildAuditAt < CHILD_AUDIT_MIN_INTERVAL_MS) return entries;
17599
+ const candidates = entries.filter((entry) => isAuditCandidate(entry, nowMs));
17600
+ if (candidates.length === 0) return entries;
17601
+ this.childAuditInFlight = true;
17602
+ this.lastChildAuditAt = nowMs;
17603
+ try {
17604
+ const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
17605
+ const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
17606
+ const changed = [];
17607
+ for (const entry of candidates.slice(0, CHILD_AUDIT_MAX_PER_SWEEP)) {
17608
+ let facts = null;
17609
+ try {
17610
+ const child = await agentBuilder.getThread(entry.reference);
17611
+ facts = child ? {
17612
+ terminated: child.terminated ?? null,
17613
+ executing: child.executing === true,
17614
+ lastActivityMs: Number(child.last_activity_at ?? child.created_at ?? 0) * 1e3
17615
+ } : null;
17616
+ } catch {
17617
+ continue;
17618
+ }
17619
+ const resolution = resolveStaleChildStatus(entry, facts, nowMs);
17620
+ if (resolution) {
17621
+ changed.push({
17622
+ reference: entry.reference,
17623
+ status: resolution,
17624
+ // Settling a live one-shot child to terminated also terminates
17625
+ // the thread itself so registry and reality agree.
17626
+ terminateChild: resolution === "terminated" && facts !== null && !facts.terminated
17627
+ });
17628
+ }
17629
+ }
17630
+ if (changed.length === 0) return entries;
17631
+ const byRef = new Map(changed.map((c) => [c.reference, c]));
17632
+ const healed = entries.map(
17633
+ (entry) => byRef.has(entry.reference) ? { ...entry, status: byRef.get(entry.reference).status } : entry
17634
+ );
17635
+ await this.setChildrenRegistry(healed);
17636
+ for (const c of changed) {
17637
+ if (c.terminateChild) {
17638
+ try {
17639
+ const childId = this.env.AGENT_BUILDER_THREAD.idFromName(c.reference);
17640
+ const childStub = this.env.AGENT_BUILDER_THREAD.get(childId);
17641
+ await childStub.terminateThread?.(c.reference);
17642
+ } catch (error) {
17643
+ console.error("[DurableThread] Stale-child terminate failed (non-fatal):", error);
17644
+ }
17645
+ }
17646
+ await this.broadcastChildStatusChanged(c.reference);
17647
+ }
17648
+ console.log(
17649
+ `[DurableThread] Reconciled ${changed.length} stale subagent entr${changed.length === 1 ? "y" : "ies"}:`,
17650
+ changed.map((c) => `${c.reference.slice(0, 8)}\u2192${c.status}`).join(", ")
17651
+ );
17652
+ return healed;
17653
+ } finally {
17654
+ this.childAuditInFlight = false;
17655
+ }
17656
+ }
17657
+ /**
17658
+ * Child-side registry settlement: called from the CHILD's own DO whenever
17659
+ * a run ends, so the spinner clears even when the parent-held completion
17660
+ * chain died with the parent. Only downgrades a live "working" status to
17661
+ * idle — never clobbers terminated.
17662
+ */
17663
+ async settleChildRegistryIdle(_threadId, reference) {
17664
+ const entries = await this.getChildrenRegistry("");
17665
+ const entry = entries.find((e) => e.reference === reference);
17666
+ if (!entry || entry.status === "terminated" || entry.status === "idle") return;
17667
+ await this.setChildrenRegistry(
17668
+ entries.map((e) => e.reference === reference ? { ...e, status: "idle" } : e)
17669
+ );
17670
+ await this.broadcastChildStatusChanged(reference);
17397
17671
  }
17398
17672
  async setChildrenRegistry(entries) {
17399
17673
  await this.ctx.storage.sql.exec(
@@ -17975,6 +18249,33 @@ var DurableThread = class _DurableThread extends DurableObject {
17975
18249
  * Used by blocking subagent tool calls.
17976
18250
  */
17977
18251
  async executeBlocking(threadId, agentId, initial_messages, data) {
18252
+ try {
18253
+ return await this.executeBlockingImpl(threadId, agentId, initial_messages, data);
18254
+ } finally {
18255
+ this.ctx.waitUntil?.(this.settleOwnRegistryEntry(threadId));
18256
+ }
18257
+ }
18258
+ /** Best-effort: mark this child's entry idle in its parent's registry. */
18259
+ async settleOwnRegistryEntry(threadId) {
18260
+ try {
18261
+ const terminatedCursor = await this.ctx.storage.sql.exec(
18262
+ `SELECT value FROM execution_state WHERE key = 'terminated' LIMIT 1`
18263
+ );
18264
+ const terminated = terminatedCursor.toArray()[0]?.value;
18265
+ if (terminated && terminated !== "" && terminated !== "null") return;
18266
+ const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
18267
+ const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
18268
+ const meta = await agentBuilder.getThread(threadId);
18269
+ const parent = meta?.parent;
18270
+ if (!parent) return;
18271
+ const parentId = this.env.AGENT_BUILDER_THREAD.idFromName(parent);
18272
+ const parentStub = this.env.AGENT_BUILDER_THREAD.get(parentId);
18273
+ await parentStub.settleChildRegistryIdle?.(parent, threadId);
18274
+ } catch (error) {
18275
+ console.error("[DurableThread] Child-side registry settlement failed (non-fatal):", error);
18276
+ }
18277
+ }
18278
+ async executeBlockingImpl(threadId, agentId, initial_messages, data) {
17978
18279
  await this.ensureMigrated();
17979
18280
  await this.assertNotTerminated();
17980
18281
  let parentThreadId = null;
@@ -19351,7 +19652,7 @@ ${resultContent}${attachmentSummary}`;
19351
19652
  `
19352
19653
  SELECT
19353
19654
  id, message_id, provider, actual_provider, standard_agents_router_used, model, model_name, prompt_name,
19354
- tools_called, queued_tools, provider_tools, parent_log_id, retry_of_log_id, error, cost_total,
19655
+ tools_called, queued_tools, provider_tools, parent_log_id, retry_of_log_id, error, error_type, cost_total,
19355
19656
  input_tokens, cached_tokens, output_tokens, total_tokens,
19356
19657
  is_complete, created_at
19357
19658
  FROM logs
@@ -19376,6 +19677,7 @@ ${resultContent}${attachmentSummary}`;
19376
19677
  parent_log_id: row.parent_log_id,
19377
19678
  retry_of_log_id: row.retry_of_log_id,
19378
19679
  error: row.error,
19680
+ error_type: row.error_type,
19379
19681
  cost_total: row.cost_total,
19380
19682
  input_tokens: row.input_tokens,
19381
19683
  cached_tokens: row.cached_tokens,