@sentry/junior 0.203.0 → 0.205.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-D4nu8MQV.js +17521 -0
- package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
- package/dist/agent-runner-C04colea.js +678 -0
- package/dist/api/schema.d.ts +2736 -2639
- package/dist/api/schema.js +4 -169
- package/dist/api.d.ts +23 -17
- package/dist/api.js +3881 -5326
- package/dist/app-Cb776lL8.d.ts +247 -0
- package/dist/app.d.ts +4 -520
- package/dist/app.js +9524 -11875
- package/dist/auth-pause-state-C2P3guS1.js +168 -0
- package/dist/catalog-runtime-CXw3asJD.js +7 -0
- package/dist/checkpoint-CrViJ8Ps.js +2450 -0
- package/dist/cli/chat.d.ts +20 -0
- package/dist/cli/chat.js +229 -336
- package/dist/cli/check.d.ts +8 -0
- package/dist/cli/check.js +513 -806
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +63 -12
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +90 -159
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +43 -64
- package/dist/cli/plugins.d.ts +13 -0
- package/dist/cli/plugins.js +148 -227
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.js +52 -8
- package/dist/cli/snapshot-create.d.ts +4 -0
- package/dist/cli/snapshot-create.js +71 -125
- package/dist/cli/upgrade.d.ts +15 -0
- package/dist/cli/upgrade.js +232 -335
- package/dist/config-BOtJQ7Pc.js +941 -0
- package/dist/context-B45_WqVX.js +298 -0
- package/dist/conversation-privacy-BZRA6Pes.js +182 -0
- package/dist/conversation-work-C6y3T5Hx.js +51 -0
- package/dist/credential-sync-Bep34IB_.js +2661 -0
- package/dist/db-BeyXKJOI.d.ts +5 -0
- package/dist/destinations-BeBiJeEM.js +29 -0
- package/dist/execution-stats-B6lgWxCK.js +373 -0
- package/dist/executor-BlUhFL7_.js +721 -0
- package/dist/experimental-CIXf7Uye.js +46 -0
- package/dist/history-ClE4toxe.d.ts +32 -0
- package/dist/history-CqTuSAdI.js +441 -0
- package/dist/instrumentation.d.ts +3 -3
- package/dist/instrumentation.js +130 -176
- package/dist/job-queue-DLMqIK74.js +63 -0
- package/dist/logging-BMJzdDlP.js +1210 -0
- package/dist/model-profile-CZmVoGdX.js +49 -0
- package/dist/nitro.d.ts +31 -36
- package/dist/nitro.js +275 -456
- package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
- package/dist/oauth-relay-CdCnu80W.js +62 -0
- package/dist/package-discovery-7a9YqLhE.js +455 -0
- package/dist/pending-auth-CUbXq06G.js +185 -0
- package/dist/plugin-module-BKGOXca2.js +81 -0
- package/dist/plugins-BI9A1vKA.d.ts +95 -0
- package/dist/plugins-egKWjgue.js +99 -0
- package/dist/private-trace-result-BLvBjy_e.js +16 -0
- package/dist/profile-T0tXE4zu.js +861 -0
- package/dist/registry-BA0__8ai.js +1419 -0
- package/dist/reporting-schema-DosTN__Q.js +105 -0
- package/dist/resolve-bvD1ORdv.js +546 -0
- package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
- package/dist/runner-BXYxfIUL.js +392 -0
- package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
- package/dist/schema-Rn8-XEaN.js +1140 -0
- package/dist/sentry-CD3SdJl-.js +26 -0
- package/dist/skills-DJOlYdBs.js +316 -0
- package/dist/spawn-h0R1bTEo.js +29 -0
- package/dist/stats-3YZn-Swi.js +2648 -0
- package/dist/store-C5qxF2ac.d.ts +208 -0
- package/dist/task-queue-CoXwd2eq.js +136 -0
- package/dist/task-runner-B2_mJZj0.js +639 -0
- package/dist/thread-state-CqZ1m-es.js +431 -0
- package/dist/turn-execution-fI6PZ0iI.js +47 -0
- package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
- package/dist/usage-B5SfMb3H.js +296 -0
- package/dist/usage-schema-CKaDkH_j.js +20 -0
- package/dist/validation-DfWcvhuS.js +40 -0
- package/dist/vercel-queue-DIkVcfK4.js +167 -0
- package/dist/vercel.d.ts +6 -6
- package/dist/vercel.js +14 -6
- package/dist/version-CnXZ614E.d.ts +5 -0
- package/dist/version-oD-oPrbO.js +8 -0
- package/dist/version.d.ts +2 -4
- package/dist/version.js +2 -6
- package/dist/web-authorization-Crbzlmzo.js +76 -0
- package/dist/web-input-BOuq3oKK.js +494 -0
- package/dist/work-B91LQKHr.js +509 -0
- package/dist/worker-BcRRTe6k.js +625 -0
- package/package.json +9 -9
- package/dist/agent-hooks-GB2VPTNA.js +0 -51
- package/dist/catalog-runtime-UBLIWD3O.js +0 -14
- package/dist/chunk-2LYHWMR4.js +0 -46
- package/dist/chunk-352OWAZH.js +0 -320
- package/dist/chunk-3Y47VMHV.js +0 -6
- package/dist/chunk-3YJSKOWF.js +0 -164
- package/dist/chunk-4KSSKTQ5.js +0 -229
- package/dist/chunk-4UO6FK4G.js +0 -64
- package/dist/chunk-5AHX53NQ.js +0 -717
- package/dist/chunk-5J3E5FWA.js +0 -700
- package/dist/chunk-5PDAG6O3.js +0 -206
- package/dist/chunk-6Y5B5PEX.js +0 -80
- package/dist/chunk-7GWQ5S5R.js +0 -2086
- package/dist/chunk-7H2VZTWN.js +0 -103
- package/dist/chunk-7NL7T6VU.js +0 -406
- package/dist/chunk-A5L27LE4.js +0 -387
- package/dist/chunk-ADIAWL34.js +0 -81
- package/dist/chunk-ASTEMBKJ.js +0 -519
- package/dist/chunk-CEMMLV5L.js +0 -98
- package/dist/chunk-CM5HNV2W.js +0 -131
- package/dist/chunk-CRTRBBRI.js +0 -105
- package/dist/chunk-DA2KGZZI.js +0 -13
- package/dist/chunk-DIB4GX3G.js +0 -8
- package/dist/chunk-DKIJ2RP5.js +0 -68
- package/dist/chunk-DWPN6SCH.js +0 -172
- package/dist/chunk-EDKUFGPL.js +0 -64
- package/dist/chunk-ENY6SKIU.js +0 -1228
- package/dist/chunk-F4MGCZOX.js +0 -2943
- package/dist/chunk-FA6POGLH.js +0 -70
- package/dist/chunk-FSUR7GTG.js +0 -20475
- package/dist/chunk-GKEOHJL5.js +0 -1856
- package/dist/chunk-GMCB6W5Y.js +0 -333
- package/dist/chunk-IE2VPG7O.js +0 -1135
- package/dist/chunk-IZ5B5IHQ.js +0 -3656
- package/dist/chunk-JAL3CH4G.js +0 -27
- package/dist/chunk-KIIEUXD3.js +0 -1764
- package/dist/chunk-KXU4NIL7.js +0 -692
- package/dist/chunk-MTM5IFCY.js +0 -1190
- package/dist/chunk-N7TMSOZH.js +0 -235
- package/dist/chunk-NCEBK6PH.js +0 -36
- package/dist/chunk-NHJU2ZAL.js +0 -56
- package/dist/chunk-P6KTRNVC.js +0 -321
- package/dist/chunk-PDO5BLNM.js +0 -22
- package/dist/chunk-PDRNLLFT.js +0 -3295
- package/dist/chunk-PEQXYSSE.js +0 -45
- package/dist/chunk-PKEX2AQX.js +0 -198
- package/dist/chunk-PSJCGIGO.js +0 -1163
- package/dist/chunk-PZUSUL55.js +0 -506
- package/dist/chunk-R45M4TUS.js +0 -37
- package/dist/chunk-RC745GGZ.js +0 -122
- package/dist/chunk-SP3QKZXJ.js +0 -820
- package/dist/chunk-TXKUKMVO.js +0 -417
- package/dist/chunk-UE4GPWSD.js +0 -931
- package/dist/chunk-UIE3R5XU.js +0 -17
- package/dist/chunk-UXPG6ZIN.js +0 -84
- package/dist/chunk-VFUK3X5B.js +0 -50
- package/dist/chunk-VNMJWREK.js +0 -787
- package/dist/chunk-VZ7CSOX2.js +0 -706
- package/dist/chunk-WCNQJKFT.js +0 -10
- package/dist/chunk-WDZR3QQM.js +0 -135
- package/dist/chunk-WLYVJCHV.js +0 -1433
- package/dist/chunk-WS7NAFTM.js +0 -1520
- package/dist/chunk-X6GDDWQH.js +0 -60
- package/dist/chunk-XPDEYNQI.js +0 -102
- package/dist/chunk-XRW44VGM.js +0 -58
- package/dist/chunk-Y2CM7HXH.js +0 -111
- package/dist/chunk-Y7X25LFY.js +0 -15
- package/dist/conversation-work-VIOQ5ZF3.js +0 -54
- package/dist/db-NGDJV7YH.js +0 -26
- package/dist/discovery-DWVDM7UK.js +0 -44
- package/dist/experimental-P5FZM4CU.js +0 -12
- package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
- package/dist/oauth-relay-5XPVRPYB.js +0 -8
- package/dist/plugins-YTYJMMJF.js +0 -14
- package/dist/runner-ORTNSB2Q.js +0 -522
- package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
- package/dist/sentry-MB3NL3IR.js +0 -42
- package/dist/skills-H376WQOH.js +0 -27
- package/dist/spawn-T43WHLOS.js +0 -42
- package/dist/task-runner-LBZWF32V.js +0 -36
- package/dist/validation-REJLRKI6.js +0 -17
- package/dist/work-3KJ3RE76.js +0 -51
- package/dist/worker-QFEBU72E.js +0 -24
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import { F as isRecord, I as toOptionalNumber, L as toOptionalString } from "./logging-BMJzdDlP.js";
|
|
2
|
+
import { Et as getDb, ut as JUNIOR_THREAD_STATE_TTL_MS } from "./agent-hooks-DXHpTJ4m.js";
|
|
3
|
+
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
4
|
+
import { u as juniorLocationConfigurations } from "./executor-BlUhFL7_.js";
|
|
5
|
+
import { $ as persistConversationMessages } from "./checkpoint-CrViJ8Ps.js";
|
|
6
|
+
import { w as getStateAdapter } from "./profile-T0tXE4zu.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { randomUUID } from "node:crypto";
|
|
9
|
+
import { and, eq, sql } from "drizzle-orm";
|
|
10
|
+
//#region src/chat/configuration/types.ts
|
|
11
|
+
/** JSON value accepted by Location configuration. */
|
|
12
|
+
const configValueSchema = z.json();
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/chat/configuration/validation.ts
|
|
15
|
+
const CONFIG_KEY_RE = /^[a-z0-9]+(?:\.[a-z0-9-]+)+$/;
|
|
16
|
+
const SECRET_KEY_RE = /(?:^|[_.-])(token|secret|password|passphrase|api[-_]?key|private[-_]?key|credential|auth)(?:$|[_.-])/i;
|
|
17
|
+
const SECRET_VALUE_PATTERNS = [
|
|
18
|
+
/\bBearer\s+[A-Za-z0-9._-]{16,}\b/i,
|
|
19
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
20
|
+
/\b(?:ghp|ghs|github_pat)_[A-Za-z0-9_]{20,}\b/,
|
|
21
|
+
/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/i,
|
|
22
|
+
/\bsk-[A-Za-z0-9]{20,}\b/,
|
|
23
|
+
/\bAIza[0-9A-Za-z\-_]{30,}\b/,
|
|
24
|
+
/\bAKIA[0-9A-Z]{16}\b/,
|
|
25
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/
|
|
26
|
+
];
|
|
27
|
+
function validateConfigKey(key) {
|
|
28
|
+
const trimmed = key.trim();
|
|
29
|
+
if (!trimmed) return "Configuration key must not be empty";
|
|
30
|
+
if (!CONFIG_KEY_RE.test(trimmed)) return `Invalid configuration key "${key}"; expected dotted lowercase namespace (for example "provider.repo")`;
|
|
31
|
+
if (SECRET_KEY_RE.test(trimmed)) return `Configuration key "${key}" appears to be secret-related and is not allowed`;
|
|
32
|
+
}
|
|
33
|
+
function collectStringValues(value, output, depth = 0) {
|
|
34
|
+
if (depth > 5) return;
|
|
35
|
+
if (typeof value === "string") {
|
|
36
|
+
output.push(value);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
for (const item of value) collectStringValues(item, output, depth + 1);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (value && typeof value === "object") for (const [key, nested] of Object.entries(value)) {
|
|
44
|
+
output.push(key);
|
|
45
|
+
collectStringValues(nested, output, depth + 1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function validateConfigValue(value) {
|
|
49
|
+
const stringValues = [];
|
|
50
|
+
collectStringValues(value, stringValues);
|
|
51
|
+
for (const text of stringValues) for (const pattern of SECRET_VALUE_PATTERNS) if (pattern.test(text)) return "Configuration value appears to contain secret material and is not allowed";
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/chat/configuration/service.ts
|
|
55
|
+
function sanitizeEntry(value) {
|
|
56
|
+
if (!isRecord(value)) return;
|
|
57
|
+
const key = toOptionalString(value.key);
|
|
58
|
+
if (!key || validateConfigKey(key)) return;
|
|
59
|
+
const updatedAt = toOptionalString(value.updatedAt);
|
|
60
|
+
if (!updatedAt) return;
|
|
61
|
+
if (value.scope !== "channel" && value.scope !== "conversation" && value.scope !== "location") return;
|
|
62
|
+
const parsedValue = configValueSchema.safeParse(value.value);
|
|
63
|
+
if (!parsedValue.success) return;
|
|
64
|
+
return {
|
|
65
|
+
key,
|
|
66
|
+
value: parsedValue.data,
|
|
67
|
+
scope: "location",
|
|
68
|
+
updatedAt,
|
|
69
|
+
updatedBy: toOptionalString(value.updatedBy),
|
|
70
|
+
source: toOptionalString(value.source),
|
|
71
|
+
expiresAt: toOptionalString(value.expiresAt)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Coerce legacy persisted configuration into current Location entries. */
|
|
75
|
+
function coerceLegacyLocationConfig(raw) {
|
|
76
|
+
const rawConfig = isRecord(raw) && isRecord(raw.configuration) ? raw.configuration : {};
|
|
77
|
+
const rawEntries = isRecord(rawConfig.entries) ? rawConfig.entries : {};
|
|
78
|
+
return Object.values(rawEntries).flatMap((value) => {
|
|
79
|
+
const entry = sanitizeEntry(value);
|
|
80
|
+
return entry ? [entry] : [];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** Create a Location configuration service over entry-level storage. */
|
|
84
|
+
function createLocationConfigurationService(storage) {
|
|
85
|
+
const get = async (key) => await storage.get(key.trim());
|
|
86
|
+
const set = async (input) => {
|
|
87
|
+
const normalizedKey = input.key.trim();
|
|
88
|
+
const keyError = validateConfigKey(normalizedKey);
|
|
89
|
+
if (keyError) throw new Error(keyError);
|
|
90
|
+
const value = configValueSchema.parse(input.value);
|
|
91
|
+
const valueError = validateConfigValue(value);
|
|
92
|
+
if (valueError) throw new Error(valueError);
|
|
93
|
+
const entry = {
|
|
94
|
+
key: normalizedKey,
|
|
95
|
+
value,
|
|
96
|
+
scope: "location",
|
|
97
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
98
|
+
updatedBy: toOptionalString(input.updatedBy),
|
|
99
|
+
source: toOptionalString(input.source),
|
|
100
|
+
expiresAt: toOptionalString(input.expiresAt)
|
|
101
|
+
};
|
|
102
|
+
await storage.set(entry);
|
|
103
|
+
return entry;
|
|
104
|
+
};
|
|
105
|
+
const unset = async (key) => await storage.unset(key.trim());
|
|
106
|
+
const list = async (options = {}) => {
|
|
107
|
+
const prefix = options.prefix?.trim();
|
|
108
|
+
return (await storage.list()).filter((entry) => prefix ? entry.key.startsWith(prefix) : true).sort((a, b) => a.key.localeCompare(b.key));
|
|
109
|
+
};
|
|
110
|
+
const resolve = async (key) => (await get(key))?.value;
|
|
111
|
+
const resolveValues = async (options = {}) => {
|
|
112
|
+
const keys = options.keys?.map((key) => key.trim()).filter(Boolean);
|
|
113
|
+
const entries = await list(options.prefix ? { prefix: options.prefix } : {});
|
|
114
|
+
return Object.fromEntries(entries.filter((entry) => !keys || keys.includes(entry.key)).map((entry) => [entry.key, entry.value]));
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
get,
|
|
118
|
+
set,
|
|
119
|
+
unset,
|
|
120
|
+
list,
|
|
121
|
+
resolve,
|
|
122
|
+
resolveValues
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/chat/configuration/sql.ts
|
|
127
|
+
/** Resolve or create the canonical Location for a Slack Destination. */
|
|
128
|
+
async function resolveLocationId(db, destination) {
|
|
129
|
+
const channelId = destination.channelId;
|
|
130
|
+
const now = /* @__PURE__ */ new Date();
|
|
131
|
+
const location = (await db.insert(juniorDestinations).values({
|
|
132
|
+
id: randomUUID(),
|
|
133
|
+
provider: "slack",
|
|
134
|
+
providerTenantId: destination.teamId,
|
|
135
|
+
providerDestinationId: channelId,
|
|
136
|
+
kind: channelId.startsWith("D") ? "dm" : channelId.startsWith("G") ? "group" : "channel",
|
|
137
|
+
parentDestinationId: null,
|
|
138
|
+
displayName: null,
|
|
139
|
+
visibility: "unknown",
|
|
140
|
+
metadata: { platform: "slack" },
|
|
141
|
+
createdAt: now,
|
|
142
|
+
updatedAt: now
|
|
143
|
+
}).onConflictDoUpdate({
|
|
144
|
+
target: [
|
|
145
|
+
juniorDestinations.provider,
|
|
146
|
+
juniorDestinations.providerTenantId,
|
|
147
|
+
juniorDestinations.providerDestinationId
|
|
148
|
+
],
|
|
149
|
+
set: { id: sql`${juniorDestinations.id}` }
|
|
150
|
+
}).returning({ id: juniorDestinations.id }))[0];
|
|
151
|
+
if (!location) throw new Error("Location upsert returned no row");
|
|
152
|
+
return location.id;
|
|
153
|
+
}
|
|
154
|
+
/** Persist configuration rows for the Location addressed by a Slack Destination. */
|
|
155
|
+
function createSqlLocationConfigurationStorage(db, destination) {
|
|
156
|
+
const locationWhere = and(eq(juniorDestinations.provider, "slack"), eq(juniorDestinations.providerTenantId, destination.teamId), eq(juniorDestinations.providerDestinationId, destination.channelId));
|
|
157
|
+
const selectEntries = () => db.select({
|
|
158
|
+
key: juniorLocationConfigurations.key,
|
|
159
|
+
value: juniorLocationConfigurations.value,
|
|
160
|
+
updatedAt: juniorLocationConfigurations.updatedAt,
|
|
161
|
+
updatedBy: juniorLocationConfigurations.updatedBy,
|
|
162
|
+
source: juniorLocationConfigurations.source,
|
|
163
|
+
expiresAt: juniorLocationConfigurations.expiresAt
|
|
164
|
+
}).from(juniorLocationConfigurations).innerJoin(juniorDestinations, eq(juniorDestinations.id, juniorLocationConfigurations.locationId));
|
|
165
|
+
const entryFromRow = (row) => ({
|
|
166
|
+
key: row.key,
|
|
167
|
+
value: configValueSchema.parse(JSON.parse(row.value)),
|
|
168
|
+
scope: "location",
|
|
169
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
170
|
+
updatedBy: row.updatedBy ?? void 0,
|
|
171
|
+
source: row.source ?? void 0,
|
|
172
|
+
expiresAt: row.expiresAt ?? void 0
|
|
173
|
+
});
|
|
174
|
+
const list = async () => (await selectEntries().where(locationWhere)).map(entryFromRow);
|
|
175
|
+
const set = async (entry) => {
|
|
176
|
+
const locationId = await resolveLocationId(db, destination);
|
|
177
|
+
await db.insert(juniorLocationConfigurations).values({
|
|
178
|
+
locationId,
|
|
179
|
+
key: entry.key,
|
|
180
|
+
value: JSON.stringify(entry.value),
|
|
181
|
+
updatedAt: new Date(entry.updatedAt),
|
|
182
|
+
updatedBy: entry.updatedBy,
|
|
183
|
+
source: entry.source,
|
|
184
|
+
expiresAt: entry.expiresAt
|
|
185
|
+
}).onConflictDoUpdate({
|
|
186
|
+
target: [juniorLocationConfigurations.locationId, juniorLocationConfigurations.key],
|
|
187
|
+
set: {
|
|
188
|
+
value: JSON.stringify(entry.value),
|
|
189
|
+
updatedAt: new Date(entry.updatedAt),
|
|
190
|
+
updatedBy: entry.updatedBy,
|
|
191
|
+
source: entry.source,
|
|
192
|
+
expiresAt: entry.expiresAt
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
get: async (key) => {
|
|
198
|
+
const rows = await selectEntries().where(and(locationWhere, eq(juniorLocationConfigurations.key, key))).limit(1);
|
|
199
|
+
return rows[0] ? entryFromRow(rows[0]) : void 0;
|
|
200
|
+
},
|
|
201
|
+
list,
|
|
202
|
+
set,
|
|
203
|
+
unset: async (key) => {
|
|
204
|
+
const locationId = await resolveLocationId(db, destination);
|
|
205
|
+
return (await db.delete(juniorLocationConfigurations).where(and(eq(juniorLocationConfigurations.locationId, locationId), eq(juniorLocationConfigurations.key, key))).returning({ key: juniorLocationConfigurations.key })).length > 0;
|
|
206
|
+
},
|
|
207
|
+
insertLegacy: async (entries) => {
|
|
208
|
+
if (entries.length === 0) return;
|
|
209
|
+
const locationId = await resolveLocationId(db, destination);
|
|
210
|
+
await db.insert(juniorLocationConfigurations).values(entries.map((entry) => ({
|
|
211
|
+
locationId,
|
|
212
|
+
key: entry.key,
|
|
213
|
+
value: JSON.stringify(entry.value),
|
|
214
|
+
updatedAt: new Date(entry.updatedAt),
|
|
215
|
+
updatedBy: entry.updatedBy,
|
|
216
|
+
source: entry.source,
|
|
217
|
+
expiresAt: entry.expiresAt
|
|
218
|
+
}))).onConflictDoNothing();
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** Resolve SQL-owned Location configuration and copy live legacy entries once. */
|
|
223
|
+
function createDurableLocationConfigurationService(args) {
|
|
224
|
+
const storage = createSqlLocationConfigurationStorage(args.db, args.destination);
|
|
225
|
+
let cutover;
|
|
226
|
+
const ensureCutover = () => cutover ??= (async () => {
|
|
227
|
+
if ((await storage.list()).length > 0) return;
|
|
228
|
+
const legacy = coerceLegacyLocationConfig(await args.loadLegacy());
|
|
229
|
+
await storage.insertLegacy(legacy);
|
|
230
|
+
})();
|
|
231
|
+
return createLocationConfigurationService({
|
|
232
|
+
get: async (key) => {
|
|
233
|
+
await ensureCutover();
|
|
234
|
+
return await storage.get(key);
|
|
235
|
+
},
|
|
236
|
+
list: async () => {
|
|
237
|
+
await ensureCutover();
|
|
238
|
+
return await storage.list();
|
|
239
|
+
},
|
|
240
|
+
set: async (entry) => {
|
|
241
|
+
await ensureCutover();
|
|
242
|
+
await storage.set(entry);
|
|
243
|
+
},
|
|
244
|
+
unset: async (key) => {
|
|
245
|
+
await ensureCutover();
|
|
246
|
+
return await storage.unset(key);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/chat/state/conversation.ts
|
|
252
|
+
function defaultConversationState() {
|
|
253
|
+
return {
|
|
254
|
+
schemaVersion: 1,
|
|
255
|
+
messages: [],
|
|
256
|
+
compactions: [],
|
|
257
|
+
processing: {},
|
|
258
|
+
vision: { byFileId: {} }
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function coercePendingAuthState(value) {
|
|
262
|
+
if (!isRecord(value)) return;
|
|
263
|
+
const kind = value.kind;
|
|
264
|
+
const provider = toOptionalString(value.provider);
|
|
265
|
+
const actorId = toOptionalString(value.actorId);
|
|
266
|
+
const authSessionId = toOptionalString(value.authSessionId);
|
|
267
|
+
const scope = toOptionalString(value.scope);
|
|
268
|
+
const sessionId = toOptionalString(value.sessionId);
|
|
269
|
+
const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
|
|
270
|
+
if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || kind === "mcp" && !authSessionId || !sessionId || typeof linkSentAtMs !== "number") return;
|
|
271
|
+
const base = {
|
|
272
|
+
provider,
|
|
273
|
+
actorId,
|
|
274
|
+
...scope ? { scope } : void 0,
|
|
275
|
+
sessionId,
|
|
276
|
+
linkSentAtMs
|
|
277
|
+
};
|
|
278
|
+
return kind === "mcp" ? {
|
|
279
|
+
...base,
|
|
280
|
+
authSessionId,
|
|
281
|
+
kind
|
|
282
|
+
} : {
|
|
283
|
+
...base,
|
|
284
|
+
kind
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
/** Safely coerce an unknown persisted value into a ThreadConversationState. */
|
|
288
|
+
function coerceThreadConversationState(value) {
|
|
289
|
+
if (!isRecord(value)) return defaultConversationState();
|
|
290
|
+
const root = value;
|
|
291
|
+
const rawConversation = isRecord(root.conversation) ? root.conversation : {};
|
|
292
|
+
const messages = [];
|
|
293
|
+
const compactions = [];
|
|
294
|
+
const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
|
|
295
|
+
const processing = {
|
|
296
|
+
activeTurnId: toOptionalString(rawProcessing.activeTurnId),
|
|
297
|
+
lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
|
|
298
|
+
pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
|
|
299
|
+
};
|
|
300
|
+
const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
|
|
301
|
+
const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
|
|
302
|
+
const byFileId = {};
|
|
303
|
+
for (const [fileId, value] of Object.entries(rawVisionByFileId)) {
|
|
304
|
+
if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
|
|
305
|
+
if (!isRecord(value)) continue;
|
|
306
|
+
const summary = toOptionalString(value.summary);
|
|
307
|
+
const analyzedAtMs = toOptionalNumber(value.analyzedAtMs);
|
|
308
|
+
if (!summary || !analyzedAtMs) continue;
|
|
309
|
+
byFileId[fileId] = {
|
|
310
|
+
summary,
|
|
311
|
+
analyzedAtMs
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
schemaVersion: 1,
|
|
316
|
+
messages,
|
|
317
|
+
compactions,
|
|
318
|
+
processing,
|
|
319
|
+
vision: {
|
|
320
|
+
backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
|
|
321
|
+
byFileId
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Wrap active conversation control into the thread scratch envelope.
|
|
327
|
+
*
|
|
328
|
+
* Visible history lives in SQL. Image summaries use a separate disposable
|
|
329
|
+
* cache, so thread scratch does not retain them.
|
|
330
|
+
*/
|
|
331
|
+
function buildConversationStatePatch(conversation) {
|
|
332
|
+
return { conversation: {
|
|
333
|
+
schemaVersion: 1,
|
|
334
|
+
processing: { ...conversation.processing }
|
|
335
|
+
} };
|
|
336
|
+
}
|
|
337
|
+
//#endregion
|
|
338
|
+
//#region src/chat/runtime/thread-state.ts
|
|
339
|
+
function threadStateKey(threadId) {
|
|
340
|
+
return `thread-state:${threadId}`;
|
|
341
|
+
}
|
|
342
|
+
function channelStateKey(channelId) {
|
|
343
|
+
return `channel-state:${channelId}`;
|
|
344
|
+
}
|
|
345
|
+
function buildThreadStatePayload(patch) {
|
|
346
|
+
const payload = {};
|
|
347
|
+
if (patch.conversation) Object.assign(payload, buildConversationStatePatch(patch.conversation));
|
|
348
|
+
if (patch.sandboxRef !== void 0) {
|
|
349
|
+
payload.app_sandbox_id = patch.sandboxRef?.id ?? "";
|
|
350
|
+
payload.app_sandbox_dependency_profile_hash = patch.sandboxRef?.profileHash ?? "";
|
|
351
|
+
payload.app_sandbox_workspace_id = patch.sandboxRef?.workspaceId ?? "";
|
|
352
|
+
}
|
|
353
|
+
return payload;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Merge a payload into thread scratch with Junior's TTL.
|
|
357
|
+
*
|
|
358
|
+
* Chat SDK state writes hardcode a 30-day TTL. This boundary owns Junior's
|
|
359
|
+
* shorter retention policy for the same scratch keys.
|
|
360
|
+
*/
|
|
361
|
+
async function mergePersistedState(key, payload) {
|
|
362
|
+
if (Object.keys(payload).length === 0) return;
|
|
363
|
+
const stateAdapter = getStateAdapter();
|
|
364
|
+
await stateAdapter.connect();
|
|
365
|
+
const existing = await stateAdapter.get(key) ?? {};
|
|
366
|
+
await stateAdapter.set(key, {
|
|
367
|
+
...existing,
|
|
368
|
+
...payload
|
|
369
|
+
}, JUNIOR_THREAD_STATE_TTL_MS);
|
|
370
|
+
}
|
|
371
|
+
/** Extract persisted sandbox metadata from thread state payload. */
|
|
372
|
+
function getPersistedSandboxState(state) {
|
|
373
|
+
const id = toOptionalString(state.app_sandbox_id);
|
|
374
|
+
if (!id) return;
|
|
375
|
+
const profileHash = toOptionalString(state.app_sandbox_dependency_profile_hash);
|
|
376
|
+
const workspaceId = toOptionalString(state.app_sandbox_workspace_id);
|
|
377
|
+
return {
|
|
378
|
+
id,
|
|
379
|
+
...profileHash ? { profileHash } : void 0,
|
|
380
|
+
...workspaceId ? { workspaceId } : void 0
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
/** Persist a thread-state patch for a Chat thread handle. */
|
|
384
|
+
async function persistThreadState(thread, patch) {
|
|
385
|
+
if (patch.conversation) await persistConversationMessages({
|
|
386
|
+
conversation: patch.conversation,
|
|
387
|
+
conversationId: toOptionalString(thread.id) ?? toOptionalString(thread.runId)
|
|
388
|
+
});
|
|
389
|
+
await persistThreadRuntimeState(thread, patch);
|
|
390
|
+
}
|
|
391
|
+
/** Persist only the Redis-backed runtime scratch in a thread-state patch. */
|
|
392
|
+
async function persistThreadRuntimeState(thread, patch) {
|
|
393
|
+
const threadId = toOptionalString(thread.id) ?? toOptionalString(thread.runId);
|
|
394
|
+
if (!threadId) throw new Error("thread id is required to persist runtime scratch");
|
|
395
|
+
await mergePersistedState(threadStateKey(threadId), buildThreadStatePayload(patch));
|
|
396
|
+
}
|
|
397
|
+
/** Load the persisted state payload for a thread without requiring a Chat singleton. */
|
|
398
|
+
async function getPersistedThreadState(threadId) {
|
|
399
|
+
const stateAdapter = getStateAdapter();
|
|
400
|
+
await stateAdapter.connect();
|
|
401
|
+
return await stateAdapter.get(threadStateKey(threadId)) ?? {};
|
|
402
|
+
}
|
|
403
|
+
/** Persist a thread-state patch by thread id without constructing a Chat thread. */
|
|
404
|
+
async function persistThreadStateById(threadId, patch) {
|
|
405
|
+
if (patch.conversation) await persistConversationMessages({
|
|
406
|
+
conversation: patch.conversation,
|
|
407
|
+
conversationId: threadId
|
|
408
|
+
});
|
|
409
|
+
await mergePersistedState(threadStateKey(threadId), buildThreadStatePayload(patch));
|
|
410
|
+
}
|
|
411
|
+
/** Load legacy Redis-backed channel state during the SQL cutover. */
|
|
412
|
+
async function getLegacyChannelState(channelId) {
|
|
413
|
+
const stateAdapter = getStateAdapter();
|
|
414
|
+
await stateAdapter.connect();
|
|
415
|
+
return await stateAdapter.get(channelStateKey(channelId)) ?? {};
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Resolve durable location configuration.
|
|
419
|
+
*
|
|
420
|
+
* Slack still reads the old channel-id Redis bag once during cutover.
|
|
421
|
+
*/
|
|
422
|
+
function getLocationConfigurationService(destination) {
|
|
423
|
+
if (destination.platform !== "slack") throw new Error("Location configuration requires a Slack Location");
|
|
424
|
+
return createDurableLocationConfigurationService({
|
|
425
|
+
destination,
|
|
426
|
+
db: getDb(),
|
|
427
|
+
loadLegacy: async () => await getLegacyChannelState(destination.channelId)
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
//#endregion
|
|
431
|
+
export { persistThreadState as a, configValueSchema as c, persistThreadRuntimeState as i, getPersistedSandboxState as n, persistThreadStateById as o, getPersistedThreadState as r, coerceThreadConversationState as s, getLocationConfigurationService as t };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Ct as getConversationEventStore } from "./agent-hooks-DXHpTJ4m.js";
|
|
2
|
+
import { n as runAgentWithTimeout } from "./agent-runner-C04colea.js";
|
|
3
|
+
import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-CcnwrOcp.js";
|
|
4
|
+
//#region src/chat/runtime/turn-execution.ts
|
|
5
|
+
/** An error thrown while the agent advances a Run. */
|
|
6
|
+
var AgentRunError = class extends Error {
|
|
7
|
+
constructor(cause) {
|
|
8
|
+
super(cause instanceof Error ? cause.message : "Agent Run failed", { cause });
|
|
9
|
+
this.name = "AgentRunError";
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Run the agent and finish the Turn after the caller saves its result.
|
|
14
|
+
*
|
|
15
|
+
* A paused Run, or a Run waiting for authorization, leaves the Turn open. A
|
|
16
|
+
* save error also leaves the Turn open so the worker can retry or recover it.
|
|
17
|
+
* A timeout aborts the Run before this function reports the failure.
|
|
18
|
+
*/
|
|
19
|
+
async function executeTurn(agentRunner, run, saveResult, timeoutMs) {
|
|
20
|
+
let outcome;
|
|
21
|
+
try {
|
|
22
|
+
outcome = await runAgentWithTimeout(agentRunner, run, timeoutMs);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
throw new AgentRunError(error);
|
|
25
|
+
}
|
|
26
|
+
if (outcome.status !== "completed") return outcome;
|
|
27
|
+
const saved = await saveResult(outcome.result);
|
|
28
|
+
const lifecycle = new ConversationTurnLifecycleService(getConversationEventStore());
|
|
29
|
+
const common = {
|
|
30
|
+
conversationId: run.conversationId,
|
|
31
|
+
createdAtMs: saved.finishedAtMs ?? Date.now(),
|
|
32
|
+
turnId: run.turnId
|
|
33
|
+
};
|
|
34
|
+
if (saved.outcome === "failed") await lifecycle.fail({
|
|
35
|
+
...common,
|
|
36
|
+
...saved.eventId ? { eventId: saved.eventId } : void 0,
|
|
37
|
+
failureCode: saved.failureCode,
|
|
38
|
+
...saved.failureReason ? { failureReason: saved.failureReason } : void 0
|
|
39
|
+
});
|
|
40
|
+
else await lifecycle.complete({
|
|
41
|
+
...common,
|
|
42
|
+
outcome: saved.outcome
|
|
43
|
+
});
|
|
44
|
+
return { status: "completed" };
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { executeTurn as n, AgentRunError as t };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Ct as getConversationEventStore } from "./agent-hooks-DXHpTJ4m.js";
|
|
2
|
+
//#region src/chat/conversations/turn-lifecycle.ts
|
|
3
|
+
/** Persist correlated turn lifecycle facts with retry-safe terminal exclusion. */
|
|
4
|
+
var ConversationTurnLifecycleService = class {
|
|
5
|
+
events;
|
|
6
|
+
constructor(events) {
|
|
7
|
+
this.events = events;
|
|
8
|
+
}
|
|
9
|
+
/** Record the turn start once after every input message is durable. */
|
|
10
|
+
async start(input) {
|
|
11
|
+
await this.events.append(input.conversationId, [{
|
|
12
|
+
idempotencyKey: `turn:${input.turnId}:started`,
|
|
13
|
+
createdAtMs: input.createdAtMs,
|
|
14
|
+
data: {
|
|
15
|
+
type: "turn_started",
|
|
16
|
+
turnId: input.turnId,
|
|
17
|
+
inputMessageIds: input.inputMessageIds,
|
|
18
|
+
surface: input.surface
|
|
19
|
+
}
|
|
20
|
+
}]);
|
|
21
|
+
}
|
|
22
|
+
/** Record successful or intentional-silence completion once. */
|
|
23
|
+
async complete(input) {
|
|
24
|
+
await this.events.append(input.conversationId, [{
|
|
25
|
+
idempotencyKey: `turn:${input.turnId}:terminal`,
|
|
26
|
+
createdAtMs: input.createdAtMs,
|
|
27
|
+
data: {
|
|
28
|
+
type: "turn_completed",
|
|
29
|
+
turnId: input.turnId,
|
|
30
|
+
outcome: input.outcome
|
|
31
|
+
}
|
|
32
|
+
}]);
|
|
33
|
+
}
|
|
34
|
+
/** Record a classified failure once. Do not store raw error text. */
|
|
35
|
+
async fail(input) {
|
|
36
|
+
await this.events.append(input.conversationId, [{
|
|
37
|
+
idempotencyKey: `turn:${input.turnId}:terminal`,
|
|
38
|
+
createdAtMs: input.createdAtMs,
|
|
39
|
+
data: {
|
|
40
|
+
type: "turn_failed",
|
|
41
|
+
turnId: input.turnId,
|
|
42
|
+
failureCode: input.failureCode,
|
|
43
|
+
...input.failureReason ? { failureReason: input.failureReason } : void 0,
|
|
44
|
+
...input.eventId ? { eventId: input.eventId } : void 0
|
|
45
|
+
}
|
|
46
|
+
}]);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
/** Return the Turn lifecycle that records Conversation events. */
|
|
50
|
+
function getTurnLifecycle() {
|
|
51
|
+
return new ConversationTurnLifecycleService(getConversationEventStore());
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { getTurnLifecycle as n, ConversationTurnLifecycleService as t };
|