@standardagents/builder 0.30.2 → 0.32.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 (53) hide show
  1. package/dist/built-in-routes.js +45 -3
  2. package/dist/built-in-routes.js.map +1 -1
  3. package/dist/client/{ApiKeysView-jhfg43UH.js → ApiKeysView-CxNw8yvZ.js} +1 -1
  4. package/dist/client/CenteredContentView-D4G91Thq.js +1 -0
  5. package/dist/client/{CompositionView-DhkLWmtE.js → CompositionView-BaWfyUW6.js} +1 -1
  6. package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-D-WToRMy.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-Dhq0t0dd.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-D1MDCns-.js → DataTable.vue_vue_type_script_setup_true_lang-CQwcIXyb.js} +1 -1
  9. package/dist/client/{JsonViewer-BoHrE76S.js → JsonViewer-Dsl-e2IH.js} +1 -1
  10. package/dist/client/{LoginView-BpR2nMNf.js → LoginView-CX0lmKUS.js} +1 -1
  11. package/dist/client/{MarketplaceView-uUf1JYla.js → MarketplaceView-BJAG7kej.js} +1 -1
  12. package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-D-SDVFQI.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-DE57Zr0r.js → ModelModal.vue_vue_type_script_setup_true_lang-CCH8VxwI.js} +1 -1
  14. package/dist/client/{ModelsView-CuM_OieH.js → ModelsView-CGgNBkY-.js} +1 -1
  15. package/dist/client/{PromptEditView-BWjrA2zr.js → PromptEditView-CL2LEH80.js} +1 -1
  16. package/dist/client/{PromptModal-BFxhuG0Z.js → PromptModal-DHHy53-v.js} +1 -1
  17. package/dist/client/{PromptsView-AlGsRB6k.js → PromptsView-BcTfvJdH.js} +1 -1
  18. package/dist/client/{ProvidersView-CbXBs3SH.js → ProvidersView-B2yy_Kbe.js} +1 -1
  19. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-BCiZyZu-.js +22 -0
  20. package/dist/client/{ThreadsView-rN9rzJi1.js → ThreadsView-BKSAJipV.js} +1 -1
  21. package/dist/client/{ToolsView-0Gpo_RFg.js → ToolsView-DBkare-6.js} +1 -1
  22. package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-CNncALQo.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-QytRm7Ql.js} +1 -1
  23. package/dist/client/{UsersView-iXPbcguY.js → UsersView-yBnliXIU.js} +1 -1
  24. package/dist/client/{VariablesView-C9BB9h34.js → VariablesView-N-VgGqPg.js} +1 -1
  25. package/dist/client/assets/ThreadInspectorPane-DSQsKyG6.css +1 -0
  26. package/dist/client/assets/index-C4KCxdCv.css +1 -0
  27. package/dist/client/{check-BdJVlUmI.js → check-pGlck8qX.js} +1 -1
  28. package/dist/client/{chevron-right-D49O3XLU.js → chevron-right-FpPE1Obh.js} +1 -1
  29. package/dist/client/{copy-40uDyTgY.js → copy-Dt1FLh3s.js} +1 -1
  30. package/dist/client/{formkit-Bl-sERnF.js → formkit-DcZQH9-p.js} +1 -1
  31. package/dist/client/{index-BCoE48be.js → index-DANZMMwO.js} +3 -3
  32. package/dist/client/index.html +4 -4
  33. package/dist/client/{package-1UgoERpx.js → package-DSNwOrcN.js} +1 -1
  34. package/dist/client/{plus-BzCjfYs0.js → plus-D0GkkdA7.js} +1 -1
  35. package/dist/client/{timeAgo-cFwldMu-.js → timeAgo-X3Xeq5Kz.js} +1 -1
  36. package/dist/client/{useActionFeedback-BVifffYQ.js → useActionFeedback-Dxh_Xp0q.js} +1 -1
  37. package/dist/client/vue-KXHVRwln.js +1 -0
  38. package/dist/{index-CcnGEkPh.d.ts → index-1AMgutkG.d.ts} +27 -0
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +203 -16
  41. package/dist/index.js.map +1 -1
  42. package/dist/plugin.js +6 -0
  43. package/dist/plugin.js.map +1 -1
  44. package/dist/runtime.d.ts +48 -2
  45. package/dist/runtime.js +197 -16
  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-CS0mV2EJ.js +0 -1
  50. package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-Dm3d37hz.js +0 -22
  51. package/dist/client/assets/ThreadInspectorPane-CfpdDnUQ.css +0 -1
  52. package/dist/client/assets/index-BTPsPULO.css +0 -1
  53. package/dist/client/vue-CmmJuziT.js +0 -1
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-CcnGEkPh.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-1AMgutkG.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
@@ -14891,6 +14891,12 @@ declare module 'virtual:@standardagents/builder' {
14891
14891
  parent: string | null;
14892
14892
  terminated: number | null;
14893
14893
  created_at: number;
14894
+ /** Unix seconds of the last message or execution on this thread (or a descendant). */
14895
+ last_activity_at?: number | null;
14896
+ /** True while the thread is executing a flow. */
14897
+ executing?: boolean;
14898
+ /** Short excerpt of the most recent user message, for thread-list rows. */
14899
+ last_message_preview?: string | null;
14894
14900
  }
14895
14901
 
14896
14902
  export interface UpdateThreadParams {
@@ -26192,9 +26198,9 @@ var DurableThread = class _DurableThread extends DurableObject {
26192
26198
  * Each migration is run in order, starting from the current version + 1.
26193
26199
  */
26194
26200
  async runMigrations(fromVersion) {
26195
- for (const migration47 of migrations) {
26196
- if (migration47.version > fromVersion) {
26197
- await migration47.up(this.ctx.storage.sql);
26201
+ for (const migration49 of migrations) {
26202
+ if (migration49.version > fromVersion) {
26203
+ await migration49.up(this.ctx.storage.sql);
26198
26204
  }
26199
26205
  }
26200
26206
  }
@@ -29588,6 +29594,28 @@ ${resultContent}${attachmentSummary}`;
29588
29594
  if (!namespace || typeof namespace.idFromName !== "function") return null;
29589
29595
  return namespace.get(namespace.idFromName(threadId));
29590
29596
  }
29597
+ /**
29598
+ * Stamp thread activity on the registry so thread lists can sort by recency
29599
+ * of real work and show which threads are executing right now. Registry
29600
+ * bookkeeping is presentation state: a failure here must never take down the
29601
+ * turn that triggered it.
29602
+ */
29603
+ async recordRegistryActivity(threadId, executing, messagePreview) {
29604
+ try {
29605
+ const namespace = this.env.AGENT_BUILDER;
29606
+ if (!namespace || typeof namespace.idFromName !== "function") return;
29607
+ const agentBuilder = namespace.get(namespace.idFromName("singleton"));
29608
+ await agentBuilder.recordThreadActivity?.(threadId, {
29609
+ executing,
29610
+ ...messagePreview !== void 0 ? { messagePreview } : {}
29611
+ });
29612
+ } catch (error) {
29613
+ console.error(
29614
+ "[DurableThread] Failed to record thread activity (non-fatal):",
29615
+ error
29616
+ );
29617
+ }
29618
+ }
29591
29619
  /** Arm the standing watchdog for an execution start. One pending row max. */
29592
29620
  async armExecutionWatchdog(threadId) {
29593
29621
  try {
@@ -29774,6 +29802,7 @@ ${resultContent}${attachmentSummary}`;
29774
29802
  }
29775
29803
  this.isExecuting = false;
29776
29804
  this.currentAbortController = null;
29805
+ await this.recordRegistryActivity(threadId, false);
29777
29806
  tookOver = true;
29778
29807
  } else if (incomplete.length > 0 && silenceMs <= ceiling) {
29779
29808
  return report("observing", "none");
@@ -29977,6 +30006,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
29977
30006
  this.isExecuting = true;
29978
30007
  this.currentAbortController = new AbortController();
29979
30008
  await this.armExecutionWatchdog(threadId);
30009
+ await this.recordRegistryActivity(threadId, true);
29980
30010
  try {
29981
30011
  const agentDef = await this.loadAgent(agentName);
29982
30012
  const packageId = agentDef.__package?.packageId;
@@ -30108,6 +30138,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30108
30138
  this.isExecuting = false;
30109
30139
  this.currentAbortController = null;
30110
30140
  await this.disarmExecutionWatchdog(threadId);
30141
+ await this.recordRegistryActivity(threadId, false);
30111
30142
  }
30112
30143
  }
30113
30144
  /**
@@ -30168,6 +30199,11 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30168
30199
  this.isExecuting = true;
30169
30200
  this.currentAbortController = new AbortController();
30170
30201
  await this.armExecutionWatchdog(threadId);
30202
+ await this.recordRegistryActivity(
30203
+ threadId,
30204
+ true,
30205
+ role === "user" ? content : void 0
30206
+ );
30171
30207
  try {
30172
30208
  const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
30173
30209
  const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
@@ -30307,6 +30343,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30307
30343
  this.isExecuting = false;
30308
30344
  this.currentAbortController = null;
30309
30345
  await this.disarmExecutionWatchdog(threadId);
30346
+ await this.recordRegistryActivity(threadId, false);
30310
30347
  }
30311
30348
  }
30312
30349
  /**
@@ -30324,6 +30361,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30324
30361
  this.isExecuting = true;
30325
30362
  this.currentAbortController = new AbortController();
30326
30363
  await this.armExecutionWatchdog(threadId);
30364
+ await this.recordRegistryActivity(threadId, true);
30327
30365
  try {
30328
30366
  const agentBuilderId = this.env.AGENT_BUILDER.idFromName("singleton");
30329
30367
  const agentBuilder = this.env.AGENT_BUILDER.get(agentBuilderId);
@@ -30433,6 +30471,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
30433
30471
  this.isExecuting = false;
30434
30472
  this.currentAbortController = null;
30435
30473
  await this.disarmExecutionWatchdog(threadId);
30474
+ await this.recordRegistryActivity(threadId, false);
30436
30475
  const latestQueuedSide = await this.getLatestQueuedMessageSide();
30437
30476
  if (latestQueuedSide) {
30438
30477
  await this.ensureQueuedMessageExecution(threadId, latestQueuedSide);
@@ -31526,9 +31565,43 @@ var migration46 = {
31526
31565
  }
31527
31566
  };
31528
31567
 
31568
+ // src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
31569
+ var migration47 = {
31570
+ version: 13,
31571
+ async up(sql) {
31572
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
31573
+ sql.exec(`ALTER TABLE threads ADD COLUMN executing_since INTEGER`);
31574
+ sql.exec(
31575
+ `CREATE INDEX IF NOT EXISTS idx_threads_last_activity_at ON threads(last_activity_at)`
31576
+ );
31577
+ sql.exec(`UPDATE threads SET last_activity_at = created_at`);
31578
+ sql.exec(`
31579
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '13')
31580
+ `);
31581
+ }
31582
+ };
31583
+
31584
+ // src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
31585
+ var migration48 = {
31586
+ version: 14,
31587
+ async up(sql) {
31588
+ sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
31589
+ sql.exec(
31590
+ `CREATE INDEX IF NOT EXISTS idx_threads_executing_since
31591
+ ON threads(executing_since) WHERE executing_since IS NOT NULL`
31592
+ );
31593
+ sql.exec(
31594
+ `UPDATE threads SET last_activity_at = created_at WHERE last_activity_at IS NULL`
31595
+ );
31596
+ sql.exec(`
31597
+ INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '14')
31598
+ `);
31599
+ }
31600
+ };
31601
+
31529
31602
  // src/durable-objects/agentbuilder-migrations/index.ts
31530
- var migrations2 = [migration35, migration36, migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46];
31531
- var LATEST_SCHEMA_VERSION2 = 12;
31603
+ var migrations2 = [migration35, migration36, migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48];
31604
+ var LATEST_SCHEMA_VERSION2 = 14;
31532
31605
 
31533
31606
  // src/utils/crypto.ts
31534
31607
  var CryptoUtil = class {
@@ -31727,6 +31800,14 @@ function parseStoredPermissions(value) {
31727
31800
  return null;
31728
31801
  }
31729
31802
  }
31803
+ var EXECUTING_STALE_AFTER_SECONDS = 15 * 60;
31804
+ var THREAD_PREVIEW_MAX_CHARS = 140;
31805
+ var buildThreadPreview = (content) => {
31806
+ const collapsed = content.replace(/\s+/g, " ").trim();
31807
+ if (!collapsed) return null;
31808
+ return collapsed.length > THREAD_PREVIEW_MAX_CHARS ? `${collapsed.slice(0, THREAD_PREVIEW_MAX_CHARS - 1)}\u2026` : collapsed;
31809
+ };
31810
+ var isThreadExecuting = (executingSince, nowSeconds) => executingSince != null && nowSeconds - executingSince < EXECUTING_STALE_AFTER_SECONDS;
31730
31811
  var DurableAgentBuilder = class extends DurableObject {
31731
31812
  migratedToVersion = null;
31732
31813
  eventSockets = /* @__PURE__ */ new Set();
@@ -32295,9 +32376,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32295
32376
  }
32296
32377
  }
32297
32378
  async runMigrations(fromVersion) {
32298
- for (const migration47 of migrations2) {
32299
- if (migration47.version > fromVersion) {
32300
- await migration47.up(this.ctx.storage.sql);
32379
+ for (const migration49 of migrations2) {
32380
+ if (migration49.version > fromVersion) {
32381
+ await migration49.up(this.ctx.storage.sql);
32301
32382
  }
32302
32383
  }
32303
32384
  }
@@ -32339,8 +32420,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32339
32420
  const encryptedEnv = await this.encryptEnvRecord(env);
32340
32421
  const properties = this.withEnvTypeRecord(params.properties ?? null, envTypes);
32341
32422
  await this.ctx.storage.sql.exec(
32342
- `INSERT INTO threads (id, agent_name, user_id, tags, env, tenvs, properties, parent, terminated, created_at)
32343
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, ?)`,
32423
+ `INSERT INTO threads (id, agent_name, user_id, tags, env, tenvs, properties, parent, terminated, created_at, last_activity_at)
32424
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)`,
32344
32425
  id,
32345
32426
  params.agent_name,
32346
32427
  params.user_id || null,
@@ -32349,6 +32430,7 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32349
32430
  encryptedEnv,
32350
32431
  properties ? JSON.stringify(properties) : null,
32351
32432
  params.parent ?? null,
32433
+ now,
32352
32434
  now
32353
32435
  );
32354
32436
  const thread = {
@@ -32362,7 +32444,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32362
32444
  properties,
32363
32445
  parent: params.parent ?? null,
32364
32446
  terminated: null,
32365
- created_at: now
32447
+ created_at: now,
32448
+ last_activity_at: now,
32449
+ executing: false
32366
32450
  };
32367
32451
  const eventThread = await this.toThreadEventPayload(thread);
32368
32452
  this.broadcastEvent({ type: "thread_created", thread: eventThread });
@@ -32418,7 +32502,10 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32418
32502
  */
32419
32503
  async getThread(id) {
32420
32504
  await this.ensureMigrated();
32421
- const cursor = await this.ctx.storage.sql.exec(`SELECT id, agent_name, user_id, tags, env, tenvs, properties, parent, terminated, created_at FROM threads WHERE id = ?`, id);
32505
+ const cursor = await this.ctx.storage.sql.exec(
32506
+ `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 = ?`,
32507
+ id
32508
+ );
32422
32509
  const rows = cursor.toArray();
32423
32510
  if (rows.length === 0) {
32424
32511
  return null;
@@ -32438,7 +32525,13 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32438
32525
  properties,
32439
32526
  parent: row.parent,
32440
32527
  terminated: row.terminated,
32441
- created_at: row.created_at
32528
+ created_at: row.created_at,
32529
+ last_activity_at: row.last_activity_at ?? row.created_at,
32530
+ executing: isThreadExecuting(
32531
+ row.executing_since,
32532
+ Math.floor(Date.now() / 1e3)
32533
+ ),
32534
+ last_message_preview: row.last_message_preview
32442
32535
  };
32443
32536
  }
32444
32537
  /**
@@ -32479,6 +32572,7 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32479
32572
  */
32480
32573
  async listThreads(params) {
32481
32574
  await this.ensureMigrated();
32575
+ const nowSeconds = Math.floor(Date.now() / 1e3);
32482
32576
  const whereClauses = [];
32483
32577
  const args = [];
32484
32578
  if (params?.agent_name) {
@@ -32514,6 +32608,18 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32514
32608
  whereClauses.push("created_at <= ?");
32515
32609
  args.push(params.endDate);
32516
32610
  }
32611
+ if (params?.executing === true) {
32612
+ whereClauses.push("executing_since IS NOT NULL AND executing_since > ?");
32613
+ args.push(nowSeconds - EXECUTING_STALE_AFTER_SECONDS);
32614
+ } else if (params?.executing === false) {
32615
+ whereClauses.push("(executing_since IS NULL OR executing_since <= ?)");
32616
+ args.push(nowSeconds - EXECUTING_STALE_AFTER_SECONDS);
32617
+ }
32618
+ if (params?.terminated === true) {
32619
+ whereClauses.push("terminated IS NOT NULL");
32620
+ } else if (params?.terminated === false) {
32621
+ whereClauses.push("terminated IS NULL");
32622
+ }
32517
32623
  const whereClause = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
32518
32624
  const countCursor = await this.ctx.storage.sql.exec(
32519
32625
  `SELECT COUNT(*) as count FROM threads ${whereClause}`,
@@ -32522,10 +32628,11 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32522
32628
  const total = countCursor.one().count;
32523
32629
  const limit = params?.limit ?? 100;
32524
32630
  const offset = params?.offset ?? 0;
32631
+ const orderByClause = params?.sort === "created" ? "created_at DESC" : params?.sort === "oldest" ? "created_at ASC" : "last_activity_at DESC, created_at DESC";
32525
32632
  const cursor = await this.ctx.storage.sql.exec(
32526
- `SELECT id, agent_name, user_id, tags, properties, parent, terminated, created_at FROM threads
32633
+ `SELECT id, agent_name, user_id, tags, properties, parent, terminated, created_at, last_activity_at, executing_since, last_message_preview FROM threads
32527
32634
  ${whereClause}
32528
- ORDER BY created_at DESC
32635
+ ORDER BY ${orderByClause}
32529
32636
  LIMIT ? OFFSET ?`,
32530
32637
  ...args,
32531
32638
  limit,
@@ -32545,10 +32652,90 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
32545
32652
  properties: row.properties ? JSON.parse(row.properties) : null,
32546
32653
  parent: row.parent,
32547
32654
  terminated: row.terminated,
32548
- created_at: row.created_at
32655
+ created_at: row.created_at,
32656
+ last_activity_at: row.last_activity_at ?? row.created_at,
32657
+ executing: isThreadExecuting(row.executing_since, nowSeconds),
32658
+ last_message_preview: row.last_message_preview
32549
32659
  }));
32550
32660
  return { threads, total };
32551
32661
  }
32662
+ /**
32663
+ * Distinct owners of threads visible to the caller, with counts, so a thread
32664
+ * list can offer an "owner" filter without exposing the admin-only user
32665
+ * directory (which is unavailable on hosted instances).
32666
+ */
32667
+ async listThreadOwners(params) {
32668
+ await this.ensureMigrated();
32669
+ const whereClause = params?.user_id ? "WHERE t.user_id = ?" : "";
32670
+ const args = params?.user_id ? [params.user_id] : [];
32671
+ const cursor = await this.ctx.storage.sql.exec(
32672
+ `SELECT t.user_id AS user_id,
32673
+ u.username AS username,
32674
+ u.display_name AS display_name,
32675
+ u.email AS email,
32676
+ COUNT(*) AS thread_count
32677
+ FROM threads t
32678
+ LEFT JOIN users u ON u.id = t.user_id
32679
+ ${whereClause}
32680
+ GROUP BY t.user_id
32681
+ ORDER BY thread_count DESC`,
32682
+ ...args
32683
+ );
32684
+ return cursor.toArray();
32685
+ }
32686
+ /**
32687
+ * Record that a thread did work: stamp `last_activity_at` and flip the
32688
+ * executing marker. Activity bubbles up the parent chain so a root thread
32689
+ * sorts by the freshest work anywhere in its subagent tree — otherwise a
32690
+ * long-running subagent would let its parent sink down an activity-sorted
32691
+ * list.
32692
+ */
32693
+ async recordThreadActivity(threadId, options) {
32694
+ await this.ensureMigrated();
32695
+ const now = Math.floor(Date.now() / 1e3);
32696
+ const executing = options?.executing;
32697
+ const preview = typeof options?.messagePreview === "string" ? buildThreadPreview(options.messagePreview) : void 0;
32698
+ await this.ctx.storage.sql.exec(
32699
+ `UPDATE threads
32700
+ SET last_activity_at = ?
32701
+ WHERE id IN (
32702
+ WITH RECURSIVE ancestry(id, parent) AS (
32703
+ SELECT id, parent FROM threads WHERE id = ?
32704
+ UNION ALL
32705
+ SELECT t.id, t.parent FROM threads t JOIN ancestry a ON t.id = a.parent
32706
+ )
32707
+ SELECT id FROM ancestry
32708
+ )`,
32709
+ now,
32710
+ threadId
32711
+ );
32712
+ if (executing === true) {
32713
+ await this.ctx.storage.sql.exec(
32714
+ `UPDATE threads SET executing_since = ? WHERE id = ? AND executing_since IS NULL`,
32715
+ now,
32716
+ threadId
32717
+ );
32718
+ } else if (executing === false) {
32719
+ await this.ctx.storage.sql.exec(
32720
+ `UPDATE threads SET executing_since = NULL WHERE id = ?`,
32721
+ threadId
32722
+ );
32723
+ }
32724
+ if (preview !== void 0) {
32725
+ await this.ctx.storage.sql.exec(
32726
+ `UPDATE threads SET last_message_preview = ? WHERE id = ?`,
32727
+ preview,
32728
+ threadId
32729
+ );
32730
+ }
32731
+ this.broadcastEvent({
32732
+ type: "thread_activity",
32733
+ threadId,
32734
+ last_activity_at: now,
32735
+ executing: executing === true,
32736
+ ...preview !== void 0 ? { last_message_preview: preview } : {}
32737
+ });
32738
+ }
32552
32739
  /**
32553
32740
  * Delete a thread from the registry.
32554
32741
  */