@sentry/junior 0.251.0 → 0.253.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.
- package/dist/{admin-Dth5-1lw.js → admin-CZnlnlg9.js} +1 -1
- package/dist/{agent-hooks-Ctw_jKaS.js → agent-hooks-CvcbAtJH.js} +8 -8
- package/dist/{agent-runner-dgQdPoGH.js → agent-runner-B4FMVbCs.js} +20 -20
- package/dist/api/schema.d.ts +32 -32
- package/dist/api.js +22 -22
- package/dist/app.js +122 -84
- package/dist/{assistant-reply-DBFRnZRx.js → assistant-reply-BU05IECs.js} +1 -1
- package/dist/{briefs-SDwPKduW.js → briefs-DyspoG90.js} +2 -2
- package/dist/{catalog-runtime-ulFceOA-.js → catalog-runtime-Br8s-TYx.js} +1 -1
- package/dist/cli/chat.js +15 -15
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +2 -2
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-DMt9kArs.js → client-CRVXpEWi.js} +2 -2
- package/dist/{config-iEOq080E.js → config-B7joRWt7.js} +2 -2
- package/dist/{connections-BFU6GarA.js → connections-CGPYNw2B.js} +9 -9
- package/dist/{conversation-privacy-yZhQHnBe.js → conversation-privacy-0JuTT_OJ.js} +1 -1
- package/dist/{credential-sync-GW0Rj57H.js → credential-sync-C-k3fnY5.js} +14 -14
- package/dist/{executor-BeH9LDKI.js → executor-YL577nNt.js} +88 -20
- package/dist/{images-hIXWwpRg.js → images-DopOctPc.js} +2 -2
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-Bxlao6zL.js → job-queue-CIhKXUE3.js} +1 -1
- package/dist/{logging-BkI10z0D.js → logging-B0QxCh3D.js} +1 -1
- package/dist/{message-BUcVfXmK.js → message-DQvor8lM.js} +3 -3
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-CYSEV35l.js → oauth-callback-server-BT7cSxBm.js} +2 -2
- package/dist/{pending-auth-h0B3FQEo.js → pending-auth-CwA1uLA9.js} +2 -2
- package/dist/{profile-AsD2uz0m.js → profile-p1oiViVs.js} +4 -4
- package/dist/{registry-CN2Nie72.js → registry-BO2LM2oW.js} +1 -1
- package/dist/{resolve-CrgwPWTS.js → resolve-CtWKGuKE.js} +2 -2
- package/dist/{result-Cokw_MYC.js → result-DR5JTBcb.js} +6 -6
- package/dist/{runner-D-VZlBb_.js → runner-BIxTJF9a.js} +9 -9
- package/dist/{sandbox-egress-signals-Sie51RW0.js → sandbox-egress-signals-DF3Pu813.js} +1 -1
- package/dist/{session-stop-DD0k0BXI.js → session-stop-BRq8Vxrr.js} +10 -10
- package/dist/{skills-CpyJJyqH.js → skills-DitcC3Vw.js} +2 -2
- package/dist/{spawn-DX3YjDtm.js → spawn-CHQ42PAo.js} +2 -2
- package/dist/{store-D-n2b2cI.js → store-DIFp9K52.js} +6 -6
- package/dist/{task-queue-Bj2hFpjr.js → task-queue-UAeJGLYM.js} +3 -3
- package/dist/{task-runner-B3g16wjr.js → task-runner-DwD-i2ta.js} +147 -131
- package/dist/{tasks-DDzBxMio.js → tasks-DThIG7F-.js} +136 -218
- package/dist/{thread-state-BlD6loX7.js → thread-state-BQxkL7Xm.js} +6 -6
- package/dist/{turn-failure-response-BRlfATgu.js → turn-failure-response-OAIxgzNh.js} +7 -7
- package/dist/{turn-lifecycle-hMCbr_Ah.js → turn-lifecycle-BAEJpjDI.js} +1 -1
- package/dist/{turn-session-routing-DLb6eQMT.js → turn-session-routing-F5s728f0.js} +1 -1
- package/dist/{validation-C2lG6yVH.js → validation-DFM4-vDr.js} +1 -1
- package/dist/{version-CyJ_kMe8.js → version-B80CD9sN.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{viewer-C8u1HAdN.js → viewer-CPxNfHWa.js} +3 -3
- package/dist/{web-input-DHgIfSwj.js → web-input-DIO177R0.js} +80 -121
- package/dist/{work-hoPN8qjl.js → work-Dl5KPKjW.js} +9 -9
- package/dist/{worker-CavJZxi8.js → worker-CJkE86Co.js} +5 -5
- package/migrations/0055_automations.sql +125 -0
- package/migrations/0056_automation_runs.sql +69 -0
- package/migrations/meta/0055_snapshot.json +3948 -0
- package/migrations/meta/0056_snapshot.json +4280 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -4
|
@@ -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-
|
|
2
|
+
import { h as runBestEffort, p as logWarn } from "./logging-B0QxCh3D.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-
|
|
4
|
+
import { C as agentTurnTotalTokens, S as agentTurnCostUsd, d as getDb, r as readActorIdentity, u as getConversationStore } from "./viewer-CPxNfHWa.js";
|
|
5
5
|
import { s as juniorConversations } from "./agent-invocations-D_YoNnAn.js";
|
|
6
|
-
import {
|
|
6
|
+
import { o as juniorAutomationRuns, s as juniorAutomations } from "./executor-YL577nNt.js";
|
|
7
7
|
import { m as sameActorIdentity } from "./history-9pO1Zd4Y.js";
|
|
8
|
-
import { p as credentialSubjectSchema } from "./registry-
|
|
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-
|
|
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-
|
|
11
|
-
import { w as getStateAdapter } from "./profile-
|
|
12
|
-
import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-
|
|
13
|
-
import { t as resolveConversationRouting } from "./turn-session-routing-
|
|
14
|
-
import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-
|
|
8
|
+
import { p as credentialSubjectSchema } from "./registry-BO2LM2oW.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-DThIG7F-.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-CvcbAtJH.js";
|
|
11
|
+
import { w as getStateAdapter } from "./profile-p1oiViVs.js";
|
|
12
|
+
import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-BQxkL7Xm.js";
|
|
13
|
+
import { t as resolveConversationRouting } from "./turn-session-routing-F5s728f0.js";
|
|
14
|
+
import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-UAeJGLYM.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,
|
|
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
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
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(
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
|
172
|
-
async function readAutomationExecutionSummaries(
|
|
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
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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(
|
|
188
|
-
runsLast1Day: sql`count(*) filter (where ${
|
|
189
|
-
runsLast7Days: sql`count(*) filter (where ${
|
|
190
|
-
runsLast30Days: sql`count(*) filter (where ${
|
|
191
|
-
runsLast90Days: sql`count(*) filter (where ${
|
|
192
|
-
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(
|
|
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(${
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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(${
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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,84 +278,75 @@ async function readAutomationExecutionHours(hourCount = 168, options = {}) {
|
|
|
261
278
|
}
|
|
262
279
|
return [...byHour.values()];
|
|
263
280
|
}
|
|
264
|
-
/**
|
|
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
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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.
|
|
281
|
-
*
|
|
282
|
-
*
|
|
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:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
scheduledTitle: juniorSchedulerTasks.title
|
|
294
|
-
}).from(juniorAutomationExecutions).leftJoin(juniorSchedulerTasks, and(eq(juniorAutomationExecutions.kind, "scheduled"), eq(juniorSchedulerTasks.id, juniorAutomationExecutions.automationId))).leftJoin(juniorEventAutomations, and(eq(juniorAutomationExecutions.kind, "event"), eq(juniorEventAutomations.id, juniorAutomationExecutions.automationId))).where(and(inArray(juniorAutomationExecutions.conversationId, untitledIds), eq(juniorAutomationExecutions.namespace, "junior"))).orderBy(desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId));
|
|
295
|
-
const seen = /* @__PURE__ */ new Set();
|
|
310
|
+
conversationId: juniorAutomationRuns.conversationId,
|
|
311
|
+
instruction: juniorAutomations.instruction,
|
|
312
|
+
title: juniorAutomations.title
|
|
313
|
+
}).from(juniorAutomationRuns).innerJoin(juniorAutomations, eq(juniorAutomations.id, juniorAutomationRuns.automationId)).where(inArray(juniorAutomationRuns.conversationId, untitledIds));
|
|
296
314
|
for (const row of rows) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const title = (row.scheduledTitle ?? row.eventTitle)?.trim() || fallbackShortTitle(row.scheduledInstruction ?? row.eventInstruction ?? "", "");
|
|
300
|
-
if (title) titles.set(row.conversationId, title);
|
|
315
|
+
const title = row.title?.trim() || fallbackShortTitle(row.instruction, "");
|
|
316
|
+
if (row.conversationId && title) titles.set(row.conversationId, title);
|
|
301
317
|
}
|
|
302
318
|
return titles;
|
|
303
319
|
}
|
|
304
320
|
/** Load newest-first executions for one automation, with conversation titles when present. */
|
|
305
321
|
async function readAutomationExecutions(args) {
|
|
306
|
-
const namespace = args.namespace ?? "junior";
|
|
307
322
|
return (await getDb().select({
|
|
308
|
-
conversationId:
|
|
323
|
+
conversationId: juniorAutomationRuns.conversationId,
|
|
309
324
|
durationMs: juniorConversations.durationMs,
|
|
310
|
-
executedAtMs:
|
|
311
|
-
executionId:
|
|
312
|
-
status:
|
|
325
|
+
executedAtMs: juniorAutomationRuns.completedAtMs,
|
|
326
|
+
executionId: juniorAutomationRuns.id,
|
|
327
|
+
status: juniorAutomationRuns.status,
|
|
313
328
|
title: juniorConversations.title,
|
|
314
329
|
usage: juniorConversations.usage
|
|
315
|
-
}).from(
|
|
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));
|
|
316
331
|
}
|
|
317
|
-
/** Load newest-first executions for the supplied viewer-visible
|
|
332
|
+
/** Load newest-first executions for the supplied viewer-visible automations. */
|
|
318
333
|
async function readAutomationRuns(args) {
|
|
319
|
-
|
|
320
|
-
const conversationIds = [...new Set((args.conversationIds ?? []).filter((conversationId) => conversationId.trim().length > 0))];
|
|
321
|
-
if (automations.length === 0 && conversationIds.length === 0) return [];
|
|
322
|
-
const namespace = args.namespace ?? "junior";
|
|
323
|
-
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 [];
|
|
324
335
|
return (await getDb().select({
|
|
325
|
-
conversationId:
|
|
336
|
+
conversationId: juniorAutomationRuns.conversationId,
|
|
326
337
|
durationMs: juniorConversations.durationMs,
|
|
327
|
-
executedAtMs:
|
|
328
|
-
executionId:
|
|
329
|
-
kind:
|
|
330
|
-
status:
|
|
331
|
-
automationId:
|
|
338
|
+
executedAtMs: juniorAutomationRuns.completedAtMs,
|
|
339
|
+
executionId: juniorAutomationRuns.id,
|
|
340
|
+
kind: juniorAutomations.kind,
|
|
341
|
+
status: juniorAutomationRuns.status,
|
|
342
|
+
automationId: juniorAutomationRuns.automationId,
|
|
332
343
|
title: juniorConversations.title,
|
|
333
344
|
usage: juniorConversations.usage
|
|
334
|
-
}).from(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
automationId: row.automationId
|
|
340
|
-
}];
|
|
341
|
-
});
|
|
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
|
+
}));
|
|
342
350
|
}
|
|
343
351
|
function mapAutomationExecutionRecord(row) {
|
|
344
352
|
const title = row.title?.trim();
|
|
@@ -359,16 +367,16 @@ function mapAutomationExecutionRecord(row) {
|
|
|
359
367
|
/** Load a fixed trailing window of terminal executions for one automation by status. */
|
|
360
368
|
async function readAutomationExecutionStatusDays(args) {
|
|
361
369
|
const dayCount = args.dayCount ?? 90;
|
|
362
|
-
const namespace = args.namespace ?? "junior";
|
|
363
370
|
const end = utcDate(args.nowMs ?? Date.now());
|
|
364
371
|
const endMs = Date.parse(`${end}T23:59:59.999Z`);
|
|
365
372
|
const startMs = Date.parse(`${end}T00:00:00.000Z`) - (dayCount - 1) * 864e5;
|
|
366
|
-
const executionDate = sql`to_char(to_timestamp(${
|
|
367
|
-
const rows = await
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
+
});
|
|
372
380
|
const byDate = /* @__PURE__ */ new Map();
|
|
373
381
|
for (let offset = 0; offset < dayCount; offset += 1) {
|
|
374
382
|
const date = utcDate(startMs + offset * 864e5);
|
|
@@ -386,18 +394,18 @@ async function readAutomationExecutionStatusDays(args) {
|
|
|
386
394
|
/** Load a fixed trailing window of terminal executions for one automation by hour. */
|
|
387
395
|
async function readAutomationExecutionStatusHours(args) {
|
|
388
396
|
const hourCount = args.hourCount ?? 168;
|
|
389
|
-
const namespace = args.namespace ?? "junior";
|
|
390
397
|
const nowMs = args.nowMs ?? Date.now();
|
|
391
398
|
const end = new Date(nowMs);
|
|
392
399
|
end.setUTCMinutes(0, 0, 0);
|
|
393
400
|
const endMs = end.getTime() + 3599999;
|
|
394
401
|
const startMs = end.getTime() - (hourCount - 1) * 36e5;
|
|
395
|
-
const executionHour = sql`to_char(to_timestamp(${
|
|
396
|
-
const rows = await
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
+
});
|
|
401
409
|
const byHour = /* @__PURE__ */ new Map();
|
|
402
410
|
for (let offset = 0; offset < hourCount; offset += 1) {
|
|
403
411
|
const date = new Date(startMs + offset * 36e5).toISOString().slice(0, 13);
|
|
@@ -412,6 +420,14 @@ async function readAutomationExecutionStatusHours(args) {
|
|
|
412
420
|
}
|
|
413
421
|
return [...byHour.values()];
|
|
414
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
|
+
}
|
|
415
431
|
//#endregion
|
|
416
432
|
//#region src/chat/automations/blocked-notice.ts
|
|
417
433
|
function buildBlockedNoticeText(automationId, title, reason) {
|
|
@@ -688,9 +704,9 @@ async function recordEventAutomationExecution(previous, next, status) {
|
|
|
688
704
|
if (next.plugin !== "junior") return;
|
|
689
705
|
const eventAutomationId = next.metadata?.eventAutomationId;
|
|
690
706
|
if (!eventAutomationId) return;
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
707
|
+
await recordAutomationExecution(eventAutomationId, {
|
|
708
|
+
conversationId: getDispatchConversationId(next),
|
|
709
|
+
errorMessage: next.errorMessage,
|
|
694
710
|
executionId: next.id,
|
|
695
711
|
nowMs: next.updatedAtMs,
|
|
696
712
|
status
|
|
@@ -1021,4 +1037,4 @@ async function processPluginTask(message, options = {}) {
|
|
|
1021
1037
|
});
|
|
1022
1038
|
}
|
|
1023
1039
|
//#endregion
|
|
1024
|
-
export {
|
|
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 };
|