@sentry/junior 0.252.0 → 0.254.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 (61) hide show
  1. package/dist/{admin-C_-XEdym.js → admin-D7kYbu5l.js} +1 -1
  2. package/dist/{agent-hooks-DcoKnnxn.js → agent-hooks-DfPubHYm.js} +9 -9
  3. package/dist/{agent-invocations-D_YoNnAn.js → agent-invocations-BY60iNw-.js} +0 -4
  4. package/dist/{agent-runner-B-7S_DCS.js → agent-runner-BWmnJ0dD.js} +21 -21
  5. package/dist/api/schema.js +1 -1
  6. package/dist/api.js +23 -23
  7. package/dist/app.js +113 -83
  8. package/dist/{assistant-reply-DbPLvsnL.js → assistant-reply-B3J6ogAM.js} +1 -1
  9. package/dist/{briefs-D4SMqc-r.js → briefs-B_fgUFD2.js} +2 -2
  10. package/dist/{catalog-runtime-BQQiwwA7.js → catalog-runtime-TG5WF66H.js} +1 -1
  11. package/dist/cli/chat.js +15 -15
  12. package/dist/cli/check.js +3 -3
  13. package/dist/cli/main.js +2 -2
  14. package/dist/cli/plugins.js +4 -4
  15. package/dist/cli/snapshot-create.js +3 -3
  16. package/dist/cli/upgrade.js +3 -3
  17. package/dist/{client-ROZeVjq0.js → client-oBgOmQV6.js} +2 -2
  18. package/dist/{config-DNNkxfGS.js → config-CRlLncIj.js} +2 -2
  19. package/dist/{connections-DJV6mLrH.js → connections-BZAuDnYm.js} +9 -9
  20. package/dist/{conversation-privacy-B19umzIL.js → conversation-privacy-Dui3hTO-.js} +1 -1
  21. package/dist/{credential-sync-CEd2F5PS.js → credential-sync-9HiSpphh.js} +14 -14
  22. package/dist/{executor-_4bdoCxe.js → executor-B88oEqok.js} +41 -92
  23. package/dist/{images-BAN7DHuO.js → images-k8HewSp_.js} +2 -2
  24. package/dist/instrumentation.js +2 -2
  25. package/dist/{job-queue-DSQx1qFY.js → job-queue-B6KaQXY6.js} +1 -1
  26. package/dist/{logging-COUsTFIL.js → logging-B3nJLXT7.js} +1 -1
  27. package/dist/{message-CtDDEwwV.js → message-C9Wx3Rxx.js} +3 -3
  28. package/dist/nitro.js +3 -3
  29. package/dist/{oauth-callback-server-CCiKrS0s.js → oauth-callback-server-CetsCZng.js} +2 -2
  30. package/dist/{pending-auth-DDx3EU4M.js → pending-auth-m8aJlwP_.js} +2 -2
  31. package/dist/{profile-DP4LKL6V.js → profile-Dh73O6lZ.js} +4 -4
  32. package/dist/{registry-CeEpPna6.js → registry-C-jns0Be.js} +1 -1
  33. package/dist/{resolve-B5ma3igH.js → resolve-CDYCpS_U.js} +2 -2
  34. package/dist/{result-Da0RVuWG.js → result-sih-x_yN.js} +6 -6
  35. package/dist/{runner-se1W_jGZ.js → runner-CA43p74t.js} +9 -9
  36. package/dist/{sandbox-egress-signals-CS959-wm.js → sandbox-egress-signals-CxHHTu8e.js} +1 -1
  37. package/dist/{schema-C9Vnyb_f.js → schema-C-a9U34c.js} +1 -1
  38. package/dist/{session-stop-DlRDisF5.js → session-stop-DenN1GuJ.js} +11 -11
  39. package/dist/{skills-CSzgs0EC.js → skills-D_XDFkSx.js} +2 -2
  40. package/dist/{spawn-DT9fMjtd.js → spawn-CcWYt3Tl.js} +2 -2
  41. package/dist/{store-Dx1U69mm.js → store-CIs0Tr31.js} +6 -6
  42. package/dist/{task-queue-BX9YrIsw.js → task-queue-DAY-aQLX.js} +3 -3
  43. package/dist/{task-runner-CO55Olal.js → task-runner-BE6Jgt2u.js} +145 -127
  44. package/dist/{tasks-DwGlFX9i.js → tasks-DO9VM9cu.js} +15 -26
  45. package/dist/{thread-state-Ces6tXmB.js → thread-state-FreaCOZD.js} +7 -7
  46. package/dist/{turn-failure-response-B4VCvEQf.js → turn-failure-response-CJSt2ZSf.js} +7 -7
  47. package/dist/{turn-lifecycle-B1VV58Yo.js → turn-lifecycle-BXclEcfZ.js} +1 -1
  48. package/dist/{turn-session-routing-Dw2Kamg9.js → turn-session-routing-CItfUsjQ.js} +1 -1
  49. package/dist/{validation-yJF9LKOv.js → validation-MW8dmhF9.js} +1 -1
  50. package/dist/{version-CZYqvIFh.js → version-DcWD8xpq.js} +1 -1
  51. package/dist/version.js +1 -1
  52. package/dist/{viewer-D96vo_42.js → viewer-CGZiUVMU.js} +4 -13
  53. package/dist/{web-input-5mls-41-.js → web-input-Dpqh0oBt.js} +44 -52
  54. package/dist/{work-D2M2-zCO.js → work-DbA9xNO1.js} +9 -9
  55. package/dist/{worker-BwCpf8tB.js → worker-DuCs9-3G.js} +5 -5
  56. package/migrations/0056_automation_runs.sql +69 -0
  57. package/migrations/0057_drop_legacy_automation_tables.sql +10 -0
  58. package/migrations/meta/0056_snapshot.json +4280 -0
  59. package/migrations/meta/0057_snapshot.json +3618 -0
  60. package/migrations/meta/_journal.json +15 -1
  61. package/package.json +5 -5
@@ -1,16 +1,16 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { d as logException, f as logInfo, p as logWarn } from "./logging-COUsTFIL.js";
3
- import { n as botConfig } from "./config-DNNkxfGS.js";
4
- import { f as getSqlExecutor, u as getConversationStore } from "./viewer-D96vo_42.js";
5
- import { s as juniorConversations } from "./agent-invocations-D_YoNnAn.js";
6
- import "./executor-_4bdoCxe.js";
7
- import { p as credentialSubjectSchema } from "./registry-CeEpPna6.js";
8
- import { V as postSlackMessage, _t as isToolResultError, ht as isAssistantMessage, vt as isToolResultMessage, wt as ToolInputError, yt as normalizeToolNameFromResult } from "./tasks-DwGlFX9i.js";
9
- import { it as JUNIOR_THREAD_STATE_TTL_MS, rt as verifyTimerWatchCredentialSubject, u as getPlugins } from "./agent-hooks-DcoKnnxn.js";
10
- import { w as getStateAdapter } from "./profile-DP4LKL6V.js";
11
- import { r as sanitizeAssistantText } from "./assistant-reply-DbPLvsnL.js";
2
+ import { d as logException, f as logInfo, p as logWarn } from "./logging-B3nJLXT7.js";
3
+ import { n as botConfig } from "./config-CRlLncIj.js";
4
+ import { f as getSqlExecutor, u as getConversationStore } from "./viewer-CGZiUVMU.js";
5
+ import { s as juniorConversations } from "./agent-invocations-BY60iNw-.js";
6
+ import "./executor-B88oEqok.js";
7
+ import { p as credentialSubjectSchema } from "./registry-C-jns0Be.js";
8
+ import { V as postSlackMessage, _t as isToolResultError, ht as isAssistantMessage, vt as isToolResultMessage, wt as ToolInputError, yt as normalizeToolNameFromResult } from "./tasks-DO9VM9cu.js";
9
+ import { it as JUNIOR_THREAD_STATE_TTL_MS, rt as verifyTimerWatchCredentialSubject, u as getPlugins } from "./agent-hooks-DfPubHYm.js";
10
+ import { w as getStateAdapter } from "./profile-Dh73O6lZ.js";
11
+ import { r as sanitizeAssistantText } from "./assistant-reply-B3J6ogAM.js";
12
12
  import { r as EVENT_AUTHOR_ID } from "./prompt-C04mP1hu.js";
13
- import { r as appendAndEnqueueInboundMessage } from "./store-Dx1U69mm.js";
13
+ import { r as appendAndEnqueueInboundMessage } from "./store-CIs0Tr31.js";
14
14
  import { z } from "zod";
15
15
  import { eq, inArray } from "drizzle-orm";
16
16
  import { eventMatchSchema, normalizeEventIdentifier, objectTypeSchema } from "@sentry/junior-plugin-api";
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { p as logWarn } from "./logging-COUsTFIL.js";
2
+ import { p as logWarn } from "./logging-B3nJLXT7.js";
3
3
  import { s as skillRoots } from "./package-discovery-BVvSnQBB.js";
4
- import { n as pluginCatalogRuntime } from "./catalog-runtime-BQQiwwA7.js";
4
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-TG5WF66H.js";
5
5
  import path from "node:path";
6
6
  import { z } from "zod";
7
7
  import fs from "node:fs/promises";
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { Ht as toolInvocationDestination, Vt as actorFromRun } from "./agent-runner-B-7S_DCS.js";
3
- import { n as createAndEnqueueAgentInvocation } from "./work-D2M2-zCO.js";
2
+ import { Ht as toolInvocationDestination, Vt as actorFromRun } from "./agent-runner-BWmnJ0dD.js";
3
+ import { n as createAndEnqueueAgentInvocation } from "./work-DbA9xNO1.js";
4
4
  //#region src/chat/agent-invocations/spawn.ts
5
5
  var spawn_exports = /* @__PURE__ */ __exportAll({ bindSpawnAgent: () => bindSpawnAgent });
6
6
  /** Bind a Run's Actor and credentials to new Agent invocations. */
@@ -1,10 +1,10 @@
1
- import { I as isRecord, L as toOptionalNumber, R as toOptionalString } from "./logging-COUsTFIL.js";
2
- import { i as getChatConfig } from "./config-DNNkxfGS.js";
1
+ import { I as isRecord, L as toOptionalNumber, R as toOptionalString } from "./logging-B3nJLXT7.js";
2
+ import { i as getChatConfig } from "./config-CRlLncIj.js";
3
3
  import { s as parseStoredSlackActor } from "./actor-BTtLaeWy.js";
4
- import { F as sameDestination, N as parseDestination, u as getConversationStore } from "./viewer-D96vo_42.js";
5
- import { it as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-DcoKnnxn.js";
6
- import { C as getDefaultRedisStateAdapterFor, w as getStateAdapter } from "./profile-DP4LKL6V.js";
7
- import "./job-queue-DSQx1qFY.js";
4
+ import { F as sameDestination, N as parseDestination, u as getConversationStore } from "./viewer-CGZiUVMU.js";
5
+ import { it as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-DfPubHYm.js";
6
+ import { C as getDefaultRedisStateAdapterFor, w as getStateAdapter } from "./profile-Dh73O6lZ.js";
7
+ import "./job-queue-B6KaQXY6.js";
8
8
  import { z } from "zod";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { destinationSchema } from "@sentry/junior-plugin-api";
@@ -1,5 +1,5 @@
1
- import { p as logWarn } from "./logging-COUsTFIL.js";
2
- import { c as QUEUE_SIGNATURE_MAX_AGE_MS, d as createVercelQueueClient, l as signQueueMessage, p as runWithTurnRequestDeadline, u as verifyQueueMessage } from "./job-queue-DSQx1qFY.js";
1
+ import { p as logWarn } from "./logging-B3nJLXT7.js";
2
+ import { c as QUEUE_SIGNATURE_MAX_AGE_MS, d as createVercelQueueClient, l as signQueueMessage, p as runWithTurnRequestDeadline, u as verifyQueueMessage } from "./job-queue-B6KaQXY6.js";
3
3
  import { z } from "zod";
4
4
  import { createHash } from "node:crypto";
5
5
  import { handleCallback, registerDevConsumer } from "@vercel/queue";
@@ -115,7 +115,7 @@ function pluginTaskCallback() {
115
115
  maxDeliveries: PLUGIN_TASK_MAX_DELIVERIES,
116
116
  onRejected: logPluginTaskQueueMessageRejected,
117
117
  run: async (message) => {
118
- const { processPluginTask } = await import("./task-runner-CO55Olal.js").then((n) => n.r);
118
+ const { processPluginTask } = await import("./task-runner-BE6Jgt2u.js").then((n) => n.r);
119
119
  await processPluginTask(message);
120
120
  },
121
121
  topic: PLUGIN_TASK_QUEUE_TOPIC,
@@ -1,20 +1,20 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { h as runBestEffort, p as logWarn } from "./logging-COUsTFIL.js";
2
+ import { h as runBestEffort, p as logWarn } from "./logging-B3nJLXT7.js";
3
3
  import { g as parseSlackMessageTs } from "./actor-BTtLaeWy.js";
4
- import { C as agentTurnTotalTokens, S as agentTurnCostUsd, d as getDb, r as readActorIdentity, u as getConversationStore } from "./viewer-D96vo_42.js";
5
- import { s as juniorConversations } from "./agent-invocations-D_YoNnAn.js";
6
- import { c as juniorAutomationExecutions, s as juniorAutomations } from "./executor-_4bdoCxe.js";
4
+ import { C as agentTurnTotalTokens, S as agentTurnCostUsd, d as getDb, r as readActorIdentity, u as getConversationStore } from "./viewer-CGZiUVMU.js";
5
+ import { s as juniorConversations } from "./agent-invocations-BY60iNw-.js";
6
+ import { o as juniorAutomationRuns, s as juniorAutomations } from "./executor-B88oEqok.js";
7
7
  import { m as sameActorIdentity } from "./history-9pO1Zd4Y.js";
8
- import { p as credentialSubjectSchema } from "./registry-CeEpPna6.js";
9
- import { Gt as hydrateConversationMessages, M as formatSlackLink, Q as getTurnRecord, V as postSlackMessage, Zt as fallbackShortTitle, _t as isToolResultError, a as readScheduledAutomation, g as getEventAutomation, j as escapeSlackMrkdwnText, mt as instructionTextForProjection, u as blockEventAutomation, ut as getPiMessageRole, vt as isToolResultMessage, xt as stripRuntimeTurnContext, yt as normalizeToolNameFromResult, z as openSlackDirectMessage } from "./tasks-DwGlFX9i.js";
10
- import { St as createPluginConversationEvents, dt as createPluginModel, ft as createPluginLogger, it as JUNIOR_THREAD_STATE_TTL_MS, lt as createPluginState, p as coreTaskRegistrations, st as getDashboardTaskLink, u as getPlugins, ut as createPluginEmbedder } from "./agent-hooks-DcoKnnxn.js";
11
- import { w as getStateAdapter } from "./profile-DP4LKL6V.js";
12
- import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-Ces6tXmB.js";
13
- import { t as resolveConversationRouting } from "./turn-session-routing-Dw2Kamg9.js";
14
- import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-BX9YrIsw.js";
8
+ import { p as credentialSubjectSchema } from "./registry-C-jns0Be.js";
9
+ import { Gt as hydrateConversationMessages, M as formatSlackLink, Q as getTurnRecord, V as postSlackMessage, Zt as fallbackShortTitle, _t as isToolResultError, a as readScheduledAutomation, g as getEventAutomation, j as escapeSlackMrkdwnText, mt as instructionTextForProjection, u as blockEventAutomation, ut as getPiMessageRole, vt as isToolResultMessage, xt as stripRuntimeTurnContext, yt as normalizeToolNameFromResult, z as openSlackDirectMessage } from "./tasks-DO9VM9cu.js";
10
+ import { St as createPluginConversationEvents, dt as createPluginModel, ft as createPluginLogger, it as JUNIOR_THREAD_STATE_TTL_MS, lt as createPluginState, p as coreTaskRegistrations, st as getDashboardTaskLink, u as getPlugins, ut as createPluginEmbedder } from "./agent-hooks-DfPubHYm.js";
11
+ import { w as getStateAdapter } from "./profile-Dh73O6lZ.js";
12
+ import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-FreaCOZD.js";
13
+ import { t as resolveConversationRouting } from "./turn-session-routing-CItfUsjQ.js";
14
+ import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-DAY-aQLX.js";
15
15
  import { z } from "zod";
16
16
  import { createHash } from "node:crypto";
17
- import { and, asc, count, desc, eq, gte, inArray, lte, max, or, sql } from "drizzle-orm";
17
+ import { and, asc, count, desc, eq, gte, inArray, isNotNull, lte, max, sql } from "drizzle-orm";
18
18
  import { destinationSchema, destinationVisibilitySchema, isSlackDestination, pluginRunContextSchema, replyAttributionSchema, sourceSchema, taskOutcomeSchema } from "@sentry/junior-plugin-api";
19
19
  //#region src/api/conversations/aggregate.ts
20
20
  function usageTokenValue(source, field) {
@@ -100,12 +100,15 @@ function conversationActiveDaysColumn() {
100
100
  }
101
101
  //#endregion
102
102
  //#region src/chat/automations/execution-stats.ts
103
- const AUTOMATION_EXECUTION_TYPES = ["scheduled", "event"];
104
103
  const AUTOMATION_EXECUTION_STATUSES = [
105
104
  "blocked",
106
105
  "completed",
107
106
  "failed"
108
107
  ];
108
+ /** An execution is a run with an outcome. Skipped runs are not executions. */
109
+ function isExecution() {
110
+ return and(inArray(juniorAutomationRuns.status, [...AUTOMATION_EXECUTION_STATUSES]), isNotNull(juniorAutomationRuns.completedAtMs));
111
+ }
109
112
  function addUsd(current, next) {
110
113
  return Math.round((current + next) * 0xe8d4a51000) / 0xe8d4a51000;
111
114
  }
@@ -138,60 +141,76 @@ function emptyStatusDay(date) {
138
141
  failed: 0
139
142
  };
140
143
  }
141
- /** Record one terminal automation execution and its durable conversation when present. */
142
- async function recordAutomationExecution(type, automationId, options) {
143
- const namespace = options.namespace ?? "junior";
144
+ /**
145
+ * Return the Conversation id when that Conversation exists, else null. A
146
+ * dispatch can end before it creates its Conversation, and the run's
147
+ * `conversation_id` is a foreign key.
148
+ */
149
+ function existingConversationId(conversationId) {
150
+ return sql`(select ${juniorConversations.conversationId} from ${juniorConversations} where ${juniorConversations.conversationId} = ${conversationId})`;
151
+ }
152
+ /**
153
+ * Record the outcome of one Automation run. A run that already has an outcome
154
+ * keeps it. A failed write is logged, and the heartbeat settles a started
155
+ * event run later.
156
+ */
157
+ async function recordAutomationExecution(automationId, options) {
144
158
  const nowMs = options.nowMs ?? Date.now();
145
159
  const status = options.status ?? "completed";
160
+ const outcome = {
161
+ completedAtMs: nowMs,
162
+ errorMessage: options.errorMessage ?? null,
163
+ status
164
+ };
146
165
  try {
147
- await getDb().insert(juniorAutomationExecutions).values({
148
- ...options.conversationId ? { conversationId: options.conversationId } : void 0,
149
- executedAtMs: nowMs,
150
- executionId: options.executionId,
151
- kind: type,
152
- namespace,
153
- status,
154
- automationId
155
- }).onConflictDoNothing({ target: [
156
- juniorAutomationExecutions.kind,
157
- juniorAutomationExecutions.namespace,
158
- juniorAutomationExecutions.executionId
159
- ] });
166
+ await getDb().insert(juniorAutomationRuns).values({
167
+ id: options.executionId,
168
+ automationId,
169
+ conversationId: options.conversationId ? existingConversationId(options.conversationId) : null,
170
+ ...outcome
171
+ }).onConflictDoUpdate({
172
+ target: juniorAutomationRuns.id,
173
+ set: {
174
+ ...outcome,
175
+ conversationId: sql`coalesce(${juniorAutomationRuns.conversationId}, excluded.conversation_id)`
176
+ },
177
+ setWhere: inArray(juniorAutomationRuns.status, ["pending", "running"])
178
+ });
160
179
  } catch (error) {
161
180
  logWarn("task.execution.stat_failed", {
162
181
  error,
163
182
  "app.task.execution.id": options.executionId,
164
- "app.task.execution.namespace": namespace,
165
183
  "app.task.execution.status": status,
166
- "app.task.execution.task_id": automationId,
167
- "app.task.execution.type": type
184
+ "app.task.execution.task_id": automationId
168
185
  });
169
186
  }
170
187
  }
171
- /** Load usage analytics for all automations of one type and namespace. */
172
- async function readAutomationExecutionSummaries(type, namespace, options = {}) {
188
+ /** Load usage analytics for the given automations. */
189
+ async function readAutomationExecutionSummaries(automationIds, options = {}) {
190
+ if (automationIds.length === 0) return /* @__PURE__ */ new Map();
173
191
  const nowMs = options.nowMs ?? Date.now();
174
192
  const oneDayAgoMs = nowMs - 864e5;
175
193
  const sevenDaysAgoMs = nowMs - 6048e5;
176
194
  const thirtyDaysAgoMs = nowMs - 2592e6;
177
195
  const ninetyDaysAgoMs = nowMs - 7776e6;
178
196
  const db = getDb();
179
- const latest = db.selectDistinctOn([juniorAutomationExecutions.automationId], {
180
- status: juniorAutomationExecutions.status,
181
- conversationId: juniorAutomationExecutions.conversationId,
182
- automationId: juniorAutomationExecutions.automationId
183
- }).from(juniorAutomationExecutions).where(and(eq(juniorAutomationExecutions.kind, type), eq(juniorAutomationExecutions.namespace, namespace), options.automationIds ? inArray(juniorAutomationExecutions.automationId, options.automationIds) : void 0)).orderBy(juniorAutomationExecutions.automationId, desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId)).as("latest_task_execution");
197
+ const selected = and(isExecution(), inArray(juniorAutomationRuns.automationId, automationIds));
198
+ const latest = db.selectDistinctOn([juniorAutomationRuns.automationId], {
199
+ status: juniorAutomationRuns.status,
200
+ conversationId: juniorAutomationRuns.conversationId,
201
+ automationId: juniorAutomationRuns.automationId
202
+ }).from(juniorAutomationRuns).where(selected).orderBy(juniorAutomationRuns.automationId, desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).as("latest_task_execution");
184
203
  const rows = await db.select({
185
204
  lastRunStatus: latest.status,
186
205
  lastConversationId: latest.conversationId,
187
- lastExecutedAtMs: max(juniorAutomationExecutions.executedAtMs),
188
- runsLast1Day: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${oneDayAgoMs})::int`,
189
- runsLast7Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${sevenDaysAgoMs})::int`,
190
- runsLast30Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${thirtyDaysAgoMs})::int`,
191
- runsLast90Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${ninetyDaysAgoMs})::int`,
192
- automationId: juniorAutomationExecutions.automationId,
206
+ lastExecutedAtMs: max(juniorAutomationRuns.completedAtMs),
207
+ runsLast1Day: sql`count(*) filter (where ${juniorAutomationRuns.completedAtMs} >= ${oneDayAgoMs})::int`,
208
+ runsLast7Days: sql`count(*) filter (where ${juniorAutomationRuns.completedAtMs} >= ${sevenDaysAgoMs})::int`,
209
+ runsLast30Days: sql`count(*) filter (where ${juniorAutomationRuns.completedAtMs} >= ${thirtyDaysAgoMs})::int`,
210
+ runsLast90Days: sql`count(*) filter (where ${juniorAutomationRuns.completedAtMs} >= ${ninetyDaysAgoMs})::int`,
211
+ automationId: juniorAutomationRuns.automationId,
193
212
  totalRuns: sql`count(*)::int`
194
- }).from(juniorAutomationExecutions).innerJoin(latest, eq(latest.automationId, juniorAutomationExecutions.automationId)).where(and(eq(juniorAutomationExecutions.kind, type), eq(juniorAutomationExecutions.namespace, namespace), options.automationIds ? inArray(juniorAutomationExecutions.automationId, options.automationIds) : void 0)).groupBy(juniorAutomationExecutions.automationId, latest.conversationId, latest.status);
213
+ }).from(juniorAutomationRuns).innerJoin(latest, eq(latest.automationId, juniorAutomationRuns.automationId)).where(selected).groupBy(juniorAutomationRuns.automationId, latest.conversationId, latest.status);
195
214
  return new Map(rows.map((row) => [row.automationId, {
196
215
  lastRunStatus: row.lastRunStatus,
197
216
  ...row.lastConversationId ? { lastConversationId: row.lastConversationId } : void 0,
@@ -210,14 +229,13 @@ async function readAutomationExecutionDays(dayCount = 90, options = {}) {
210
229
  const end = utcDate(options.nowMs ?? Date.now());
211
230
  const endMs = Date.parse(`${end}T23:59:59.999Z`);
212
231
  const startMs = Date.parse(`${end}T00:00:00.000Z`) - (dayCount - 1) * 864e5;
213
- const executionDate = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
214
- const conversationCost = conversationUsageCostExpr(juniorConversations.usage);
215
- const rows = await getDb().select({
216
- count: count(),
217
- costUsd: sql`SUM(${conversationCost})::double precision`,
218
- date: executionDate,
219
- kind: juniorAutomationExecutions.kind
220
- }).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), eq(juniorAutomationExecutions.status, "completed"), options.access)).groupBy(executionDate, juniorAutomationExecutions.kind).orderBy(asc(executionDate), asc(juniorAutomationExecutions.kind));
232
+ const executionDate = sql`to_char(to_timestamp(${juniorAutomationRuns.completedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
233
+ const rows = await readCompletedExecutionBuckets({
234
+ access: options.access,
235
+ bucket: executionDate,
236
+ endMs,
237
+ startMs
238
+ });
221
239
  const byDate = /* @__PURE__ */ new Map();
222
240
  for (let offset = 0; offset < dayCount; offset += 1) {
223
241
  const date = utcDate(startMs + offset * 864e5);
@@ -239,14 +257,13 @@ async function readAutomationExecutionHours(hourCount = 168, options = {}) {
239
257
  end.setUTCMinutes(0, 0, 0);
240
258
  const endMs = end.getTime() + 3599999;
241
259
  const startMs = end.getTime() - (hourCount - 1) * 36e5;
242
- const executionHour = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
243
- const conversationCost = conversationUsageCostExpr(juniorConversations.usage);
244
- const rows = await getDb().select({
245
- count: count(),
246
- costUsd: sql`SUM(${conversationCost})::double precision`,
247
- date: executionHour,
248
- kind: juniorAutomationExecutions.kind
249
- }).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), eq(juniorAutomationExecutions.status, "completed"), options.access)).groupBy(executionHour, juniorAutomationExecutions.kind).orderBy(asc(executionHour), asc(juniorAutomationExecutions.kind));
260
+ const executionHour = sql`to_char(to_timestamp(${juniorAutomationRuns.completedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
261
+ const rows = await readCompletedExecutionBuckets({
262
+ access: options.access,
263
+ bucket: executionHour,
264
+ endMs,
265
+ startMs
266
+ });
250
267
  const byHour = /* @__PURE__ */ new Map();
251
268
  for (let offset = 0; offset < hourCount; offset += 1) {
252
269
  const date = new Date(startMs + offset * 36e5).toISOString().slice(0, 13);
@@ -261,82 +278,75 @@ async function readAutomationExecutionHours(hourCount = 168, options = {}) {
261
278
  }
262
279
  return [...byHour.values()];
263
280
  }
264
- /** Load the newest terminal automation execution linked to one conversation, if any. */
281
+ /** Count completed executions and their cost per time bucket and kind. */
282
+ async function readCompletedExecutionBuckets(args) {
283
+ const conversationCost = conversationUsageCostExpr(juniorConversations.usage);
284
+ return await getDb().select({
285
+ count: count(),
286
+ costUsd: sql`SUM(${conversationCost})::double precision`,
287
+ date: args.bucket,
288
+ kind: juniorAutomations.kind
289
+ }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId)).where(and(gte(juniorAutomationRuns.completedAtMs, args.startMs), lte(juniorAutomationRuns.completedAtMs, args.endMs), eq(juniorAutomationRuns.status, "completed"), args.access)).groupBy(args.bucket, juniorAutomations.kind).orderBy(asc(args.bucket), asc(juniorAutomations.kind));
290
+ }
291
+ /** Load the Automation run linked to one conversation, if any. */
265
292
  async function readAutomationExecutionByConversationId(args) {
266
- const namespace = args.namespace ?? "junior";
267
- const row = (await getDb().select({
268
- kind: juniorAutomationExecutions.kind,
269
- automationId: juniorAutomationExecutions.automationId
270
- }).from(juniorAutomationExecutions).where(and(eq(juniorAutomationExecutions.conversationId, args.conversationId), eq(juniorAutomationExecutions.namespace, namespace), inArray(juniorAutomationExecutions.kind, [...AUTOMATION_EXECUTION_TYPES]))).orderBy(desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId)).limit(1))[0];
271
- if (!row) return void 0;
272
- if (row.kind !== "scheduled" && row.kind !== "event") return void 0;
273
- return {
274
- kind: row.kind,
275
- automationId: row.automationId
276
- };
293
+ const [row] = await getDb().select({
294
+ kind: juniorAutomations.kind,
295
+ automationId: juniorAutomationRuns.automationId
296
+ }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).where(eq(juniorAutomationRuns.conversationId, args.conversationId)).limit(1);
297
+ return row;
277
298
  }
278
299
  /**
279
300
  * Load the display name of the Automation linked to each untitled
280
- * conversation. The newest execution wins. Like the Automation list, the name
281
- * falls back to the first line of the instruction. Titled conversations and
282
- * conversations without a linked Automation are absent from the result.
301
+ * conversation. Like the Automation list, the name falls back to the first
302
+ * line of the instruction. Titled conversations and conversations without a
303
+ * linked Automation are absent from the result.
283
304
  */
284
305
  async function readAutomationTitlesByConversationId(conversations) {
285
306
  const titles = /* @__PURE__ */ new Map();
286
307
  const untitledIds = conversations.flatMap((conversation) => conversation.title ? [] : [conversation.conversationId]);
287
308
  if (untitledIds.length === 0) return titles;
288
309
  const rows = await getDb().select({
289
- conversationId: juniorAutomationExecutions.conversationId,
310
+ conversationId: juniorAutomationRuns.conversationId,
290
311
  instruction: juniorAutomations.instruction,
291
312
  title: juniorAutomations.title
292
- }).from(juniorAutomationExecutions).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationExecutions.automationId)).where(and(inArray(juniorAutomationExecutions.conversationId, untitledIds), eq(juniorAutomationExecutions.namespace, "junior"))).orderBy(desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId));
293
- const seen = /* @__PURE__ */ new Set();
313
+ }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).where(inArray(juniorAutomationRuns.conversationId, untitledIds));
294
314
  for (const row of rows) {
295
- if (!row.conversationId || seen.has(row.conversationId)) continue;
296
- seen.add(row.conversationId);
297
315
  const title = row.title?.trim() || fallbackShortTitle(row.instruction, "");
298
- if (title) titles.set(row.conversationId, title);
316
+ if (row.conversationId && title) titles.set(row.conversationId, title);
299
317
  }
300
318
  return titles;
301
319
  }
302
320
  /** Load newest-first executions for one automation, with conversation titles when present. */
303
321
  async function readAutomationExecutions(args) {
304
- const namespace = args.namespace ?? "junior";
305
322
  return (await getDb().select({
306
- conversationId: juniorAutomationExecutions.conversationId,
323
+ conversationId: juniorAutomationRuns.conversationId,
307
324
  durationMs: juniorConversations.durationMs,
308
- executedAtMs: juniorAutomationExecutions.executedAtMs,
309
- executionId: juniorAutomationExecutions.executionId,
310
- status: juniorAutomationExecutions.status,
325
+ executedAtMs: juniorAutomationRuns.completedAtMs,
326
+ executionId: juniorAutomationRuns.id,
327
+ status: juniorAutomationRuns.status,
311
328
  title: juniorConversations.title,
312
329
  usage: juniorConversations.usage
313
- }).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(eq(juniorAutomationExecutions.kind, args.kind), eq(juniorAutomationExecutions.namespace, namespace), eq(juniorAutomationExecutions.automationId, args.automationId))).orderBy(desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId)).limit(args.limit)).map((row) => mapAutomationExecutionRecord(row));
330
+ }).from(juniorAutomationRuns).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId)).where(and(isExecution(), eq(juniorAutomationRuns.automationId, args.automationId))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => mapAutomationExecutionRecord(row));
314
331
  }
315
- /** Load newest-first executions for the supplied viewer-visible tasks. */
332
+ /** Load newest-first executions for the supplied viewer-visible automations. */
316
333
  async function readAutomationRuns(args) {
317
- const automations = args.automations ?? [];
318
- const conversationIds = [...new Set((args.conversationIds ?? []).filter((conversationId) => conversationId.trim().length > 0))];
319
- if (automations.length === 0 && conversationIds.length === 0) return [];
320
- const namespace = args.namespace ?? "junior";
321
- const selectors = [...automations.map((task) => and(eq(juniorAutomationExecutions.kind, task.kind), eq(juniorAutomationExecutions.automationId, task.automationId))), ...conversationIds.length > 0 ? [inArray(juniorAutomationExecutions.conversationId, conversationIds)] : []];
334
+ if (args.automationIds.length === 0) return [];
322
335
  return (await getDb().select({
323
- conversationId: juniorAutomationExecutions.conversationId,
336
+ conversationId: juniorAutomationRuns.conversationId,
324
337
  durationMs: juniorConversations.durationMs,
325
- executedAtMs: juniorAutomationExecutions.executedAtMs,
326
- executionId: juniorAutomationExecutions.executionId,
327
- kind: juniorAutomationExecutions.kind,
328
- status: juniorAutomationExecutions.status,
329
- automationId: juniorAutomationExecutions.automationId,
338
+ executedAtMs: juniorAutomationRuns.completedAtMs,
339
+ executionId: juniorAutomationRuns.id,
340
+ kind: juniorAutomations.kind,
341
+ status: juniorAutomationRuns.status,
342
+ automationId: juniorAutomationRuns.automationId,
330
343
  title: juniorConversations.title,
331
344
  usage: juniorConversations.usage
332
- }).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(eq(juniorAutomationExecutions.namespace, namespace), or(...selectors))).orderBy(desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId)).limit(args.limit)).flatMap((row) => {
333
- if (row.kind !== "scheduled" && row.kind !== "event") return [];
334
- return [{
335
- ...mapAutomationExecutionRecord(row),
336
- kind: row.kind,
337
- automationId: row.automationId
338
- }];
339
- });
345
+ }).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationRuns.conversationId)).where(and(isExecution(), inArray(juniorAutomationRuns.automationId, args.automationIds))).orderBy(desc(juniorAutomationRuns.completedAtMs), desc(juniorAutomationRuns.id)).limit(args.limit)).map((row) => ({
346
+ ...mapAutomationExecutionRecord(row),
347
+ kind: row.kind,
348
+ automationId: row.automationId
349
+ }));
340
350
  }
341
351
  function mapAutomationExecutionRecord(row) {
342
352
  const title = row.title?.trim();
@@ -357,16 +367,16 @@ function mapAutomationExecutionRecord(row) {
357
367
  /** Load a fixed trailing window of terminal executions for one automation by status. */
358
368
  async function readAutomationExecutionStatusDays(args) {
359
369
  const dayCount = args.dayCount ?? 90;
360
- const namespace = args.namespace ?? "junior";
361
370
  const end = utcDate(args.nowMs ?? Date.now());
362
371
  const endMs = Date.parse(`${end}T23:59:59.999Z`);
363
372
  const startMs = Date.parse(`${end}T00:00:00.000Z`) - (dayCount - 1) * 864e5;
364
- const executionDate = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
365
- const rows = await getDb().select({
366
- count: count(),
367
- date: executionDate,
368
- status: juniorAutomationExecutions.status
369
- }).from(juniorAutomationExecutions).where(and(eq(juniorAutomationExecutions.kind, args.kind), eq(juniorAutomationExecutions.namespace, namespace), eq(juniorAutomationExecutions.automationId, args.automationId), gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), inArray(juniorAutomationExecutions.status, [...AUTOMATION_EXECUTION_STATUSES]))).groupBy(executionDate, juniorAutomationExecutions.status).orderBy(asc(executionDate), asc(juniorAutomationExecutions.status));
373
+ const executionDate = sql`to_char(to_timestamp(${juniorAutomationRuns.completedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
374
+ const rows = await readExecutionStatusBuckets({
375
+ automationId: args.automationId,
376
+ bucket: executionDate,
377
+ endMs,
378
+ startMs
379
+ });
370
380
  const byDate = /* @__PURE__ */ new Map();
371
381
  for (let offset = 0; offset < dayCount; offset += 1) {
372
382
  const date = utcDate(startMs + offset * 864e5);
@@ -384,18 +394,18 @@ async function readAutomationExecutionStatusDays(args) {
384
394
  /** Load a fixed trailing window of terminal executions for one automation by hour. */
385
395
  async function readAutomationExecutionStatusHours(args) {
386
396
  const hourCount = args.hourCount ?? 168;
387
- const namespace = args.namespace ?? "junior";
388
397
  const nowMs = args.nowMs ?? Date.now();
389
398
  const end = new Date(nowMs);
390
399
  end.setUTCMinutes(0, 0, 0);
391
400
  const endMs = end.getTime() + 3599999;
392
401
  const startMs = end.getTime() - (hourCount - 1) * 36e5;
393
- const executionHour = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
394
- const rows = await getDb().select({
395
- count: count(),
396
- date: executionHour,
397
- status: juniorAutomationExecutions.status
398
- }).from(juniorAutomationExecutions).where(and(eq(juniorAutomationExecutions.kind, args.kind), eq(juniorAutomationExecutions.namespace, namespace), eq(juniorAutomationExecutions.automationId, args.automationId), gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), inArray(juniorAutomationExecutions.status, [...AUTOMATION_EXECUTION_STATUSES]))).groupBy(executionHour, juniorAutomationExecutions.status).orderBy(asc(executionHour), asc(juniorAutomationExecutions.status));
402
+ const executionHour = sql`to_char(to_timestamp(${juniorAutomationRuns.completedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
403
+ const rows = await readExecutionStatusBuckets({
404
+ automationId: args.automationId,
405
+ bucket: executionHour,
406
+ endMs,
407
+ startMs
408
+ });
399
409
  const byHour = /* @__PURE__ */ new Map();
400
410
  for (let offset = 0; offset < hourCount; offset += 1) {
401
411
  const date = new Date(startMs + offset * 36e5).toISOString().slice(0, 13);
@@ -410,6 +420,14 @@ async function readAutomationExecutionStatusHours(args) {
410
420
  }
411
421
  return [...byHour.values()];
412
422
  }
423
+ /** Count one automation's executions per time bucket and status. */
424
+ async function readExecutionStatusBuckets(args) {
425
+ return await getDb().select({
426
+ count: count(),
427
+ date: args.bucket,
428
+ status: juniorAutomationRuns.status
429
+ }).from(juniorAutomationRuns).where(and(isExecution(), eq(juniorAutomationRuns.automationId, args.automationId), gte(juniorAutomationRuns.completedAtMs, args.startMs), lte(juniorAutomationRuns.completedAtMs, args.endMs))).groupBy(args.bucket, juniorAutomationRuns.status).orderBy(asc(args.bucket), asc(juniorAutomationRuns.status));
430
+ }
413
431
  //#endregion
414
432
  //#region src/chat/automations/blocked-notice.ts
415
433
  function buildBlockedNoticeText(automationId, title, reason) {
@@ -686,9 +704,9 @@ async function recordEventAutomationExecution(previous, next, status) {
686
704
  if (next.plugin !== "junior") return;
687
705
  const eventAutomationId = next.metadata?.eventAutomationId;
688
706
  if (!eventAutomationId) return;
689
- const conversationId = getDispatchConversationId(next);
690
- await recordAutomationExecution("event", eventAutomationId, {
691
- ...await getConversationStore().get({ conversationId }) ? { conversationId } : void 0,
707
+ await recordAutomationExecution(eventAutomationId, {
708
+ conversationId: getDispatchConversationId(next),
709
+ errorMessage: next.errorMessage,
692
710
  executionId: next.id,
693
711
  nowMs: next.updatedAtMs,
694
712
  status
@@ -1019,4 +1037,4 @@ async function processPluginTask(message, options = {}) {
1019
1037
  });
1020
1038
  }
1021
1039
  //#endregion
1022
- export { readAutomationTitlesByConversationId as A, readAutomationExecutionDays as C, readAutomationExecutionSummaries as D, readAutomationExecutionStatusHours as E, conversationActiveDaysColumn as M, conversationAggregateColumns as N, readAutomationExecutions as O, conversationRangeColumns as P, readAutomationExecutionByConversationId as S, readAutomationExecutionStatusDays as T, markDispatchCompleted as _, confirmDispatchMailboxAppend as a, notifyAutomationBlocked as b, getDispatchIdForConversation as c, getDispatchTurnId as d, getPluginDispatchProjection as f, markDispatchBlocked as g, markDispatchAwaitingResume as h, claimDispatchMailboxAppend as i, recordAutomationExecution as j, readAutomationRuns as k, getDispatchInputMessageId as l, listPendingDispatchMailboxAppends as m, scheduleSessionCompletedPluginTasks as n, createOrGetDispatch as o, isTerminalDispatchStatus as p, task_runner_exports as r, getDispatchConversationId as s, processPluginTask as t, getDispatchRecord as u, markDispatchFailed as v, readAutomationExecutionHours as w, emptyAutomationRunWindows as x, markDispatchRunning as y };
1040
+ export { readAutomationRuns as A, readAutomationExecutionByConversationId as C, readAutomationExecutionStatusHours as D, readAutomationExecutionStatusDays as E, conversationRangeColumns as F, recordAutomationExecution as M, conversationActiveDaysColumn as N, readAutomationExecutionSummaries as O, conversationAggregateColumns as P, existingConversationId as S, readAutomationExecutionHours as T, markDispatchCompleted as _, confirmDispatchMailboxAppend as a, notifyAutomationBlocked as b, getDispatchIdForConversation as c, getDispatchTurnId as d, getPluginDispatchProjection as f, markDispatchBlocked as g, markDispatchAwaitingResume as h, claimDispatchMailboxAppend as i, readAutomationTitlesByConversationId as j, readAutomationExecutions as k, getDispatchInputMessageId as l, listPendingDispatchMailboxAppends as m, scheduleSessionCompletedPluginTasks as n, createOrGetDispatch as o, isTerminalDispatchStatus as p, task_runner_exports as r, getDispatchConversationId as s, processPluginTask as t, getDispatchRecord as u, markDispatchFailed as v, readAutomationExecutionDays as w, emptyAutomationRunWindows as x, markDispatchRunning as y };
@@ -1,16 +1,16 @@
1
- import { d as unwrapCurrentInstruction, f as escapeXml, i as completeText, o as resolveGatewayModel } from "./client-ROZeVjq0.js";
2
- import { d as logException, o as getActiveTraceId, p as logWarn, s as getLogContextAttributes, t as buildTurnFailureResponse, v as setSpanAttributes } from "./logging-COUsTFIL.js";
1
+ import { d as unwrapCurrentInstruction, f as escapeXml, i as completeText, o as resolveGatewayModel } from "./client-oBgOmQV6.js";
2
+ import { d as logException, o as getActiveTraceId, p as logWarn, s as getLogContextAttributes, t as buildTurnFailureResponse, v as setSpanAttributes } from "./logging-B3nJLXT7.js";
3
3
  import { n as captureMessage } from "./sentry-CgWZM_kV.js";
4
- import { n as botConfig, v as normalizeSlackEmojiName } from "./config-DNNkxfGS.js";
4
+ import { n as botConfig, v as normalizeSlackEmojiName } from "./config-CRlLncIj.js";
5
5
  import { c as toStoredSlackActor, f as parseSlackUserId, g as parseSlackMessageTs } from "./actor-BTtLaeWy.js";
6
- import { c as getConversationEventStore, f as getSqlExecutor, m as withConversationEventLock, p as createSqlConversationEventStore, u as getConversationStore, v as startLocationThread, x as addAgentTurnUsage, y as bindProviderConversation } from "./viewer-D96vo_42.js";
7
- import { a as juniorSchedulerRuns, o as juniorAutomationVersions, s as juniorAutomations } from "./executor-_4bdoCxe.js";
6
+ import { c as getConversationEventStore, f as getSqlExecutor, m as withConversationEventLock, p as createSqlConversationEventStore, u as getConversationStore, v as startLocationThread, x as addAgentTurnUsage, y as bindProviderConversation } from "./viewer-CGZiUVMU.js";
7
+ import { a as juniorAutomationVersions, o as juniorAutomationRuns, s as juniorAutomations } from "./executor-B88oEqok.js";
8
8
  import { d as contextProvenance, f as instructionActors, n as agentHistoryItemSchema, p as instructionProvenanceFor } from "./history-9pO1Zd4Y.js";
9
- import { p as credentialSubjectSchema } from "./registry-CeEpPna6.js";
10
- import { $t as authenticationUnlinkedEvent, Gt as normalizeSlackConversationId, Ht as getSlackClient, Qt as authenticationLinkedEvent, Rt as SlackActionError, Xt as JUNIOR_NATIVE_EVENT_NAMESPACE, Zt as agentsInstructionsUpdatedEvent, n as applyPluginFormatMarkdown, qt as withSlackRetries } from "./agent-hooks-DcoKnnxn.js";
9
+ import { p as credentialSubjectSchema } from "./registry-C-jns0Be.js";
10
+ import { $t as authenticationUnlinkedEvent, Gt as normalizeSlackConversationId, Ht as getSlackClient, Qt as authenticationLinkedEvent, Rt as SlackActionError, Xt as JUNIOR_NATIVE_EVENT_NAMESPACE, Zt as agentsInstructionsUpdatedEvent, n as applyPluginFormatMarkdown, qt as withSlackRetries } from "./agent-hooks-DfPubHYm.js";
11
11
  import { G as messageCardRef } from "./conversation-T18Xlkx9.js";
12
- import { D as fenceLock, O as withLock, T as MUTATION_LOCK_TTL_MS, k as sleep, w as getStateAdapter } from "./profile-DP4LKL6V.js";
13
- import { A as eventAutomationSchema, D as scheduledRunSchema, E as scheduledAutomationSchema, S as automationVersionSchema } from "./automation-gznOMaQ2.js";
12
+ import { D as fenceLock, O as withLock, T as MUTATION_LOCK_TTL_MS, k as sleep, w as getStateAdapter } from "./profile-Dh73O6lZ.js";
13
+ import { A as eventAutomationSchema, E as scheduledAutomationSchema, S as automationVersionSchema } from "./automation-gznOMaQ2.js";
14
14
  import { isDeepStrictEqual } from "node:util";
15
15
  import { z } from "zod";
16
16
  import { createHash } from "node:crypto";
@@ -3240,28 +3240,17 @@ async function listScheduledAutomationsForTeam(db, teamId) {
3240
3240
  return await readListedScheduledAutomations(db, teamId);
3241
3241
  }
3242
3242
  async function writeScheduledAutomation(db, task, current, editedBy) {
3243
- if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) await db.delete(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.id, scheduledRunId(task.id, task.nextRunAtMs)), eq(juniorSchedulerRuns.status, "blocked")));
3243
+ if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) await db.delete(juniorAutomationRuns).where(and(eq(juniorAutomationRuns.id, scheduledRunId(task.id, task.nextRunAtMs)), eq(juniorAutomationRuns.status, "blocked")));
3244
3244
  if (task.status === "deleted" && current?.status !== "deleted") await skipPendingRunsForDeletedTask(db, task);
3245
3245
  await upsertScheduledAutomation(db, task);
3246
3246
  await recordAutomationVersion(db, task, current, editedBy);
3247
3247
  }
3248
3248
  async function skipPendingRunsForDeletedTask(db, task) {
3249
- const errorMessage = `Scheduled automation ${task.id} was deleted before the run started.`;
3250
- const rows = await db.select({ record: juniorSchedulerRuns.record }).from(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.taskId, task.id), eq(juniorSchedulerRuns.status, "pending")));
3251
- for (const row of rows) {
3252
- const parsed = scheduledRunSchema.safeParse(row.record);
3253
- if (!parsed.success || parsed.data.status !== "pending") continue;
3254
- const next = scheduledRunSchema.parse({
3255
- ...parsed.data,
3256
- completedAtMs: task.updatedAtMs,
3257
- errorMessage,
3258
- status: "skipped"
3259
- });
3260
- await db.update(juniorSchedulerRuns).set({
3261
- record: next,
3262
- status: next.status
3263
- }).where(and(eq(juniorSchedulerRuns.id, next.id), eq(juniorSchedulerRuns.status, "pending")));
3264
- }
3249
+ await db.update(juniorAutomationRuns).set({
3250
+ completedAtMs: task.updatedAtMs,
3251
+ errorMessage: `Scheduled automation ${task.id} was deleted before the run started.`,
3252
+ status: "skipped"
3253
+ }).where(and(eq(juniorAutomationRuns.automationId, task.id), eq(juniorAutomationRuns.status, "pending")));
3265
3254
  }
3266
3255
  /** Create a scheduled automation once under its retry-stable task lock. */
3267
3256
  async function createScheduledAutomation(db, task) {
@@ -1,10 +1,10 @@
1
- import { I as isRecord, L as toOptionalNumber, R as toOptionalString } from "./logging-COUsTFIL.js";
2
- import { d as getDb } from "./viewer-D96vo_42.js";
3
- import { d as juniorDestinations } from "./agent-invocations-D_YoNnAn.js";
4
- import { u as juniorLocationConfigurations } from "./executor-_4bdoCxe.js";
5
- import { Kt as persistConversationMessages } from "./tasks-DwGlFX9i.js";
6
- import { it as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-DcoKnnxn.js";
7
- import { w as getStateAdapter } from "./profile-DP4LKL6V.js";
1
+ import { I as isRecord, L as toOptionalNumber, R as toOptionalString } from "./logging-B3nJLXT7.js";
2
+ import { d as getDb } from "./viewer-CGZiUVMU.js";
3
+ import { d as juniorDestinations } from "./agent-invocations-BY60iNw-.js";
4
+ import { l as juniorLocationConfigurations } from "./executor-B88oEqok.js";
5
+ import { Kt as persistConversationMessages } from "./tasks-DO9VM9cu.js";
6
+ import { it as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-DfPubHYm.js";
7
+ import { w as getStateAdapter } from "./profile-Dh73O6lZ.js";
8
8
  import { z } from "zod";
9
9
  import { randomUUID } from "node:crypto";
10
10
  import { and, eq, sql } from "drizzle-orm";
@@ -1,13 +1,13 @@
1
- import { _ as getProviderErrorUserMessage, g as getProviderErrorAttributes, h as findProviderError, p as ProviderError } from "./client-ROZeVjq0.js";
2
- import { I as isRecord, t as buildTurnFailureResponse } from "./logging-COUsTFIL.js";
1
+ import { _ as getProviderErrorUserMessage, g as getProviderErrorAttributes, h as findProviderError, p as ProviderError } from "./client-oBgOmQV6.js";
2
+ import { I as isRecord, t as buildTurnFailureResponse } from "./logging-B3nJLXT7.js";
3
3
  import { g as parseSlackMessageTs } from "./actor-BTtLaeWy.js";
4
- import { c as getConversationEventStore, d as getDb } from "./viewer-D96vo_42.js";
5
- import { Gt as hydrateConversationMessages, an as markConversationMessage } from "./tasks-DwGlFX9i.js";
6
- import { Nt as listConversationAnnotations } from "./agent-hooks-DcoKnnxn.js";
4
+ import { c as getConversationEventStore, d as getDb } from "./viewer-CGZiUVMU.js";
5
+ import { Gt as hydrateConversationMessages, an as markConversationMessage } from "./tasks-DO9VM9cu.js";
6
+ import { Nt as listConversationAnnotations } from "./agent-hooks-DfPubHYm.js";
7
7
  import { J as removedCardSchema, Y as resolveMessageCards, q as readMessageCardRefs } from "./conversation-T18Xlkx9.js";
8
- import { r as clearPendingAuth, u as buildDeterministicTurnId } from "./pending-auth-DDx3EU4M.js";
8
+ import { r as clearPendingAuth, u as buildDeterministicTurnId } from "./pending-auth-m8aJlwP_.js";
9
9
  import { d as markTurnCompleted, u as markTurnClosed } from "./oauth-authorization-BA4Dt6a7.js";
10
- import { o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-Ces6tXmB.js";
10
+ import { o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-FreaCOZD.js";
11
11
  //#region src/chat/conversations/pending-cards.ts
12
12
  /** Select undelivered cards and load their latest saved facts. */
13
13
  async function loadPendingMessageCards(conversationId) {