@standardagents/builder 0.31.0 → 0.33.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 (37) hide show
  1. package/dist/built-in-routes.js +35 -1
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-wfApkZad.js → ApiKeysView-CxNw8yvZ.js} +1 -1
  4. package/dist/client/{CenteredContentView-JFIi6iAA.js → CenteredContentView-D4G91Thq.js} +1 -1
  5. package/dist/client/{CompositionView-CIm8Kt1I.js → CompositionView-BaWfyUW6.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-DO1fUxUR.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-CVzMoEOi.js} +1 -1
  7. package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-DfEFB4ze.js → CopyButton.vue_vue_type_script_setup_true_lang-Cz_ZrOAS.js} +1 -1
  8. package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-4XH4cxYM.js → DataTable.vue_vue_type_script_setup_true_lang-CQwcIXyb.js} +1 -1
  9. package/dist/client/{JsonViewer-md2l5DdY.js → JsonViewer-Dsl-e2IH.js} +1 -1
  10. package/dist/client/{LoginView-BJSfZpgw.js → LoginView-CX0lmKUS.js} +1 -1
  11. package/dist/client/{MarketplaceView-DXE7ys3s.js → MarketplaceView-BJAG7kej.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-FiL__HMg.js → Modal.vue_vue_type_script_setup_true_lang-B2Yd_pvh.js} +1 -1
  13. package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-BIBHjNB0.js → ModelModal.vue_vue_type_script_setup_true_lang-CCH8VxwI.js} +1 -1
  14. package/dist/client/{ModelsView-BhtR_WPG.js → ModelsView-CGgNBkY-.js} +1 -1
  15. package/dist/client/{PromptEditView-DadQ9eJ0.js → PromptEditView-CL2LEH80.js} +1 -1
  16. package/dist/client/{PromptModal-BMBKSmJC.js → PromptModal-DHHy53-v.js} +1 -1
  17. package/dist/client/{PromptsView-F1l2HocI.js → PromptsView-BcTfvJdH.js} +1 -1
  18. package/dist/client/{ProvidersView-DFWGCJwN.js → ProvidersView-B2yy_Kbe.js} +1 -1
  19. package/dist/client/{ThreadInspectorPane.vue_vue_type_script_setup_true_lang-C7JV15r3.js → ThreadInspectorPane.vue_vue_type_script_setup_true_lang-BCiZyZu-.js} +13 -13
  20. package/dist/client/{ThreadsView-CMb05n9d.js → ThreadsView-BKSAJipV.js} +1 -1
  21. package/dist/client/{ToolsView-7F2ybb1x.js → ToolsView-DBkare-6.js} +1 -1
  22. package/dist/client/{UsersView-Badppc4K.js → UsersView-yBnliXIU.js} +1 -1
  23. package/dist/client/{VariablesView-E45rU9bc.js → VariablesView-N-VgGqPg.js} +1 -1
  24. package/dist/client/assets/{ThreadInspectorPane-Bha1aVZZ.css → ThreadInspectorPane-DSQsKyG6.css} +1 -1
  25. package/dist/client/{index-BYdncvi0.js → index-DANZMMwO.js} +2 -2
  26. package/dist/client/index.html +1 -1
  27. package/dist/{index-CL7q3PuW.d.ts → index-CqhPhqAI.d.ts} +10 -0
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +117 -17
  30. package/dist/index.js.map +1 -1
  31. package/dist/plugin.js +2 -0
  32. package/dist/plugin.js.map +1 -1
  33. package/dist/runtime.d.ts +23 -2
  34. package/dist/runtime.js +115 -17
  35. package/dist/runtime.js.map +1 -1
  36. package/dist/test.d.ts +1 -1
  37. package/package.json +5 -5
@@ -26,7 +26,7 @@
26
26
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
27
  <link rel="icon" type="image/svg+xml" href="/agents/assets/img/favicon-9uH1rb2F.svg">
28
28
  <title>AgentBuilder Admin</title>
29
- <script type="module" crossorigin src="/agents/index-BYdncvi0.js"></script>
29
+ <script type="module" crossorigin src="/agents/index-DANZMMwO.js"></script>
30
30
  <link rel="modulepreload" crossorigin href="/agents/formkit-DcZQH9-p.js">
31
31
  <link rel="modulepreload" crossorigin href="/agents/vendor-TPZjEwCU.js">
32
32
  <link rel="modulepreload" crossorigin href="/agents/syntax-D-ZcovVx.js">
@@ -1234,6 +1234,8 @@ interface LogDetails {
1234
1234
  */
1235
1235
  interface DurableThreadRpc {
1236
1236
  stop(): Promise<Response>;
1237
+ /** Newest non-empty user message on the thread, for thread-list previews. */
1238
+ getLatestUserMessagePreview(): Promise<string | null>;
1237
1239
  executeBlocking?(threadId: string, agentId: string, initial_messages?: unknown[], data?: unknown): Promise<{
1238
1240
  status: "success" | "error";
1239
1241
  result?: string;
@@ -1419,6 +1421,8 @@ interface ThreadRegistryEntry {
1419
1421
  last_activity_at?: number | null;
1420
1422
  /** True while the thread is executing a flow (stale markers read as false). */
1421
1423
  executing?: boolean;
1424
+ /** Short excerpt of the most recent user message, for thread-list rows. */
1425
+ last_message_preview?: string | null;
1422
1426
  }
1423
1427
  /**
1424
1428
  * RPC methods exposed by DurableAgentBuilder for external callers.
@@ -1622,9 +1626,15 @@ interface DurableAgentBuilderRpc {
1622
1626
  email: string | null;
1623
1627
  thread_count: number;
1624
1628
  }>>;
1629
+ /** Fill previews for threads that predate preview tracking (bulk, idempotent). */
1630
+ backfillThreadPreviews?(entries: Array<{
1631
+ id: string;
1632
+ preview: string | null;
1633
+ }>): Promise<void>;
1625
1634
  /** Stamp thread activity / executing state for thread-list recency + status. */
1626
1635
  recordThreadActivity?(threadId: string, options?: {
1627
1636
  executing?: boolean;
1637
+ messagePreview?: string | null;
1628
1638
  }): Promise<void>;
1629
1639
  getThreadTreeIds?(threadId: string): Promise<string[]>;
1630
1640
  deleteThread(threadId: string): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ModelDefinition, AgentDefinition } from './runtime.js';
2
2
  export { AgentBuilderEnv, AuthContext, AuthUser, BroadcastOptions, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, FlowStateWithSdk, HubClientInfo, ImageContextConfig, InjectMessageOptions, LLMProviderInterface, NamespaceResolutionError, PromptContent, PromptDefinition, PromptIncludePart, PromptPart, PromptToolConfig, Provider, ProviderContentPart, ProviderFilePart, ProviderImagePart, ProviderRegistry, ProviderTextPart, SessionToolBinding, SessionToolConfig, SideConfig, SubagentToolConfig, SubpromptConfig, ThreadRegistryEntry, ThreadWatchdog, ThreadWatchdogState, ToolConfig, UpdateThreadParams, User, WatchdogCheckin, WatchdogProbeResult, authenticate, buildImageDescription, cat, createNamespaceContext, emitThreadEvent, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isQualifiedName, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, stat, tail, unlink, updateThread, writeFile, writeImage } from './runtime.js';
3
3
  export { AgentPluginOptions, agentbuilder, builder } from './plugin.js';
4
- export { A as Agent, x as AttachmentRef, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, m as ClientStreamRequest, n as ClientStreamResponse, k as ClientToolRequest, l as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, w as FileRecord, y as FileStats, u as FlowResult, F as FlowState, G as GrepResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, J as MessageContent, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, q as ThreadInstance, r as ThreadMetadata, T as ToolCall, t as ToolResult, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-CL7q3PuW.js';
4
+ export { A as Agent, x as AttachmentRef, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, o as ClientDuplex, p as ClientForwarding, m as ClientStreamRequest, n as ClientStreamResponse, k as ClientToolRequest, l as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, w as FileRecord, y as FileStats, u as FlowResult, F as FlowState, G as GrepResult, D as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, J as MessageContent, H as MultimodalContent, R as RequestContext, S as StorageBackend, v as TelemetryEvent, z as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, q as ThreadInstance, r as ThreadMetadata, T as ToolCall, t as ToolResult, g as callClient, h as callClientStream, c as createThreadEndpointHandler, j as decodeStreamChunk, d as defineController, i as encodeStreamChunk, s as supportsClientForwarding } from './index-CqhPhqAI.js';
5
5
  export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderConfigSlot, ProviderConfigSlots, ProviderConfigValueSource, ProviderDefinition, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ProviderMethodOverrides, ModelCapabilities as ProviderModelCapabilities, ProviderOverride, ProviderOverrideContext, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, ProviderSyncOverride, ProviderSystemMessage, ProviderTool, ProviderToolCallPart, ProviderToolMessage, ProviderToolResultContent, ProviderUsage, ProviderUserMessage, ReasoningConfig, StructuredPrompt, TextContent, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineProvider, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel, providerConst, providerEnv, providerValue } from '@standardagents/spec';
6
6
  export { C as ConstituentItem, D as DiscoveredPackage, N as NamespacedRegistry, P as PackingAnalysis, a as PackingOptions, b as PackingResult, c as UnpackOptions, d as UnpackResult } from './types-Bpe7IANZ.js';
7
7
  export { D as DiscoveryConfig, P as PackageDiscoveryService, d as discoverPackages } from './discovery-DiMJWisl.js';
package/dist/index.js CHANGED
@@ -14895,6 +14895,8 @@ declare module 'virtual:@standardagents/builder' {
14895
14895
  last_activity_at?: number | null;
14896
14896
  /** True while the thread is executing a flow. */
14897
14897
  executing?: boolean;
14898
+ /** Short excerpt of the most recent user message, for thread-list rows. */
14899
+ last_message_preview?: string | null;
14898
14900
  }
14899
14901
 
14900
14902
  export interface UpdateThreadParams {
@@ -26196,9 +26198,9 @@ var DurableThread = class _DurableThread extends DurableObject {
26196
26198
  * Each migration is run in order, starting from the current version + 1.
26197
26199
  */
26198
26200
  async runMigrations(fromVersion) {
26199
- for (const migration48 of migrations) {
26200
- if (migration48.version > fromVersion) {
26201
- await migration48.up(this.ctx.storage.sql);
26201
+ for (const migration50 of migrations) {
26202
+ if (migration50.version > fromVersion) {
26203
+ await migration50.up(this.ctx.storage.sql);
26202
26204
  }
26203
26205
  }
26204
26206
  }
@@ -29598,12 +29600,15 @@ ${resultContent}${attachmentSummary}`;
29598
29600
  * bookkeeping is presentation state: a failure here must never take down the
29599
29601
  * turn that triggered it.
29600
29602
  */
29601
- async recordRegistryActivity(threadId, executing) {
29603
+ async recordRegistryActivity(threadId, executing, messagePreview) {
29602
29604
  try {
29603
29605
  const namespace = this.env.AGENT_BUILDER;
29604
29606
  if (!namespace || typeof namespace.idFromName !== "function") return;
29605
29607
  const agentBuilder = namespace.get(namespace.idFromName("singleton"));
29606
- await agentBuilder.recordThreadActivity?.(threadId, { executing });
29608
+ await agentBuilder.recordThreadActivity?.(threadId, {
29609
+ executing,
29610
+ ...messagePreview !== void 0 ? { messagePreview } : {}
29611
+ });
29607
29612
  } catch (error) {
29608
29613
  console.error(
29609
29614
  "[DurableThread] Failed to record thread activity (non-fatal):",
@@ -29611,6 +29616,27 @@ ${resultContent}${attachmentSummary}`;
29611
29616
  );
29612
29617
  }
29613
29618
  }
29619
+ /**
29620
+ * The newest non-empty user message on this thread, for thread-list previews.
29621
+ * Returns null when the thread has none. The Durable Object does not know its
29622
+ * own thread id, so the caller is the one that stamps the registry.
29623
+ */
29624
+ async getLatestUserMessagePreview() {
29625
+ await this.ensureMigrated();
29626
+ try {
29627
+ const rows = this.ctx.storage.sql.exec(
29628
+ `SELECT content FROM messages
29629
+ WHERE role = 'user'
29630
+ AND content IS NOT NULL AND content != ''
29631
+ AND subagent_id IS NULL
29632
+ AND (silent IS NULL OR silent = 0)
29633
+ ORDER BY created_at DESC LIMIT 1`
29634
+ ).toArray();
29635
+ return rows[0]?.content ?? null;
29636
+ } catch {
29637
+ return null;
29638
+ }
29639
+ }
29614
29640
  /** Arm the standing watchdog for an execution start. One pending row max. */
29615
29641
  async armExecutionWatchdog(threadId) {
29616
29642
  try {
@@ -30194,7 +30220,11 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30194
30220
  this.isExecuting = true;
30195
30221
  this.currentAbortController = new AbortController();
30196
30222
  await this.armExecutionWatchdog(threadId);
30197
- await this.recordRegistryActivity(threadId, true);
30223
+ await this.recordRegistryActivity(
30224
+ threadId,
30225
+ true,
30226
+ role === "user" ? content : void 0
30227
+ );
30198
30228
  try {
30199
30229
  const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
30200
30230
  const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
@@ -31572,9 +31602,41 @@ var migration47 = {
31572
31602
  }
31573
31603
  };
31574
31604
 
31605
+ // src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
31606
+ var migration48 = {
31607
+ version: 14,
31608
+ async up(sql) {
31609
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
31610
+ sql.exec(
31611
+ `CREATE INDEX IF NOT EXISTS idx_threads_executing_since
31612
+ ON threads(executing_since) WHERE executing_since IS NOT NULL`
31613
+ );
31614
+ sql.exec(
31615
+ `UPDATE threads SET last_activity_at = created_at WHERE last_activity_at IS NULL`
31616
+ );
31617
+ sql.exec(`
31618
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '14')
31619
+ `);
31620
+ }
31621
+ };
31622
+
31623
+ // src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
31624
+ var migration49 = {
31625
+ version: 15,
31626
+ async up(sql) {
31627
+ sql.exec(
31628
+ `UPDATE threads SET last_message_preview = NULL
31629
+ WHERE last_message_preview LIKE 'Subagent (reference: %'`
31630
+ );
31631
+ sql.exec(`
31632
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '15')
31633
+ `);
31634
+ }
31635
+ };
31636
+
31575
31637
  // src/durable-objects/agentbuilder-migrations/index.ts
31576
- var migrations2 = [migration35, migration36, migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47];
31577
- var LATEST_SCHEMA_VERSION2 = 13;
31638
+ var migrations2 = [migration35, migration36, migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49];
31639
+ var LATEST_SCHEMA_VERSION2 = 15;
31578
31640
 
31579
31641
  // src/utils/crypto.ts
31580
31642
  var CryptoUtil = class {
@@ -31774,6 +31836,13 @@ function parseStoredPermissions(value) {
31774
31836
  }
31775
31837
  }
31776
31838
  var EXECUTING_STALE_AFTER_SECONDS = 15 * 60;
31839
+ var THREAD_PREVIEW_MAX_CHARS = 140;
31840
+ var isRuntimeSubagentEnvelope = (content) => /^Subagent \(reference: /.test(content.trimStart());
31841
+ var buildThreadPreview = (content) => {
31842
+ const collapsed = content.replace(/\s+/g, " ").trim();
31843
+ if (!collapsed) return null;
31844
+ return collapsed.length > THREAD_PREVIEW_MAX_CHARS ? `${collapsed.slice(0, THREAD_PREVIEW_MAX_CHARS - 1)}\u2026` : collapsed;
31845
+ };
31777
31846
  var isThreadExecuting = (executingSince, nowSeconds) => executingSince != null && nowSeconds - executingSince < EXECUTING_STALE_AFTER_SECONDS;
31778
31847
  var DurableAgentBuilder = class extends DurableObject {
31779
31848
  migratedToVersion = null;
@@ -32343,9 +32412,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32343
32412
  }
32344
32413
  }
32345
32414
  async runMigrations(fromVersion) {
32346
- for (const migration48 of migrations2) {
32347
- if (migration48.version > fromVersion) {
32348
- await migration48.up(this.ctx.storage.sql);
32415
+ for (const migration50 of migrations2) {
32416
+ if (migration50.version > fromVersion) {
32417
+ await migration50.up(this.ctx.storage.sql);
32349
32418
  }
32350
32419
  }
32351
32420
  }
@@ -32470,7 +32539,7 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32470
32539
  async getThread(id) {
32471
32540
  await this.ensureMigrated();
32472
32541
  const cursor = await this.ctx.storage.sql.exec(
32473
- `SELECT id, agent_name, user_id, tags, env, tenvs, properties, parent, terminated, created_at, last_activity_at, executing_since FROM threads WHERE id = ?`,
32542
+ `SELECT id, agent_name, user_id, tags, env, tenvs, properties, parent, terminated, created_at, last_activity_at, executing_since, last_message_preview FROM threads WHERE id = ?`,
32474
32543
  id
32475
32544
  );
32476
32545
  const rows = cursor.toArray();
@@ -32497,7 +32566,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32497
32566
  executing: isThreadExecuting(
32498
32567
  row.executing_since,
32499
32568
  Math.floor(Date.now() / 1e3)
32500
- )
32569
+ ),
32570
+ last_message_preview: row.last_message_preview
32501
32571
  };
32502
32572
  }
32503
32573
  /**
@@ -32594,9 +32664,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32594
32664
  const total = countCursor.one().count;
32595
32665
  const limit = params?.limit ?? 100;
32596
32666
  const offset = params?.offset ?? 0;
32597
- const orderByClause = params?.sort === "created" ? "created_at DESC" : params?.sort === "oldest" ? "created_at ASC" : "COALESCE(last_activity_at, created_at) DESC, created_at DESC";
32667
+ const orderByClause = params?.sort === "created" ? "created_at DESC" : params?.sort === "oldest" ? "created_at ASC" : "last_activity_at DESC, created_at DESC";
32598
32668
  const cursor = await this.ctx.storage.sql.exec(
32599
- `SELECT id, agent_name, user_id, tags, properties, parent, terminated, created_at, last_activity_at, executing_since FROM threads
32669
+ `SELECT id, agent_name, user_id, tags, properties, parent, terminated, created_at, last_activity_at, executing_since, last_message_preview FROM threads
32600
32670
  ${whereClause}
32601
32671
  ORDER BY ${orderByClause}
32602
32672
  LIMIT ? OFFSET ?`,
@@ -32620,7 +32690,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32620
32690
  terminated: row.terminated,
32621
32691
  created_at: row.created_at,
32622
32692
  last_activity_at: row.last_activity_at ?? row.created_at,
32623
- executing: isThreadExecuting(row.executing_since, nowSeconds)
32693
+ executing: isThreadExecuting(row.executing_since, nowSeconds),
32694
+ last_message_preview: row.last_message_preview
32624
32695
  }));
32625
32696
  return { threads, total };
32626
32697
  }
@@ -32659,6 +32730,7 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32659
32730
  await this.ensureMigrated();
32660
32731
  const now = Math.floor(Date.now() / 1e3);
32661
32732
  const executing = options?.executing;
32733
+ const preview = typeof options?.messagePreview === "string" && !isRuntimeSubagentEnvelope(options.messagePreview) ? buildThreadPreview(options.messagePreview) : void 0;
32662
32734
  await this.ctx.storage.sql.exec(
32663
32735
  `UPDATE threads
32664
32736
  SET last_activity_at = ?
@@ -32685,13 +32757,41 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32685
32757
  threadId
32686
32758
  );
32687
32759
  }
32760
+ if (preview !== void 0) {
32761
+ await this.ctx.storage.sql.exec(
32762
+ `UPDATE threads SET last_message_preview = ? WHERE id = ?`,
32763
+ preview,
32764
+ threadId
32765
+ );
32766
+ }
32688
32767
  this.broadcastEvent({
32689
32768
  type: "thread_activity",
32690
32769
  threadId,
32691
32770
  last_activity_at: now,
32692
- executing: executing === true
32771
+ executing: executing === true,
32772
+ ...preview !== void 0 ? { last_message_preview: preview } : {}
32693
32773
  });
32694
32774
  }
32775
+ /**
32776
+ * Fill in previews for threads that predate preview tracking. Callers read
32777
+ * each thread's newest user message and stamp the results here in one batch.
32778
+ *
32779
+ * A thread with no user message is stamped with an empty string rather than
32780
+ * left NULL: NULL means "not looked at yet", so without the sentinel an
32781
+ * empty thread would be re-read on every list request forever.
32782
+ */
32783
+ async backfillThreadPreviews(entries) {
32784
+ if (entries.length === 0) return;
32785
+ await this.ensureMigrated();
32786
+ for (const entry of entries) {
32787
+ const preview = typeof entry.preview === "string" && !isRuntimeSubagentEnvelope(entry.preview) ? buildThreadPreview(entry.preview) ?? "" : "";
32788
+ await this.ctx.storage.sql.exec(
32789
+ `UPDATE threads SET last_message_preview = ? WHERE id = ? AND last_message_preview IS NULL`,
32790
+ preview,
32791
+ entry.id
32792
+ );
32793
+ }
32794
+ }
32695
32795
  /**
32696
32796
  * Delete a thread from the registry.
32697
32797
  */