@standardagents/builder 0.25.7 → 0.26.0

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 (52) hide show
  1. package/dist/built-in-routes.js +246 -16
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-B9XLopSb.js → ApiKeysView-jhfg43UH.js} +1 -1
  4. package/dist/client/CenteredContentView-CS0mV2EJ.js +1 -0
  5. package/dist/client/{CompositionView-DJOtNN7f.js → CompositionView-DhkLWmtE.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-55dXt19T.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-D-WToRMy.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-B6NqQmW9.js → CopyButton.vue_vue_type_script_setup_true_lang-Dhq0t0dd.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-0cbPVIIz.js → DataTable.vue_vue_type_script_setup_true_lang-D1MDCns-.js} +1 -1
  9. package/dist/client/{JsonViewer-B7yHhxqP.js → JsonViewer-BoHrE76S.js} +1 -1
  10. package/dist/client/{LoginView-mY9OytK6.js → LoginView-BpR2nMNf.js} +1 -1
  11. package/dist/client/{MarketplaceView-B9Z9YMuB.js → MarketplaceView-uUf1JYla.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-DEVCRbXl.js → Modal.vue_vue_type_script_setup_true_lang-D-SDVFQI.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-FxEBZ3A8.js → ModelModal.vue_vue_type_script_setup_true_lang-DE57Zr0r.js} +1 -1
  14. package/dist/client/{ModelsView-DTU8pk-T.js → ModelsView-CuM_OieH.js} +1 -1
  15. package/dist/client/{PromptEditView-BAKoULxX.js → PromptEditView-BWjrA2zr.js} +1 -1
  16. package/dist/client/{PromptModal-CoinSKIM.js → PromptModal-BFxhuG0Z.js} +1 -1
  17. package/dist/client/{PromptsView-DIkj3q-o.js → PromptsView-AlGsRB6k.js} +1 -1
  18. package/dist/client/{ProvidersView-B8A6ezDR.js → ProvidersView-CbXBs3SH.js} +1 -1
  19. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-Dm3d37hz.js +22 -0
  20. package/dist/client/{ThreadsView-DGCVdDej.js → ThreadsView-rN9rzJi1.js} +1 -1
  21. package/dist/client/{ToolsView-CC-WsZet.js → ToolsView-0Gpo_RFg.js} +1 -1
  22. package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-BH3WC9G_.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-CNncALQo.js} +1 -1
  23. package/dist/client/{UsersView-U3Stwxui.js → UsersView-iXPbcguY.js} +1 -1
  24. package/dist/client/{VariablesView-0-a1F4cn.js → VariablesView-C9BB9h34.js} +1 -1
  25. package/dist/client/assets/{ThreadInspectorPane-ouC_RyKA.css → ThreadInspectorPane-CfpdDnUQ.css} +1 -1
  26. package/dist/client/assets/index-BTPsPULO.css +1 -0
  27. package/dist/client/{check-Dkzs1C3V.js → check-BdJVlUmI.js} +1 -1
  28. package/dist/client/{chevron-right-BGirddPg.js → chevron-right-D49O3XLU.js} +1 -1
  29. package/dist/client/{copy-C08OR8rY.js → copy-40uDyTgY.js} +1 -1
  30. package/dist/client/{formkit-YJ1Z7D5r.js → formkit-Bl-sERnF.js} +1 -1
  31. package/dist/client/index-BCoE48be.js +6 -0
  32. package/dist/client/index.html +4 -4
  33. package/dist/client/{package-guyzMTDG.js → package-1UgoERpx.js} +1 -1
  34. package/dist/client/{plus-DG3ZN5wg.js → plus-BzCjfYs0.js} +1 -1
  35. package/dist/client/{timeAgo-D7V247YG.js → timeAgo-cFwldMu-.js} +1 -1
  36. package/dist/client/{useActionFeedback-byARNQjQ.js → useActionFeedback-BVifffYQ.js} +1 -1
  37. package/dist/client/{vue-BqC6IZDY.js → vue-CmmJuziT.js} +1 -1
  38. package/dist/{index-CX9Ti98s.d.ts → index-DJ7B8CTp.d.ts} +63 -0
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.js +532 -83
  41. package/dist/index.js.map +1 -1
  42. package/dist/plugin.js +27 -3
  43. package/dist/plugin.js.map +1 -1
  44. package/dist/runtime.d.ts +184 -6
  45. package/dist/runtime.js +505 -80
  46. package/dist/runtime.js.map +1 -1
  47. package/dist/test.d.ts +1 -1
  48. package/package.json +5 -5
  49. package/dist/client/CenteredContentView-Bwv4MVSC.js +0 -1
  50. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-CvssvzLz.js +0 -22
  51. package/dist/client/assets/index-Ca3he3fq.css +0 -1
  52. package/dist/client/index-4Zwz6zXD.js +0 -6
@@ -1082,6 +1082,37 @@ function resolveStreamTimeoutMs(env2, key, fallback) {
1082
1082
  const parsed = typeof raw === "number" ? raw : typeof raw === "string" ? Number.parseInt(raw, 10) : NaN;
1083
1083
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
1084
1084
  }
1085
+ function resolveWatchdogStaleCeilingMs(env2) {
1086
+ const firstChunk = resolveStreamTimeoutMs(
1087
+ env2,
1088
+ "STANDARD_AGENTS_STREAM_FIRST_CHUNK_TIMEOUT_MS",
1089
+ STREAM_FIRST_CHUNK_TIMEOUT_MS
1090
+ );
1091
+ const stall = resolveStreamTimeoutMs(
1092
+ env2,
1093
+ "STANDARD_AGENTS_STREAM_STALL_TIMEOUT_MS",
1094
+ STREAM_STALL_TIMEOUT_MS
1095
+ );
1096
+ return Math.max(firstChunk, stall) + 6e4;
1097
+ }
1098
+ async function awaitBounded(work, budgetMs, label) {
1099
+ let timer;
1100
+ work.catch(() => {
1101
+ });
1102
+ const timeout = new Promise((resolve2) => {
1103
+ timer = setTimeout(() => {
1104
+ console.error(
1105
+ `[LLMRequest] ${label} did not settle within ${budgetMs}ms \u2014 continuing without it`
1106
+ );
1107
+ resolve2(void 0);
1108
+ }, budgetMs);
1109
+ });
1110
+ try {
1111
+ return await Promise.race([work, timeout]);
1112
+ } finally {
1113
+ if (timer) clearTimeout(timer);
1114
+ }
1115
+ }
1085
1116
  function modelSupportsImageInput(modelDef) {
1086
1117
  const supportsImages = modelDef.capabilities?.supportsImages;
1087
1118
  if (typeof supportsImages === "boolean") {
@@ -1295,13 +1326,42 @@ function transformMessages(messages) {
1295
1326
  arguments: typeof tc.function.arguments === "string" ? JSON.parse(tc.function.arguments) : tc.function.arguments,
1296
1327
  extraContent: tc.extra_content && typeof tc.extra_content === "object" ? tc.extra_content : void 0
1297
1328
  }));
1329
+ const storedDetails = msg.reasoning_details;
1330
+ const reasoningDetails = Array.isArray(
1331
+ storedDetails
1332
+ ) ? storedDetails.flatMap((entry) => {
1333
+ if (!entry || typeof entry !== "object") return [];
1334
+ const raw = entry;
1335
+ const type = raw.type === "summary" || raw.type === "encrypted" ? raw.type : "text";
1336
+ return [
1337
+ {
1338
+ type,
1339
+ id: typeof raw.id === "string" ? raw.id : void 0,
1340
+ text: typeof raw.text === "string" ? raw.text : void 0,
1341
+ data: typeof raw.data === "string" ? raw.data : void 0,
1342
+ format: typeof raw.format === "string" ? raw.format : void 0
1343
+ }
1344
+ ];
1345
+ }) : void 0;
1346
+ const storedReasoning = msg.reasoning_content;
1347
+ let reasoning = typeof storedReasoning === "string" && storedReasoning.length > 0 ? storedReasoning : void 0;
1348
+ if (!reasoning && Array.isArray(reasoningDetails)) {
1349
+ const seen = /* @__PURE__ */ new Set();
1350
+ const texts = [];
1351
+ for (const d of reasoningDetails) {
1352
+ if (typeof d?.text === "string" && d.text.length > 0 && !seen.has(d.text)) {
1353
+ seen.add(d.text);
1354
+ texts.push(d.text);
1355
+ }
1356
+ }
1357
+ if (texts.length) reasoning = texts.join("\n");
1358
+ }
1298
1359
  return {
1299
1360
  role: "assistant",
1300
1361
  content: typeof msg.content === "string" ? msg.content : null,
1301
1362
  toolCalls: toolCalls?.length ? toolCalls : void 0,
1302
- // Pass reasoning for multi-turn context
1303
- reasoning: msg.reasoning_content || void 0,
1304
- reasoningDetails: msg.reasoning_details || void 0
1363
+ reasoning,
1364
+ reasoningDetails: reasoningDetails?.length ? reasoningDetails : void 0
1305
1365
  };
1306
1366
  }
1307
1367
  case "tool":
@@ -1476,7 +1536,7 @@ function normalizeProviderMetadataValue(value) {
1476
1536
  if (!trimmed || trimmed.toLowerCase() === "unknown") return void 0;
1477
1537
  return trimmed;
1478
1538
  }
1479
- var NON_VISION_PLACEHOLDER_TEXT, STREAM_FIRST_CHUNK_TIMEOUT_MS, STREAM_STALL_TIMEOUT_MS, LLMRequest;
1539
+ var NON_VISION_PLACEHOLDER_TEXT, STREAM_FIRST_CHUNK_TIMEOUT_MS, STREAM_STALL_TIMEOUT_MS, HOOK_AWAIT_BUDGET_MS, STREAM_CLEANUP_BUDGET_MS, LLMRequest;
1480
1540
  var init_LLMRequest = __esm({
1481
1541
  "src/agents/LLMRequest.ts"() {
1482
1542
  init_pricing();
@@ -1487,6 +1547,8 @@ var init_LLMRequest = __esm({
1487
1547
  NON_VISION_PLACEHOLDER_TEXT = "[Image omitted: model does not support vision]";
1488
1548
  STREAM_FIRST_CHUNK_TIMEOUT_MS = 12e4;
1489
1549
  STREAM_STALL_TIMEOUT_MS = 45e3;
1550
+ HOOK_AWAIT_BUDGET_MS = 3e4;
1551
+ STREAM_CLEANUP_BUDGET_MS = 5e3;
1490
1552
  LLMRequest = class {
1491
1553
  /**
1492
1554
  * Execute an LLM request with full retry/fallback logic
@@ -1970,7 +2032,14 @@ var init_LLMRequest = __esm({
1970
2032
  parentSignal.removeEventListener("abort", onParentAbort);
1971
2033
  }
1972
2034
  try {
1973
- await iterator.return?.();
2035
+ const cleanup = iterator.return?.();
2036
+ if (cleanup) {
2037
+ await awaitBounded(
2038
+ Promise.resolve(cleanup),
2039
+ STREAM_CLEANUP_BUDGET_MS,
2040
+ "stream iterator cleanup"
2041
+ );
2042
+ }
1974
2043
  } catch {
1975
2044
  }
1976
2045
  }
@@ -2103,7 +2172,11 @@ var init_LLMRequest = __esm({
2103
2172
  */
2104
2173
  static async resolveProgressArgument(state, toolName) {
2105
2174
  try {
2106
- const mod = await state.thread.instance.loadTool(toolName);
2175
+ const mod = await awaitBounded(
2176
+ state.thread.instance.loadTool(toolName),
2177
+ HOOK_AWAIT_BUDGET_MS,
2178
+ `loadTool(${toolName}) for progress argument`
2179
+ );
2107
2180
  const pa = mod?.progressArgument ?? mod?.default?.progressArgument;
2108
2181
  return typeof pa === "string" && pa ? pa : void 0;
2109
2182
  } catch {
@@ -2124,7 +2197,11 @@ var init_LLMRequest = __esm({
2124
2197
  function: { name: entry.name, arguments: entry.argumentsDelta || "" }
2125
2198
  };
2126
2199
  try {
2127
- await FlowEngine.runToolCallStartedHook(state, toolCall, progress);
2200
+ await awaitBounded(
2201
+ FlowEngine.runToolCallStartedHook(state, toolCall, progress),
2202
+ HOOK_AWAIT_BUDGET_MS,
2203
+ `tool_call_started hook for ${entry.name}`
2204
+ );
2128
2205
  } catch (error) {
2129
2206
  console.error("[LLMRequest] Error firing tool_call_started:", error);
2130
2207
  }
@@ -12323,8 +12400,12 @@ var init_ThreadStateImpl = __esm({
12323
12400
  options?.includeSilent,
12324
12401
  options?.maxDepth
12325
12402
  );
12403
+ const messages = result.messages.map(this._mapMessage);
12404
+ if (options?.order === "desc") {
12405
+ messages.reverse();
12406
+ }
12326
12407
  return {
12327
- messages: result.messages.map(this._mapMessage),
12408
+ messages,
12328
12409
  total: result.total,
12329
12410
  hasMore: result.hasMore
12330
12411
  };
@@ -42163,14 +42244,19 @@ function transformAssistantMessage2(msg) {
42163
42244
  }
42164
42245
  }
42165
42246
  if (msg.reasoning && typeof msg.reasoning === "string") {
42166
- if (reasoningItemsById.size > 0) {
42167
- const firstItem = reasoningItemsById.values().next().value;
42168
- if (firstItem) {
42169
- firstItem.summary.unshift({ type: "summary_text", text: msg.reasoning });
42247
+ const alreadyPresent = Array.from(reasoningItemsById.values()).some(
42248
+ (item) => item.summary.some((s) => s.text === msg.reasoning)
42249
+ );
42250
+ if (!alreadyPresent) {
42251
+ if (reasoningItemsById.size > 0) {
42252
+ const firstItem = reasoningItemsById.values().next().value;
42253
+ if (firstItem) {
42254
+ firstItem.summary.unshift({ type: "summary_text", text: msg.reasoning });
42255
+ }
42256
+ } else {
42257
+ const newItem = getOrCreateItem(defaultId);
42258
+ newItem.summary = [{ type: "summary_text", text: msg.reasoning }];
42170
42259
  }
42171
- } else {
42172
- const newItem = getOrCreateItem(defaultId);
42173
- newItem.summary = [{ type: "summary_text", text: msg.reasoning }];
42174
42260
  }
42175
42261
  }
42176
42262
  for (const item of reasoningItemsById.values()) {
@@ -66037,7 +66123,11 @@ var config_get_default = defineController2(async ({ config, env: env2, req }) =>
66037
66123
  });
66038
66124
  });
66039
66125
 
66126
+ // src/version.ts
66127
+ var BUILDER_VERSION = "0.26.0".length > 0 ? "0.26.0" : "dev";
66128
+
66040
66129
  // src/api/diagnostics.get.ts
66130
+ init_LLMRequest();
66041
66131
  function isFetcher(value) {
66042
66132
  return !!value && typeof value.fetch === "function";
66043
66133
  }
@@ -66126,8 +66216,30 @@ var diagnostics_get_default = defineController2(async ({ req, env: env2, tools,
66126
66216
  });
66127
66217
  }
66128
66218
  }
66129
- return Response.json({ warnings });
66219
+ const runtime = {
66220
+ builderVersion: BUILDER_VERSION,
66221
+ streamFirstChunkTimeoutMs: resolveStreamTimeoutValue(
66222
+ env2,
66223
+ "STANDARD_AGENTS_STREAM_FIRST_CHUNK_TIMEOUT_MS",
66224
+ 12e4
66225
+ ),
66226
+ streamStallTimeoutMs: resolveStreamTimeoutValue(
66227
+ env2,
66228
+ "STANDARD_AGENTS_STREAM_STALL_TIMEOUT_MS",
66229
+ 45e3
66230
+ ),
66231
+ watchdogCeilingMs: resolveWatchdogStaleCeilingMs(env2),
66232
+ watchdogCompanionAvailable: Boolean(
66233
+ env2.AGENT_BUILDER_WATCHDOG
66234
+ )
66235
+ };
66236
+ return Response.json({ warnings, runtime });
66130
66237
  });
66238
+ function resolveStreamTimeoutValue(env2, key, fallback) {
66239
+ const raw = env2?.[key];
66240
+ const parsed = typeof raw === "number" ? raw : typeof raw === "string" ? Number.parseInt(raw, 10) : NaN;
66241
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
66242
+ }
66131
66243
 
66132
66244
  // src/api/events.get.ts
66133
66245
  var events_get_default = defineController2(async ({ req, env: env2 }) => {
@@ -70729,6 +70841,61 @@ var cost_get_default = defineController2(async ({ req, params, env: env2 }) => {
70729
70841
  }
70730
70842
  });
70731
70843
 
70844
+ // src/api/threads/[id]/diagnostics.get.ts
70845
+ var diagnostics_get_default2 = defineController2(async ({ req, params, env: env2 }) => {
70846
+ if (isPlatformHosted(env2)) {
70847
+ const authResult = await requirePermission(req, env2, "logs");
70848
+ if (authResult instanceof Response) {
70849
+ return authResult;
70850
+ }
70851
+ } else {
70852
+ const authContext = await authenticate(req, env2);
70853
+ if (authContext && !hasPermission(authContext.user, "logs")) {
70854
+ return Response.json(
70855
+ { error: "Forbidden: 'logs' access required" },
70856
+ { status: 403 }
70857
+ );
70858
+ }
70859
+ }
70860
+ const threadId = params.id;
70861
+ if (!threadId) {
70862
+ return Response.json({ error: "Thread ID required" }, { status: 400 });
70863
+ }
70864
+ try {
70865
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
70866
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
70867
+ const threadMeta = await agentBuilder.getThread(threadId);
70868
+ if (!threadMeta) {
70869
+ return Response.json({ error: "Thread not found" }, { status: 404 });
70870
+ }
70871
+ const threadStub = env2.AGENT_BUILDER_THREAD.get(
70872
+ env2.AGENT_BUILDER_THREAD.idFromName(threadId)
70873
+ );
70874
+ const thread = await threadStub.getDiagnostics?.();
70875
+ let watchdog = null;
70876
+ const watchdogNamespace = env2.AGENT_BUILDER_WATCHDOG;
70877
+ if (watchdogNamespace) {
70878
+ try {
70879
+ const companion = watchdogNamespace.get(watchdogNamespace.idFromName(threadId));
70880
+ watchdog = await companion.getState();
70881
+ } catch (error) {
70882
+ console.error("[diagnostics] Failed to read watchdog companion state:", error);
70883
+ }
70884
+ }
70885
+ return Response.json({
70886
+ thread: thread ?? null,
70887
+ watchdog,
70888
+ terminated: threadMeta.terminated ?? null
70889
+ });
70890
+ } catch (error) {
70891
+ console.error("Error fetching thread diagnostics:", error);
70892
+ return Response.json(
70893
+ { error: error instanceof Error ? error.message : "Failed to fetch diagnostics" },
70894
+ { status: 500 }
70895
+ );
70896
+ }
70897
+ });
70898
+
70732
70899
  // src/api/threads/[id]/effects.get.ts
70733
70900
  async function getAuthorizedThreadStub(req, env2, threadId) {
70734
70901
  const authResult = await requireAuth(req, env2);
@@ -71247,8 +71414,69 @@ var pending_tool_calls_get_default = defineController2(async ({ params, env: env
71247
71414
  }
71248
71415
  });
71249
71416
 
71417
+ // src/api/threads/[id]/revive.post.ts
71418
+ var revive_post_default = defineController2(async ({ req, params, env: env2 }) => {
71419
+ if (isPlatformHosted(env2)) {
71420
+ const authResult = await requirePermission(req, env2, "logs");
71421
+ if (authResult instanceof Response) {
71422
+ return authResult;
71423
+ }
71424
+ } else {
71425
+ const authContext = await authenticate(req, env2);
71426
+ if (authContext && !hasPermission(authContext.user, "logs")) {
71427
+ return Response.json(
71428
+ { error: "Forbidden: 'logs' access required" },
71429
+ { status: 403 }
71430
+ );
71431
+ }
71432
+ }
71433
+ const threadId = params.id;
71434
+ if (!threadId) {
71435
+ return Response.json({ error: "Thread ID required" }, { status: 400 });
71436
+ }
71437
+ try {
71438
+ const agentBuilderId = env2.AGENT_BUILDER.idFromName("singleton");
71439
+ const agentBuilder = env2.AGENT_BUILDER.get(agentBuilderId);
71440
+ const threadMeta = await agentBuilder.getThread(threadId);
71441
+ if (!threadMeta) {
71442
+ return Response.json({ error: "Thread not found" }, { status: 404 });
71443
+ }
71444
+ const threadStub = env2.AGENT_BUILDER_THREAD.get(
71445
+ env2.AGENT_BUILDER_THREAD.idFromName(threadId)
71446
+ );
71447
+ if (typeof threadStub.watchdogProbe !== "function") {
71448
+ return Response.json(
71449
+ { error: "This deployment does not support watchdog probes" },
71450
+ { status: 501 }
71451
+ );
71452
+ }
71453
+ const result = await threadStub.watchdogProbe(threadId);
71454
+ return Response.json(result);
71455
+ } catch (error) {
71456
+ console.error("Error running watchdog probe:", error);
71457
+ return Response.json(
71458
+ { error: error instanceof Error ? error.message : "Failed to run watchdog probe" },
71459
+ { status: 500 }
71460
+ );
71461
+ }
71462
+ });
71463
+
71250
71464
  // src/api/threads/[id]/rpc.post.ts
71251
71465
  var rpc_post_default = defineController2(async ({ req, params, env: env2 }) => {
71466
+ if (isPlatformHosted(env2)) {
71467
+ const authResult = await requirePermission(req, env2, "logs");
71468
+ if (authResult instanceof Response) {
71469
+ return authResult;
71470
+ }
71471
+ } else {
71472
+ const authContext = await authenticate(req, env2);
71473
+ if (authContext && !hasPermission(authContext.user, "logs")) {
71474
+ return Response.json(
71475
+ { error: "Forbidden: 'logs' access required" },
71476
+ { status: 403 }
71477
+ );
71478
+ }
71479
+ }
71252
71480
  const threadId = params.id;
71253
71481
  if (!threadId) {
71254
71482
  return Response.json({ error: "Thread ID required" }, { status: 400 });
@@ -73001,6 +73229,7 @@ var routeHandlers = {
73001
73229
  "GET:/threads/:id/bridge": bridge_default,
73002
73230
  "POST:/threads/:id/continue": continue_post_default,
73003
73231
  "GET:/threads/:id/cost": cost_get_default,
73232
+ "GET:/threads/:id/diagnostics": diagnostics_get_default2,
73004
73233
  "GET:/threads/:id/effects": effects_get_default,
73005
73234
  "GET:/threads/:id/fs": fs_default,
73006
73235
  "DELETE:/threads/:id/kv": kv_default,
@@ -73010,6 +73239,7 @@ var routeHandlers = {
73010
73239
  "GET:/threads/:id/messages": messages_get_default,
73011
73240
  "POST:/threads/:id/messages": messages_post_default,
73012
73241
  "GET:/threads/:id/pending-tool-calls": pending_tool_calls_get_default,
73242
+ "POST:/threads/:id/revive": revive_post_default,
73013
73243
  "POST:/threads/:id/rpc": rpc_post_default,
73014
73244
  "POST:/threads/:id/stop": stop_post_default,
73015
73245
  "GET:/threads/:id/stream": stream_default,