@sentry/junior 0.224.0 → 0.226.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/{agent-hooks-DNLBwmsh.js → agent-hooks-FUetyyLe.js} +23 -16
- package/dist/{agent-runner-9g6TQk9y.js → agent-runner-DEF7auSj.js} +3114 -318
- package/dist/api/schema.d.ts +353 -10
- package/dist/api/schema.js +4 -4
- package/dist/api.js +22 -18
- package/dist/{app-BbSD6EtD.d.ts → app-TVL42pGL.d.ts} +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.js +893 -7660
- package/dist/{briefs-CjXnGKzZ.js → briefs-Cb4T2BUI.js} +5 -5
- package/dist/{catalog-runtime-CVZ9TEuK.js → catalog-runtime-BYGR3Dr4.js} +1 -1
- package/dist/{checkpoint-Ceck_fLO.js → checkpoint-CJe-dZFF.js} +76 -10
- package/dist/cli/chat.js +13 -13
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-B27ufhzl.js → client-C2WBBx7m.js} +2 -2
- package/dist/{config-BAhl7-RC.js → config-Bws6Rpsh.js} +11 -13
- package/dist/{conversation-1HUoZr2l.js → conversation-DPbmIwjy.js} +67 -6
- package/dist/{conversation-privacy-C-4JQzKD.js → conversation-privacy-pCcW0a5Y.js} +1 -1
- package/dist/{credential-sync-BXHIxbtS.js → credential-sync-ibuNsMyl.js} +14 -134
- package/dist/{executor-BG2Armby.js → executor-D36FWjcf.js} +1 -1
- package/dist/{history-yaSjGMD_.d.ts → history-DI9KJT3J.d.ts} +38 -1
- package/dist/instrumentation.js +2 -2
- package/dist/{vercel-queue-DIkVcfK4.js → job-queue-2mFFDMHB.js} +61 -2
- package/dist/{logging-iY0ssa2q.js → logging-CvjMHRvg.js} +2 -2
- package/dist/{model-profile-CZmVoGdX.js → model-profile-BwU8mqwD.js} +14 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -4
- package/dist/{oauth-authorization-BIijVS2N.js → oauth-authorization-l4O1rXsL.js} +1 -1
- package/dist/{oauth-callback-server-DPkK3kzi.js → oauth-callback-server-Ds8M5FFX.js} +2 -2
- package/dist/pending-auth-DLNJbD8H.js +59 -0
- package/dist/{profile-6KWhg_-U.js → profile-dvV7PB7u.js} +4 -4
- package/dist/{prompt-90miy8VV.js → prompt-DehgjkO5.js} +1 -1
- package/dist/{registry-Ctgdjt-W.js → registry-C6FHctIu.js} +3 -3
- package/dist/{resolve-CB45ZukQ.js → resolve-DqwGI9wq.js} +2 -2
- package/dist/{runner-BwKCqoF3.js → runner-CzYEcB73.js} +9 -9
- package/dist/{sandbox-egress-signals-Cmy8ODat.js → sandbox-egress-signals-BZOv0fns.js} +1 -1
- package/dist/{schema-RpbW5Od2.js → schema-BZg-xzfG.js} +2 -2
- package/dist/{skills-c2bMDVRS.js → skills-EhiVbjF4.js} +2 -2
- package/dist/{spawn-Cg6lyjE7.js → spawn-Ck3L0rEr.js} +2 -2
- package/dist/{task-queue-SL4uLfBB.js → task-queue-TXhAWQwj.js} +3 -4
- package/dist/{task-runner-fntmnqzx.js → task-runner-axUJS1No.js} +380 -11
- package/dist/{thread-state-Wfr0GdKS.js → thread-state-CTuWhjru.js} +5 -5
- package/dist/{turn-failure-response-DLjSEm-R.js → turn-failure-response-csVoLOzx.js} +8 -9
- package/dist/{turn-lifecycle-5_jvLqRz.js → turn-lifecycle-C1S6vELh.js} +1 -1
- package/dist/{turn-session-routing-CQaysLoe.js → turn-session-routing-5RejBvit.js} +1 -1
- package/dist/{validation-CnZkCN7o.js → validation-q2PFxkTQ.js} +1 -1
- package/dist/{version-C6lA-h_o.js → version-ByLtIYyy.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/web-authorization-Biy8nsS7.js +162 -0
- package/dist/web-input-DM9LaEdn.js +7774 -0
- package/dist/{reporting-window-DbUqng0P.js → web-message-id-xPI_EFSC.js} +11 -1
- package/dist/{work-4Nhz9mVv.js → work-QrbHdXTc.js} +7 -8
- package/dist/{worker-CdWPsNL2.js → worker-r17aopXW.js} +5 -6
- package/package.json +4 -4
- package/dist/execution-stats-DikPOxJ1.js +0 -373
- package/dist/job-queue-D4oAVrL9.js +0 -63
- package/dist/short-title-xEkZwkcV.js +0 -65
- package/dist/stats-DP9zl3Jg.js +0 -2758
- package/dist/web-authorization-Bbx999_q.js +0 -38
- package/dist/web-input-BoE1xE-K.js +0 -899
|
@@ -1,16 +1,386 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
+
import { p as logWarn } from "./logging-CvjMHRvg.js";
|
|
2
3
|
import { _ as parseSlackMessageTs, r as credentialSubjectSchema } from "./context-Bplcgypl.js";
|
|
3
|
-
import { At as createPluginConversationEvents, Kt as getConversationStore, Q as readActorIdentity, _t as createPluginState, bt as createPluginLogger, p as coreTaskRegistrations, pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb, u as getPlugins, vt as createPluginEmbedder, yt as createPluginModel } from "./agent-hooks-
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { w as
|
|
7
|
-
import {
|
|
8
|
-
import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-
|
|
9
|
-
import { t as resolveConversationRouting } from "./turn-session-routing-
|
|
10
|
-
import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-
|
|
4
|
+
import { At as createPluginConversationEvents, Kt as getConversationStore, Q as readActorIdentity, _t as createPluginState, bt as createPluginLogger, nn as agentTurnCostUsd, p as coreTaskRegistrations, pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb, rn as agentTurnTotalTokens, u as getPlugins, vt as createPluginEmbedder, yt as createPluginModel } from "./agent-hooks-FUetyyLe.js";
|
|
5
|
+
import { E as juniorConversations, s as juniorAutomationExecutions } from "./executor-D36FWjcf.js";
|
|
6
|
+
import { Z as sameActorIdentity } from "./conversation-DPbmIwjy.js";
|
|
7
|
+
import { C as isToolResultError, D as stripRuntimeTurnContext, J as hydrateConversationMessages, T as normalizeToolNameFromResult, b as instructionTextForProjection, g as getPiMessageRole, s as getTurnRecord, w as isToolResultMessage } from "./checkpoint-CJe-dZFF.js";
|
|
8
|
+
import { w as getStateAdapter } from "./profile-dvV7PB7u.js";
|
|
9
|
+
import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-CTuWhjru.js";
|
|
10
|
+
import { t as resolveConversationRouting } from "./turn-session-routing-5RejBvit.js";
|
|
11
|
+
import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-TXhAWQwj.js";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
import { createHash } from "node:crypto";
|
|
14
|
+
import { and, asc, count, desc, eq, gte, inArray, lte, max, or, sql } from "drizzle-orm";
|
|
13
15
|
import { destinationSchema, destinationVisibilitySchema, isSlackDestination, pluginRunContextSchema, replyAttributionSchema, sourceSchema, taskOutcomeSchema } from "@sentry/junior-plugin-api";
|
|
16
|
+
//#region src/api/conversations/aggregate.ts
|
|
17
|
+
function usageTokenValue(source, field) {
|
|
18
|
+
return sql`CASE
|
|
19
|
+
WHEN ${source.usage}->>${field} IS NOT NULL
|
|
20
|
+
THEN (${source.usage}->>${field})::double precision
|
|
21
|
+
ELSE NULL
|
|
22
|
+
END`;
|
|
23
|
+
}
|
|
24
|
+
function tokenValue(source) {
|
|
25
|
+
return sql`
|
|
26
|
+
CASE
|
|
27
|
+
WHEN COALESCE(
|
|
28
|
+
${source.usage}->>'inputTokens',
|
|
29
|
+
${source.usage}->>'outputTokens',
|
|
30
|
+
${source.usage}->>'cachedInputTokens',
|
|
31
|
+
${source.usage}->>'cacheCreationTokens'
|
|
32
|
+
) IS NOT NULL
|
|
33
|
+
THEN COALESCE((${source.usage}->>'inputTokens')::double precision, 0)
|
|
34
|
+
+ COALESCE((${source.usage}->>'outputTokens')::double precision, 0)
|
|
35
|
+
+ COALESCE((${source.usage}->>'cachedInputTokens')::double precision, 0)
|
|
36
|
+
+ COALESCE((${source.usage}->>'cacheCreationTokens')::double precision, 0)
|
|
37
|
+
WHEN ${source.usage}->>'totalTokens' IS NOT NULL
|
|
38
|
+
THEN (${source.usage}->>'totalTokens')::double precision
|
|
39
|
+
ELSE NULL
|
|
40
|
+
END
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
function costValue(source) {
|
|
44
|
+
return conversationUsageCostExpr(source.usage);
|
|
45
|
+
}
|
|
46
|
+
/** Read one conversation row's usage cost as a SQL expression. */
|
|
47
|
+
function conversationUsageCostExpr(usage) {
|
|
48
|
+
return sql`
|
|
49
|
+
CASE
|
|
50
|
+
WHEN ${usage}->'cost'->>'total' IS NOT NULL
|
|
51
|
+
THEN (${usage}->'cost'->>'total')::double precision
|
|
52
|
+
WHEN COALESCE(
|
|
53
|
+
${usage}->'cost'->>'input',
|
|
54
|
+
${usage}->'cost'->>'output',
|
|
55
|
+
${usage}->'cost'->>'cacheRead',
|
|
56
|
+
${usage}->'cost'->>'cacheWrite'
|
|
57
|
+
) IS NOT NULL
|
|
58
|
+
THEN COALESCE((${usage}->'cost'->>'input')::double precision, 0)
|
|
59
|
+
+ COALESCE((${usage}->'cost'->>'output')::double precision, 0)
|
|
60
|
+
+ COALESCE((${usage}->'cost'->>'cacheRead')::double precision, 0)
|
|
61
|
+
+ COALESCE((${usage}->'cost'->>'cacheWrite')::double precision, 0)
|
|
62
|
+
ELSE NULL
|
|
63
|
+
END
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
/** Select complete conversation metrics inside the database instead of materializing source rows. */
|
|
67
|
+
function conversationAggregateColumns(sources) {
|
|
68
|
+
const metrics = sources?.metrics ?? juniorConversations;
|
|
69
|
+
const roots = sources?.roots ?? juniorConversations;
|
|
70
|
+
const conversationCount = sources ? sql`COUNT(DISTINCT ${roots.conversationId})` : sql`COUNT(*)`;
|
|
71
|
+
return {
|
|
72
|
+
active: sql`${conversationCount} FILTER (
|
|
73
|
+
WHERE ${roots.executionStatus} NOT IN ('idle', 'failed')
|
|
74
|
+
)::integer`,
|
|
75
|
+
conversations: sql`${conversationCount}::integer`,
|
|
76
|
+
cachedInputTokens: sql`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
|
|
77
|
+
cacheCreationTokens: sql`SUM(${usageTokenValue(metrics, "cacheCreationTokens")})::double precision`,
|
|
78
|
+
costUsd: sql`SUM(${costValue(metrics)})::double precision`,
|
|
79
|
+
durationMs: sql`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
|
|
80
|
+
failed: sql`${conversationCount} FILTER (
|
|
81
|
+
WHERE ${roots.executionStatus} = 'failed'
|
|
82
|
+
)::integer`,
|
|
83
|
+
inputTokens: sql`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
|
|
84
|
+
tokens: sql`SUM(${tokenValue(metrics)})::double precision`
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Select the complete first/last activity range for a grouped conversation aggregate. */
|
|
88
|
+
function conversationRangeColumns() {
|
|
89
|
+
return {
|
|
90
|
+
firstSeenAt: sql`MIN(${juniorConversations.createdAt})`.mapWith(juniorConversations.createdAt),
|
|
91
|
+
lastSeenAt: sql`MAX(${juniorConversations.lastActivityAt})`.mapWith(juniorConversations.lastActivityAt)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Count distinct UTC activity dates without loading conversation timestamps into the app. */
|
|
95
|
+
function conversationActiveDaysColumn() {
|
|
96
|
+
return sql`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/chat/automations/execution-stats.ts
|
|
100
|
+
const AUTOMATION_EXECUTION_TYPES = ["scheduled", "event"];
|
|
101
|
+
const AUTOMATION_EXECUTION_STATUSES = [
|
|
102
|
+
"blocked",
|
|
103
|
+
"completed",
|
|
104
|
+
"failed"
|
|
105
|
+
];
|
|
106
|
+
function addUsd(current, next) {
|
|
107
|
+
return Math.round((current + next) * 0xe8d4a51000) / 0xe8d4a51000;
|
|
108
|
+
}
|
|
109
|
+
const EMPTY_RUN_WINDOWS = {
|
|
110
|
+
1: 0,
|
|
111
|
+
7: 0,
|
|
112
|
+
30: 0,
|
|
113
|
+
90: 0
|
|
114
|
+
};
|
|
115
|
+
/** Empty run windows for automations with no execution history. */
|
|
116
|
+
function emptyAutomationRunWindows() {
|
|
117
|
+
return { ...EMPTY_RUN_WINDOWS };
|
|
118
|
+
}
|
|
119
|
+
function utcDate(nowMs) {
|
|
120
|
+
return new Date(nowMs).toISOString().slice(0, 10);
|
|
121
|
+
}
|
|
122
|
+
function emptyDay(date) {
|
|
123
|
+
return {
|
|
124
|
+
costUsd: 0,
|
|
125
|
+
date,
|
|
126
|
+
event: 0,
|
|
127
|
+
scheduled: 0
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function emptyStatusDay(date) {
|
|
131
|
+
return {
|
|
132
|
+
blocked: 0,
|
|
133
|
+
completed: 0,
|
|
134
|
+
date,
|
|
135
|
+
failed: 0
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/** Record one terminal automation execution and its durable conversation when present. */
|
|
139
|
+
async function recordAutomationExecution(type, automationId, options) {
|
|
140
|
+
const namespace = options.namespace ?? "junior";
|
|
141
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
142
|
+
const status = options.status ?? "completed";
|
|
143
|
+
try {
|
|
144
|
+
await getDb().insert(juniorAutomationExecutions).values({
|
|
145
|
+
...options.conversationId ? { conversationId: options.conversationId } : void 0,
|
|
146
|
+
executedAtMs: nowMs,
|
|
147
|
+
executionId: options.executionId,
|
|
148
|
+
kind: type,
|
|
149
|
+
namespace,
|
|
150
|
+
status,
|
|
151
|
+
automationId
|
|
152
|
+
}).onConflictDoNothing({ target: [
|
|
153
|
+
juniorAutomationExecutions.kind,
|
|
154
|
+
juniorAutomationExecutions.namespace,
|
|
155
|
+
juniorAutomationExecutions.executionId
|
|
156
|
+
] });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
logWarn("task.execution.stat_failed", {
|
|
159
|
+
error,
|
|
160
|
+
"app.task.execution.id": options.executionId,
|
|
161
|
+
"app.task.execution.namespace": namespace,
|
|
162
|
+
"app.task.execution.status": status,
|
|
163
|
+
"app.task.execution.task_id": automationId,
|
|
164
|
+
"app.task.execution.type": type
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** Load usage analytics for all automations of one type and namespace. */
|
|
169
|
+
async function readAutomationExecutionSummaries(type, namespace, options = {}) {
|
|
170
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
171
|
+
const oneDayAgoMs = nowMs - 864e5;
|
|
172
|
+
const sevenDaysAgoMs = nowMs - 6048e5;
|
|
173
|
+
const thirtyDaysAgoMs = nowMs - 2592e6;
|
|
174
|
+
const ninetyDaysAgoMs = nowMs - 7776e6;
|
|
175
|
+
const db = getDb();
|
|
176
|
+
const latest = db.selectDistinctOn([juniorAutomationExecutions.automationId], {
|
|
177
|
+
conversationId: juniorAutomationExecutions.conversationId,
|
|
178
|
+
automationId: juniorAutomationExecutions.automationId
|
|
179
|
+
}).from(juniorAutomationExecutions).where(and(eq(juniorAutomationExecutions.kind, type), eq(juniorAutomationExecutions.namespace, namespace))).orderBy(juniorAutomationExecutions.automationId, desc(juniorAutomationExecutions.executedAtMs), desc(juniorAutomationExecutions.executionId)).as("latest_task_execution");
|
|
180
|
+
const rows = await db.select({
|
|
181
|
+
lastConversationId: latest.conversationId,
|
|
182
|
+
lastExecutedAtMs: max(juniorAutomationExecutions.executedAtMs),
|
|
183
|
+
runsLast1Day: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${oneDayAgoMs})::int`,
|
|
184
|
+
runsLast7Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${sevenDaysAgoMs})::int`,
|
|
185
|
+
runsLast30Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${thirtyDaysAgoMs})::int`,
|
|
186
|
+
runsLast90Days: sql`count(*) filter (where ${juniorAutomationExecutions.executedAtMs} >= ${ninetyDaysAgoMs})::int`,
|
|
187
|
+
automationId: juniorAutomationExecutions.automationId,
|
|
188
|
+
totalRuns: sql`count(*)::int`
|
|
189
|
+
}).from(juniorAutomationExecutions).innerJoin(latest, eq(latest.automationId, juniorAutomationExecutions.automationId)).where(and(eq(juniorAutomationExecutions.kind, type), eq(juniorAutomationExecutions.namespace, namespace))).groupBy(juniorAutomationExecutions.automationId, latest.conversationId);
|
|
190
|
+
return new Map(rows.map((row) => [row.automationId, {
|
|
191
|
+
...row.lastConversationId ? { lastConversationId: row.lastConversationId } : void 0,
|
|
192
|
+
...row.lastExecutedAtMs !== null ? { lastExecutedAtMs: row.lastExecutedAtMs } : void 0,
|
|
193
|
+
runs: {
|
|
194
|
+
1: row.runsLast1Day,
|
|
195
|
+
7: row.runsLast7Days,
|
|
196
|
+
30: row.runsLast30Days,
|
|
197
|
+
90: row.runsLast90Days
|
|
198
|
+
},
|
|
199
|
+
totalRuns: row.totalRuns
|
|
200
|
+
}]));
|
|
201
|
+
}
|
|
202
|
+
/** Load a fixed trailing window of completed executions stacked by task type. */
|
|
203
|
+
async function readAutomationExecutionDays(dayCount = 90, options = {}) {
|
|
204
|
+
const end = utcDate(options.nowMs ?? Date.now());
|
|
205
|
+
const endMs = Date.parse(`${end}T23:59:59.999Z`);
|
|
206
|
+
const startMs = Date.parse(`${end}T00:00:00.000Z`) - (dayCount - 1) * 864e5;
|
|
207
|
+
const executionDate = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
|
|
208
|
+
const conversationCost = conversationUsageCostExpr(juniorConversations.usage);
|
|
209
|
+
const rows = await getDb().select({
|
|
210
|
+
count: count(),
|
|
211
|
+
costUsd: sql`SUM(${conversationCost})::double precision`,
|
|
212
|
+
date: executionDate,
|
|
213
|
+
kind: juniorAutomationExecutions.kind
|
|
214
|
+
}).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), eq(juniorAutomationExecutions.status, "completed"))).groupBy(executionDate, juniorAutomationExecutions.kind).orderBy(asc(executionDate), asc(juniorAutomationExecutions.kind));
|
|
215
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
216
|
+
for (let offset = 0; offset < dayCount; offset += 1) {
|
|
217
|
+
const date = utcDate(startMs + offset * 864e5);
|
|
218
|
+
byDate.set(date, emptyDay(date));
|
|
219
|
+
}
|
|
220
|
+
for (const row of rows) {
|
|
221
|
+
const day = byDate.get(row.date);
|
|
222
|
+
if (!day) continue;
|
|
223
|
+
if (row.kind === "scheduled") day.scheduled = row.count;
|
|
224
|
+
else if (row.kind === "event") day.event = row.count;
|
|
225
|
+
if (row.costUsd !== null && row.costUsd > 0) day.costUsd = addUsd(day.costUsd, row.costUsd);
|
|
226
|
+
}
|
|
227
|
+
return [...byDate.values()];
|
|
228
|
+
}
|
|
229
|
+
/** Load a fixed trailing window of completed executions stacked by hour. */
|
|
230
|
+
async function readAutomationExecutionHours(hourCount = 168, options = {}) {
|
|
231
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
232
|
+
const end = new Date(nowMs);
|
|
233
|
+
end.setUTCMinutes(0, 0, 0);
|
|
234
|
+
const endMs = end.getTime() + 3599999;
|
|
235
|
+
const startMs = end.getTime() - (hourCount - 1) * 36e5;
|
|
236
|
+
const executionHour = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
|
|
237
|
+
const conversationCost = conversationUsageCostExpr(juniorConversations.usage);
|
|
238
|
+
const rows = await getDb().select({
|
|
239
|
+
count: count(),
|
|
240
|
+
costUsd: sql`SUM(${conversationCost})::double precision`,
|
|
241
|
+
date: executionHour,
|
|
242
|
+
kind: juniorAutomationExecutions.kind
|
|
243
|
+
}).from(juniorAutomationExecutions).leftJoin(juniorConversations, eq(juniorConversations.conversationId, juniorAutomationExecutions.conversationId)).where(and(gte(juniorAutomationExecutions.executedAtMs, startMs), lte(juniorAutomationExecutions.executedAtMs, endMs), eq(juniorAutomationExecutions.status, "completed"))).groupBy(executionHour, juniorAutomationExecutions.kind).orderBy(asc(executionHour), asc(juniorAutomationExecutions.kind));
|
|
244
|
+
const byHour = /* @__PURE__ */ new Map();
|
|
245
|
+
for (let offset = 0; offset < hourCount; offset += 1) {
|
|
246
|
+
const date = new Date(startMs + offset * 36e5).toISOString().slice(0, 13);
|
|
247
|
+
byHour.set(date, emptyDay(date));
|
|
248
|
+
}
|
|
249
|
+
for (const row of rows) {
|
|
250
|
+
const hour = byHour.get(row.date);
|
|
251
|
+
if (!hour) continue;
|
|
252
|
+
if (row.kind === "scheduled") hour.scheduled = row.count;
|
|
253
|
+
else if (row.kind === "event") hour.event = row.count;
|
|
254
|
+
if (row.costUsd !== null && row.costUsd > 0) hour.costUsd = addUsd(hour.costUsd, row.costUsd);
|
|
255
|
+
}
|
|
256
|
+
return [...byHour.values()];
|
|
257
|
+
}
|
|
258
|
+
/** Load the newest terminal automation execution linked to one conversation, if any. */
|
|
259
|
+
async function readAutomationExecutionByConversationId(args) {
|
|
260
|
+
const namespace = args.namespace ?? "junior";
|
|
261
|
+
const row = (await getDb().select({
|
|
262
|
+
kind: juniorAutomationExecutions.kind,
|
|
263
|
+
automationId: juniorAutomationExecutions.automationId
|
|
264
|
+
}).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];
|
|
265
|
+
if (!row) return void 0;
|
|
266
|
+
if (row.kind !== "scheduled" && row.kind !== "event") return void 0;
|
|
267
|
+
return {
|
|
268
|
+
kind: row.kind,
|
|
269
|
+
automationId: row.automationId
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/** Load newest-first executions for one automation, with conversation titles when present. */
|
|
273
|
+
async function readAutomationExecutions(args) {
|
|
274
|
+
const namespace = args.namespace ?? "junior";
|
|
275
|
+
return (await getDb().select({
|
|
276
|
+
conversationId: juniorAutomationExecutions.conversationId,
|
|
277
|
+
durationMs: juniorConversations.durationMs,
|
|
278
|
+
executedAtMs: juniorAutomationExecutions.executedAtMs,
|
|
279
|
+
executionId: juniorAutomationExecutions.executionId,
|
|
280
|
+
status: juniorAutomationExecutions.status,
|
|
281
|
+
title: juniorConversations.title,
|
|
282
|
+
usage: juniorConversations.usage
|
|
283
|
+
}).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));
|
|
284
|
+
}
|
|
285
|
+
/** Load newest-first executions for the supplied viewer-visible tasks. */
|
|
286
|
+
async function readAutomationRuns(args) {
|
|
287
|
+
const automations = args.automations ?? [];
|
|
288
|
+
const conversationIds = [...new Set((args.conversationIds ?? []).filter((conversationId) => conversationId.trim().length > 0))];
|
|
289
|
+
if (automations.length === 0 && conversationIds.length === 0) return [];
|
|
290
|
+
const namespace = args.namespace ?? "junior";
|
|
291
|
+
const selectors = [...automations.map((task) => and(eq(juniorAutomationExecutions.kind, task.kind), eq(juniorAutomationExecutions.automationId, task.automationId))), ...conversationIds.length > 0 ? [inArray(juniorAutomationExecutions.conversationId, conversationIds)] : []];
|
|
292
|
+
return (await getDb().select({
|
|
293
|
+
conversationId: juniorAutomationExecutions.conversationId,
|
|
294
|
+
durationMs: juniorConversations.durationMs,
|
|
295
|
+
executedAtMs: juniorAutomationExecutions.executedAtMs,
|
|
296
|
+
executionId: juniorAutomationExecutions.executionId,
|
|
297
|
+
kind: juniorAutomationExecutions.kind,
|
|
298
|
+
status: juniorAutomationExecutions.status,
|
|
299
|
+
automationId: juniorAutomationExecutions.automationId,
|
|
300
|
+
title: juniorConversations.title,
|
|
301
|
+
usage: juniorConversations.usage
|
|
302
|
+
}).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) => {
|
|
303
|
+
if (row.kind !== "scheduled" && row.kind !== "event") return [];
|
|
304
|
+
return [{
|
|
305
|
+
...mapAutomationExecutionRecord(row),
|
|
306
|
+
kind: row.kind,
|
|
307
|
+
automationId: row.automationId
|
|
308
|
+
}];
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
function mapAutomationExecutionRecord(row) {
|
|
312
|
+
const title = row.title?.trim();
|
|
313
|
+
const costUsd = agentTurnCostUsd(row.usage ?? void 0);
|
|
314
|
+
const totalTokens = agentTurnTotalTokens(row.usage ?? void 0);
|
|
315
|
+
const durationMs = typeof row.durationMs === "number" && Number.isFinite(row.durationMs) && row.durationMs > 0 ? row.durationMs : void 0;
|
|
316
|
+
return {
|
|
317
|
+
...row.conversationId ? { conversationId: row.conversationId } : void 0,
|
|
318
|
+
...costUsd !== void 0 ? { costUsd } : void 0,
|
|
319
|
+
...durationMs !== void 0 ? { durationMs } : void 0,
|
|
320
|
+
executedAt: new Date(row.executedAtMs).toISOString(),
|
|
321
|
+
executionId: row.executionId,
|
|
322
|
+
status: row.status,
|
|
323
|
+
...title ? { title } : void 0,
|
|
324
|
+
...totalTokens !== void 0 ? { totalTokens } : void 0
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/** Load a fixed trailing window of terminal executions for one automation by status. */
|
|
328
|
+
async function readAutomationExecutionStatusDays(args) {
|
|
329
|
+
const dayCount = args.dayCount ?? 90;
|
|
330
|
+
const namespace = args.namespace ?? "junior";
|
|
331
|
+
const end = utcDate(args.nowMs ?? Date.now());
|
|
332
|
+
const endMs = Date.parse(`${end}T23:59:59.999Z`);
|
|
333
|
+
const startMs = Date.parse(`${end}T00:00:00.000Z`) - (dayCount - 1) * 864e5;
|
|
334
|
+
const executionDate = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD')`;
|
|
335
|
+
const rows = await getDb().select({
|
|
336
|
+
count: count(),
|
|
337
|
+
date: executionDate,
|
|
338
|
+
status: juniorAutomationExecutions.status
|
|
339
|
+
}).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));
|
|
340
|
+
const byDate = /* @__PURE__ */ new Map();
|
|
341
|
+
for (let offset = 0; offset < dayCount; offset += 1) {
|
|
342
|
+
const date = utcDate(startMs + offset * 864e5);
|
|
343
|
+
byDate.set(date, emptyStatusDay(date));
|
|
344
|
+
}
|
|
345
|
+
for (const row of rows) {
|
|
346
|
+
const day = byDate.get(row.date);
|
|
347
|
+
if (!day) continue;
|
|
348
|
+
if (row.status === "completed") day.completed = row.count;
|
|
349
|
+
else if (row.status === "failed") day.failed = row.count;
|
|
350
|
+
else if (row.status === "blocked") day.blocked = row.count;
|
|
351
|
+
}
|
|
352
|
+
return [...byDate.values()];
|
|
353
|
+
}
|
|
354
|
+
/** Load a fixed trailing window of terminal executions for one automation by hour. */
|
|
355
|
+
async function readAutomationExecutionStatusHours(args) {
|
|
356
|
+
const hourCount = args.hourCount ?? 168;
|
|
357
|
+
const namespace = args.namespace ?? "junior";
|
|
358
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
359
|
+
const end = new Date(nowMs);
|
|
360
|
+
end.setUTCMinutes(0, 0, 0);
|
|
361
|
+
const endMs = end.getTime() + 3599999;
|
|
362
|
+
const startMs = end.getTime() - (hourCount - 1) * 36e5;
|
|
363
|
+
const executionHour = sql`to_char(to_timestamp(${juniorAutomationExecutions.executedAtMs} / 1000.0) at time zone 'UTC', 'YYYY-MM-DD"T"HH24')`;
|
|
364
|
+
const rows = await getDb().select({
|
|
365
|
+
count: count(),
|
|
366
|
+
date: executionHour,
|
|
367
|
+
status: juniorAutomationExecutions.status
|
|
368
|
+
}).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));
|
|
369
|
+
const byHour = /* @__PURE__ */ new Map();
|
|
370
|
+
for (let offset = 0; offset < hourCount; offset += 1) {
|
|
371
|
+
const date = new Date(startMs + offset * 36e5).toISOString().slice(0, 13);
|
|
372
|
+
byHour.set(date, emptyStatusDay(date));
|
|
373
|
+
}
|
|
374
|
+
for (const row of rows) {
|
|
375
|
+
const hour = byHour.get(row.date);
|
|
376
|
+
if (!hour) continue;
|
|
377
|
+
if (row.status === "completed") hour.completed = row.count;
|
|
378
|
+
else if (row.status === "failed") hour.failed = row.count;
|
|
379
|
+
else if (row.status === "blocked") hour.blocked = row.count;
|
|
380
|
+
}
|
|
381
|
+
return [...byHour.values()];
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
14
384
|
//#region src/chat/agent-dispatch/store.ts
|
|
15
385
|
const DISPATCH_PREFIX = "junior:agent_dispatch";
|
|
16
386
|
const DISPATCH_LOCK_TTL_MS = 6e5;
|
|
@@ -240,9 +610,8 @@ async function recordEventAutomationExecution(previous, next, status) {
|
|
|
240
610
|
const eventAutomationId = next.metadata?.eventAutomationId;
|
|
241
611
|
if (!eventAutomationId) return;
|
|
242
612
|
const conversationId = getDispatchConversationId(next);
|
|
243
|
-
const conversation = await getConversationStore().get({ conversationId });
|
|
244
613
|
await recordAutomationExecution("event", eventAutomationId, {
|
|
245
|
-
...
|
|
614
|
+
...await getConversationStore().get({ conversationId }) ? { conversationId } : void 0,
|
|
246
615
|
executionId: next.id,
|
|
247
616
|
nowMs: next.updatedAtMs,
|
|
248
617
|
status
|
|
@@ -561,4 +930,4 @@ async function processPluginTask(message, options = {}) {
|
|
|
561
930
|
});
|
|
562
931
|
}
|
|
563
932
|
//#endregion
|
|
564
|
-
export { markDispatchFailed as _, confirmDispatchMailboxAppend as a, getDispatchInputMessageId as c, getPluginDispatchProjection as d, isTerminalDispatchStatus as f, markDispatchCompleted as g, markDispatchBlocked as h, claimDispatchMailboxAppend as i, getDispatchRecord as l, markDispatchAwaitingResume as m, scheduleSessionCompletedPluginTasks as n, createOrGetDispatch as o, listPendingDispatchMailboxAppends as p, task_runner_exports as r, getDispatchConversationId as s, processPluginTask as t, getDispatchTurnId as u, markDispatchRunning as v };
|
|
933
|
+
export { conversationAggregateColumns as A, readAutomationExecutionStatusDays as C, readAutomationRuns as D, readAutomationExecutions as E, recordAutomationExecution as O, readAutomationExecutionHours as S, readAutomationExecutionSummaries as T, markDispatchFailed as _, confirmDispatchMailboxAppend as a, readAutomationExecutionByConversationId as b, getDispatchInputMessageId as c, getPluginDispatchProjection as d, isTerminalDispatchStatus as f, markDispatchCompleted as g, markDispatchBlocked as h, claimDispatchMailboxAppend as i, conversationRangeColumns as j, conversationActiveDaysColumn as k, getDispatchRecord as l, markDispatchAwaitingResume as m, scheduleSessionCompletedPluginTasks as n, createOrGetDispatch as o, listPendingDispatchMailboxAppends as p, task_runner_exports as r, getDispatchConversationId as s, processPluginTask as t, getDispatchTurnId as u, markDispatchRunning as v, readAutomationExecutionStatusHours as w, readAutomationExecutionDays as x, emptyAutomationRunWindows as y };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb } from "./agent-hooks-
|
|
1
|
+
import { I as isRecord, L as toOptionalNumber, R as toOptionalString } from "./logging-CvjMHRvg.js";
|
|
2
|
+
import { pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb } from "./agent-hooks-FUetyyLe.js";
|
|
3
3
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
4
|
-
import { u as juniorLocationConfigurations } from "./executor-
|
|
5
|
-
import { Y as persistConversationMessages } from "./checkpoint-
|
|
6
|
-
import { w as getStateAdapter } from "./profile-
|
|
4
|
+
import { u as juniorLocationConfigurations } from "./executor-D36FWjcf.js";
|
|
5
|
+
import { Y as persistConversationMessages } from "./checkpoint-CJe-dZFF.js";
|
|
6
|
+
import { w as getStateAdapter } from "./profile-dvV7PB7u.js";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
import { and, eq, sql } from "drizzle-orm";
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { y as
|
|
2
|
-
import {
|
|
3
|
-
import { F as isRecord, t as buildTurnFailureResponse } from "./logging-iY0ssa2q.js";
|
|
1
|
+
import { _ as findProviderError, h as ProviderError, v as getProviderErrorAttributes, y as getProviderErrorUserMessage } from "./client-C2WBBx7m.js";
|
|
2
|
+
import { I as isRecord, t as buildTurnFailureResponse } from "./logging-CvjMHRvg.js";
|
|
4
3
|
import { _ as parseSlackMessageTs } from "./context-Bplcgypl.js";
|
|
5
|
-
import { Wt as getConversationEventStore } from "./agent-hooks-
|
|
6
|
-
import { A as messageCardKey, N as readMessageCards, j as messageCardRefSchema } from "./conversation-
|
|
7
|
-
import { J as hydrateConversationMessages,
|
|
8
|
-
import {
|
|
9
|
-
import { g as markTurnCompleted, h as markTurnClosed } from "./oauth-authorization-
|
|
10
|
-
import { o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-
|
|
4
|
+
import { Wt as getConversationEventStore } from "./agent-hooks-FUetyyLe.js";
|
|
5
|
+
import { A as messageCardKey, N as readMessageCards, j as messageCardRefSchema } from "./conversation-DPbmIwjy.js";
|
|
6
|
+
import { J as hydrateConversationMessages, ct as markConversationMessage } from "./checkpoint-CJe-dZFF.js";
|
|
7
|
+
import { r as clearPendingAuth, s as buildDeterministicTurnId } from "./pending-auth-DLNJbD8H.js";
|
|
8
|
+
import { g as markTurnCompleted, h as markTurnClosed } from "./oauth-authorization-l4O1rXsL.js";
|
|
9
|
+
import { o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-CTuWhjru.js";
|
|
11
10
|
//#region src/chat/conversations/pending-cards.ts
|
|
12
11
|
/** Read undelivered card snapshots across resume and history replacement. */
|
|
13
12
|
async function loadPendingMessageCards(conversationId) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Wt as getConversationEventStore } from "./agent-hooks-
|
|
1
|
+
import { Wt as getConversationEventStore } from "./agent-hooks-FUetyyLe.js";
|
|
2
2
|
//#region src/chat/conversations/turn-lifecycle.ts
|
|
3
3
|
/** Persist correlated turn lifecycle facts with retry-safe terminal exclusion. */
|
|
4
4
|
var ConversationTurnLifecycleService = class {
|
|
@@ -34,7 +34,7 @@ function slackSourceForDestination(args) {
|
|
|
34
34
|
* Reads parent Conversations when this Conversation has no Destination.
|
|
35
35
|
*/
|
|
36
36
|
async function resolveConversationRouting(args) {
|
|
37
|
-
const conversationStore = args.conversationStore ?? (await import("./agent-hooks-
|
|
37
|
+
const conversationStore = args.conversationStore ?? (await import("./agent-hooks-FUetyyLe.js").then((n) => n.Ut)).getConversationStore();
|
|
38
38
|
const chain = await loadConversationChain(args.conversationId, conversationStore);
|
|
39
39
|
let destination;
|
|
40
40
|
let source;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
-
import { n as pluginCatalogRuntime } from "./catalog-runtime-
|
|
2
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-BYGR3Dr4.js";
|
|
3
3
|
import { isDeepStrictEqual } from "node:util";
|
|
4
4
|
//#region src/chat/plugins/validation.ts
|
|
5
5
|
var validation_exports = /* @__PURE__ */ __exportAll({
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as JUNIOR_VERSION } from "./version-
|
|
1
|
+
import { t as JUNIOR_VERSION } from "./version-ByLtIYyy.js";
|
|
2
2
|
export { JUNIOR_VERSION };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Pt as ensureConversationWake, Wt as requestConversationWork } from "./agent-runner-DEF7auSj.js";
|
|
2
|
+
import { Wt as getConversationEventStore } from "./agent-hooks-FUetyyLe.js";
|
|
3
|
+
import { n as loadTurnCheckpoint, o as failTurnRecord, s as getTurnRecord } from "./checkpoint-CJe-dZFF.js";
|
|
4
|
+
import { w as getStateAdapter } from "./profile-dvV7PB7u.js";
|
|
5
|
+
import { a as getVercelConversationWorkQueue } from "./job-queue-2mFFDMHB.js";
|
|
6
|
+
import { n as resolveTurnSessionRouting } from "./turn-session-routing-5RejBvit.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
//#region src/chat/task-execution/authorized-turn.ts
|
|
10
|
+
/**
|
|
11
|
+
* OAuth callbacks save readiness, not agent history. The conversation worker
|
|
12
|
+
* consumes this event under its lease before it resumes the paused Turn.
|
|
13
|
+
*/
|
|
14
|
+
const authorizationSchema = z.object({
|
|
15
|
+
kind: z.enum(["mcp", "plugin"]),
|
|
16
|
+
provider: z.string().min(1),
|
|
17
|
+
actorId: z.string().min(1),
|
|
18
|
+
authorizationId: z.string().min(1),
|
|
19
|
+
authSessionId: z.string().optional(),
|
|
20
|
+
scope: z.string().optional(),
|
|
21
|
+
configuration: z.record(z.string(), z.unknown()).optional(),
|
|
22
|
+
toolChannelId: z.string().optional()
|
|
23
|
+
});
|
|
24
|
+
function authorizationKey$1(turn) {
|
|
25
|
+
return `turn:${turn.turnId}:authorization:${turn.version}`;
|
|
26
|
+
}
|
|
27
|
+
/** Save authorization for this parked boundary without changing model history. */
|
|
28
|
+
async function recordTurnAuthorization(args) {
|
|
29
|
+
const turn = await getTurnRecord(args.conversationId, args.turnId);
|
|
30
|
+
if (!turn || turn.state !== "paused" || turn.resumeReason !== "auth") return;
|
|
31
|
+
await getConversationEventStore().append(args.conversationId, [{
|
|
32
|
+
idempotencyKey: authorizationKey$1(turn),
|
|
33
|
+
createdAtMs: Date.now(),
|
|
34
|
+
data: {
|
|
35
|
+
type: "structured_event",
|
|
36
|
+
namespace: "junior",
|
|
37
|
+
name: "turn_authorized",
|
|
38
|
+
version: 1,
|
|
39
|
+
turnId: turn.turnId,
|
|
40
|
+
content: authorizationSchema.parse(args)
|
|
41
|
+
}
|
|
42
|
+
}], { activity: "preserve" });
|
|
43
|
+
return {
|
|
44
|
+
conversationId: args.conversationId,
|
|
45
|
+
destination: args.destination,
|
|
46
|
+
turnId: turn.turnId,
|
|
47
|
+
expectedVersion: turn.version
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Read authorization for the exact pause; a later auth pause needs its own callback. */
|
|
51
|
+
async function getTurnAuthorization(turn) {
|
|
52
|
+
const event = await getConversationEventStore().loadByIdempotencyKey(turn.conversationId, authorizationKey$1(turn));
|
|
53
|
+
if (event?.data.type !== "structured_event") return void 0;
|
|
54
|
+
return authorizationSchema.parse(event.data.content);
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/chat/task-execution/turn-wake.ts
|
|
58
|
+
/** Build the worker input for a paused turn. */
|
|
59
|
+
async function getPausedTurnRequest(args) {
|
|
60
|
+
const turn = (await loadTurnCheckpoint({
|
|
61
|
+
conversationId: args.conversationId,
|
|
62
|
+
turnId: args.turnId
|
|
63
|
+
})).record;
|
|
64
|
+
if (!turn || turn.state !== "paused" || turn.resumeReason !== "timeout" && turn.resumeReason !== "yield" && turn.resumeReason !== "retry" && !(turn.resumeReason === "auth" && await getTurnAuthorization(turn)) || turn.resumeReason === "timeout" && turn.sliceId < 2) return;
|
|
65
|
+
let routing;
|
|
66
|
+
try {
|
|
67
|
+
routing = await resolveTurnSessionRouting({
|
|
68
|
+
conversationId: args.conversationId,
|
|
69
|
+
conversationStore: args.conversationStore
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
await failTurnRecord({
|
|
73
|
+
conversationId: args.conversationId,
|
|
74
|
+
expectedVersion: turn.version,
|
|
75
|
+
turnId: args.turnId,
|
|
76
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
conversationId: args.conversationId,
|
|
82
|
+
destination: routing.destination,
|
|
83
|
+
turnId: args.turnId,
|
|
84
|
+
expectedVersion: turn.version
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Wake the conversation worker for a paused turn. */
|
|
88
|
+
async function wakePausedTurn(request, options = {}) {
|
|
89
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
90
|
+
await requestConversationWork({
|
|
91
|
+
conversationId: request.conversationId,
|
|
92
|
+
destination: request.destination,
|
|
93
|
+
nowMs,
|
|
94
|
+
state: options.state
|
|
95
|
+
});
|
|
96
|
+
const queue = options.queue ?? getVercelConversationWorkQueue();
|
|
97
|
+
await ensureConversationWake({
|
|
98
|
+
conversationId: request.conversationId,
|
|
99
|
+
idempotencyKey: [
|
|
100
|
+
"agent-continue",
|
|
101
|
+
request.conversationId,
|
|
102
|
+
request.turnId,
|
|
103
|
+
request.expectedVersion,
|
|
104
|
+
nowMs
|
|
105
|
+
].join(":"),
|
|
106
|
+
nowMs,
|
|
107
|
+
queue,
|
|
108
|
+
state: options.state
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/** Create paused Turn operations for one app. */
|
|
112
|
+
function createPausedTurns(options = {}) {
|
|
113
|
+
return {
|
|
114
|
+
get: (args) => getPausedTurnRequest({
|
|
115
|
+
...args,
|
|
116
|
+
conversationStore: options.conversationStore
|
|
117
|
+
}),
|
|
118
|
+
wake: (request) => wakePausedTurn(request, options)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Record completed authorization and wake the sole execution owner. */
|
|
122
|
+
async function wakeAuthorizedTurn(args, queue) {
|
|
123
|
+
const request = await recordTurnAuthorization(args);
|
|
124
|
+
if (request) await wakePausedTurn(request, { queue });
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/chat/conversations/web-authorization.ts
|
|
128
|
+
const WEB_AUTHORIZATION_PREFIX = "junior:web_authorization:v1";
|
|
129
|
+
const WEB_AUTHORIZATION_TTL_MS = 864e5;
|
|
130
|
+
function authorizationKey(conversationId, actorId) {
|
|
131
|
+
return `${WEB_AUTHORIZATION_PREFIX}:${conversationId}:${actorId}`;
|
|
132
|
+
}
|
|
133
|
+
/** Build authorization delivery for one web turn. */
|
|
134
|
+
function createWebAuthorization(args) {
|
|
135
|
+
return {
|
|
136
|
+
createState: async () => randomUUID(),
|
|
137
|
+
deliver: async (request) => {
|
|
138
|
+
await getStateAdapter().set(authorizationKey(args.conversationId, args.actorId), JSON.stringify({
|
|
139
|
+
...request,
|
|
140
|
+
...args
|
|
141
|
+
}), WEB_AUTHORIZATION_TTL_MS);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/** Read the pending authorization request for one web actor. */
|
|
146
|
+
async function getWebAuthorization(args) {
|
|
147
|
+
const value = await getStateAdapter().get(authorizationKey(args.conversationId, args.actorId));
|
|
148
|
+
if (typeof value !== "string") return void 0;
|
|
149
|
+
try {
|
|
150
|
+
const parsed = JSON.parse(value);
|
|
151
|
+
if (parsed.actorId !== args.actorId || parsed.conversationId !== args.conversationId || typeof parsed.authorizationUrl !== "string" || typeof parsed.label !== "string" || typeof parsed.completionText !== "string") return;
|
|
152
|
+
return parsed;
|
|
153
|
+
} catch {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Remove a web authorization request after the parked turn continues. */
|
|
158
|
+
async function deleteWebAuthorization(args) {
|
|
159
|
+
await getStateAdapter().delete(authorizationKey(args.conversationId, args.actorId));
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
export { getPausedTurnRequest as a, getTurnAuthorization as c, createPausedTurns as i, deleteWebAuthorization as n, wakeAuthorizedTurn as o, getWebAuthorization as r, wakePausedTurn as s, createWebAuthorization as t };
|