@sentry/junior 0.51.0 → 0.53.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/app.js +523 -295
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -494,6 +494,7 @@ function coerceThreadConversationState(value) {
|
|
|
494
494
|
const processing = {
|
|
495
495
|
activeTurnId: toOptionalString(rawProcessing.activeTurnId),
|
|
496
496
|
lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
|
|
497
|
+
lastSessionId: toOptionalString(rawProcessing.lastSessionId),
|
|
497
498
|
pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
|
|
498
499
|
};
|
|
499
500
|
const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
|
|
@@ -2101,17 +2102,24 @@ function startActiveTurn(args) {
|
|
|
2101
2102
|
args.conversation.processing.activeTurnId = args.nextTurnId;
|
|
2102
2103
|
args.updateConversationStats(args.conversation);
|
|
2103
2104
|
}
|
|
2104
|
-
function
|
|
2105
|
-
if (!
|
|
2106
|
-
|
|
2105
|
+
function clearActiveTurn(conversation, sessionId) {
|
|
2106
|
+
if (!sessionId || conversation.processing.activeTurnId === sessionId) {
|
|
2107
|
+
conversation.processing.activeTurnId = void 0;
|
|
2107
2108
|
}
|
|
2109
|
+
}
|
|
2110
|
+
function markTurnClosed(args) {
|
|
2111
|
+
clearActiveTurn(args.conversation, args.sessionId);
|
|
2112
|
+
args.conversation.processing.lastCompletedAtMs = args.nowMs;
|
|
2113
|
+
args.updateConversationStats(args.conversation);
|
|
2114
|
+
}
|
|
2115
|
+
function markTurnCompleted(args) {
|
|
2116
|
+
clearActiveTurn(args.conversation, args.sessionId);
|
|
2117
|
+
args.conversation.processing.lastSessionId = args.sessionId;
|
|
2108
2118
|
args.conversation.processing.lastCompletedAtMs = args.nowMs;
|
|
2109
2119
|
args.updateConversationStats(args.conversation);
|
|
2110
2120
|
}
|
|
2111
2121
|
function markTurnFailed(args) {
|
|
2112
|
-
|
|
2113
|
-
args.conversation.processing.activeTurnId = void 0;
|
|
2114
|
-
}
|
|
2122
|
+
clearActiveTurn(args.conversation, args.sessionId);
|
|
2115
2123
|
args.conversation.processing.lastCompletedAtMs = args.nowMs;
|
|
2116
2124
|
args.markConversationMessage(args.conversation, args.userMessageId, {
|
|
2117
2125
|
replied: false,
|
|
@@ -2450,6 +2458,9 @@ import { Agent as Agent2 } from "@mariozechner/pi-agent-core";
|
|
|
2450
2458
|
import fs from "fs";
|
|
2451
2459
|
import path2 from "path";
|
|
2452
2460
|
|
|
2461
|
+
// src/chat/turn-context-tag.ts
|
|
2462
|
+
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
2463
|
+
|
|
2453
2464
|
// src/chat/interruption-marker.ts
|
|
2454
2465
|
var INTERRUPTED_MARKER = "\n\n[Response interrupted before completion]";
|
|
2455
2466
|
function getInterruptionMarker() {
|
|
@@ -3020,7 +3031,6 @@ function formatSlackCapabilityNames(capabilities) {
|
|
|
3020
3031
|
}
|
|
3021
3032
|
var HEADER = "You are a Slack-based helper assistant. Follow the personality block for voice and tone in every reply. The behavior and output blocks define platform mechanics and override personality only when those mechanics conflict.";
|
|
3022
3033
|
var TURN_CONTEXT_HEADER = "Per-turn runtime context for this request. Treat these blocks as trusted runtime facts and skill/provider instructions for the current turn; the static system prompt remains authoritative.";
|
|
3023
|
-
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
3024
3034
|
var TOOL_POLICY_RULES = [
|
|
3025
3035
|
"- Tool schemas are the source of truth for parameters; tool names are case-sensitive, so call tools exactly by their exposed names and do not invent arguments.",
|
|
3026
3036
|
"- Use tools for actionable work and for facts that are mutable, external, repository-backed, provider-backed, or requested as verified/current. Stable general knowledge and already-provided context may be answered directly.",
|
|
@@ -7882,6 +7892,7 @@ import { Type as Type21 } from "@sinclair/typebox";
|
|
|
7882
7892
|
|
|
7883
7893
|
// src/chat/respond-helpers.ts
|
|
7884
7894
|
var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
|
|
7895
|
+
var RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
|
|
7885
7896
|
function getSessionIdentifiers(context) {
|
|
7886
7897
|
return {
|
|
7887
7898
|
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId,
|
|
@@ -8044,6 +8055,75 @@ function getPiMessageRole(value) {
|
|
|
8044
8055
|
const role = value.role;
|
|
8045
8056
|
return typeof role === "string" ? role : void 0;
|
|
8046
8057
|
}
|
|
8058
|
+
function getUserMessageContent(message) {
|
|
8059
|
+
const record = message;
|
|
8060
|
+
return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
|
|
8061
|
+
}
|
|
8062
|
+
function isRuntimeTurnContextPart(part, marker) {
|
|
8063
|
+
return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(marker);
|
|
8064
|
+
}
|
|
8065
|
+
function replaceRuntimeTurnContext(message, turnContextPrompt) {
|
|
8066
|
+
const content = getUserMessageContent(message);
|
|
8067
|
+
if (!content) {
|
|
8068
|
+
return void 0;
|
|
8069
|
+
}
|
|
8070
|
+
const marker = turnContextPrompt.split("\n", 1)[0];
|
|
8071
|
+
const contextIndex = content.findIndex(
|
|
8072
|
+
(part) => isRuntimeTurnContextPart(part, marker)
|
|
8073
|
+
);
|
|
8074
|
+
if (contextIndex < 0) {
|
|
8075
|
+
return void 0;
|
|
8076
|
+
}
|
|
8077
|
+
const nextContent = [...content];
|
|
8078
|
+
nextContent[contextIndex] = {
|
|
8079
|
+
...nextContent[contextIndex],
|
|
8080
|
+
text: turnContextPrompt
|
|
8081
|
+
};
|
|
8082
|
+
return {
|
|
8083
|
+
...message,
|
|
8084
|
+
content: nextContent
|
|
8085
|
+
};
|
|
8086
|
+
}
|
|
8087
|
+
function refreshRuntimeTurnContext(messages, turnContextPrompt) {
|
|
8088
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
8089
|
+
const updated = replaceRuntimeTurnContext(
|
|
8090
|
+
messages[index],
|
|
8091
|
+
turnContextPrompt
|
|
8092
|
+
);
|
|
8093
|
+
if (!updated) {
|
|
8094
|
+
continue;
|
|
8095
|
+
}
|
|
8096
|
+
const nextMessages = [...messages];
|
|
8097
|
+
nextMessages[index] = updated;
|
|
8098
|
+
return nextMessages;
|
|
8099
|
+
}
|
|
8100
|
+
return [
|
|
8101
|
+
...messages,
|
|
8102
|
+
{
|
|
8103
|
+
role: "user",
|
|
8104
|
+
content: [{ type: "text", text: turnContextPrompt }],
|
|
8105
|
+
timestamp: Date.now()
|
|
8106
|
+
}
|
|
8107
|
+
];
|
|
8108
|
+
}
|
|
8109
|
+
function stripRuntimeTurnContext(messages) {
|
|
8110
|
+
return messages.flatMap((message) => {
|
|
8111
|
+
const content = getUserMessageContent(message);
|
|
8112
|
+
if (!content) {
|
|
8113
|
+
return [message];
|
|
8114
|
+
}
|
|
8115
|
+
const nextContent = content.filter(
|
|
8116
|
+
(part) => !isRuntimeTurnContextPart(part, RUNTIME_TURN_CONTEXT_START)
|
|
8117
|
+
);
|
|
8118
|
+
if (nextContent.length === content.length) {
|
|
8119
|
+
return [message];
|
|
8120
|
+
}
|
|
8121
|
+
if (nextContent.length === 0) {
|
|
8122
|
+
return [];
|
|
8123
|
+
}
|
|
8124
|
+
return [{ ...message, content: nextContent }];
|
|
8125
|
+
});
|
|
8126
|
+
}
|
|
8047
8127
|
function extractAssistantText(message) {
|
|
8048
8128
|
const content = message.content ?? [];
|
|
8049
8129
|
return content.filter(
|
|
@@ -9138,84 +9218,51 @@ function createTracedStreamFn(base = streamSimple) {
|
|
|
9138
9218
|
// src/chat/sandbox/sandbox.ts
|
|
9139
9219
|
import fs4 from "fs/promises";
|
|
9140
9220
|
|
|
9141
|
-
// src/chat/sandbox/egress-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
]);
|
|
9150
|
-
return [...domains].sort((left, right) => left.localeCompare(right));
|
|
9151
|
-
}
|
|
9152
|
-
function providerEntries() {
|
|
9153
|
-
return getPluginProviders().map((plugin) => ({
|
|
9154
|
-
provider: plugin.manifest.name,
|
|
9155
|
-
domains: manifestDomains(plugin.manifest)
|
|
9156
|
-
})).filter((entry) => entry.domains.length > 0).sort((left, right) => left.provider.localeCompare(right.provider));
|
|
9157
|
-
}
|
|
9158
|
-
function resolveSandboxEgressProviderForHost(host) {
|
|
9159
|
-
return providerEntries().find(
|
|
9160
|
-
(entry) => entry.domains.some((domain) => matchesSandboxEgressDomain(host, domain))
|
|
9161
|
-
)?.provider;
|
|
9162
|
-
}
|
|
9163
|
-
function sandboxProxyUrl() {
|
|
9164
|
-
const baseUrl = resolveBaseUrl();
|
|
9165
|
-
if (!baseUrl) {
|
|
9166
|
-
throw new Error(
|
|
9167
|
-
"Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
9168
|
-
);
|
|
9169
|
-
}
|
|
9170
|
-
return new URL("/", baseUrl).toString();
|
|
9221
|
+
// src/chat/sandbox/egress-session.ts
|
|
9222
|
+
import { createHmac, randomUUID as randomUUID3, timingSafeEqual } from "crypto";
|
|
9223
|
+
var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
|
|
9224
|
+
var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
|
|
9225
|
+
var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
|
|
9226
|
+
var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
9227
|
+
function leaseKey(provider, context) {
|
|
9228
|
+
return `${SANDBOX_EGRESS_LEASE_PREFIX}:${provider}:${context.requesterId}:${context.egressId}:${context.contextId}`;
|
|
9171
9229
|
}
|
|
9172
|
-
function
|
|
9173
|
-
const
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
const entries = providerEntries();
|
|
9177
|
-
if (entries.length === 0) {
|
|
9178
|
-
return { allow };
|
|
9230
|
+
function getSandboxEgressSecret() {
|
|
9231
|
+
const explicit = process.env.JUNIOR_SANDBOX_EGRESS_SECRET?.trim();
|
|
9232
|
+
if (explicit) {
|
|
9233
|
+
return explicit;
|
|
9179
9234
|
}
|
|
9180
|
-
const
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
allow[domain] = [{ forwardURL }];
|
|
9184
|
-
}
|
|
9235
|
+
const sharedInternal = process.env.JUNIOR_INTERNAL_RESUME_SECRET?.trim();
|
|
9236
|
+
if (sharedInternal) {
|
|
9237
|
+
return sharedInternal;
|
|
9185
9238
|
}
|
|
9186
|
-
|
|
9239
|
+
throw new Error(
|
|
9240
|
+
"Cannot determine sandbox egress secret (set JUNIOR_SANDBOX_EGRESS_SECRET or JUNIOR_INTERNAL_RESUME_SECRET)"
|
|
9241
|
+
);
|
|
9187
9242
|
}
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
for (const plugin of getPluginProviders().sort(
|
|
9191
|
-
(left, right) => left.manifest.name.localeCompare(right.manifest.name)
|
|
9192
|
-
)) {
|
|
9193
|
-
Object.assign(env, resolvePluginCommandEnv(plugin.manifest));
|
|
9194
|
-
const credentials = plugin.manifest.credentials;
|
|
9195
|
-
if (credentials) {
|
|
9196
|
-
env[credentials.authTokenEnv] = resolveAuthTokenPlaceholder(credentials);
|
|
9197
|
-
}
|
|
9198
|
-
}
|
|
9199
|
-
return env;
|
|
9243
|
+
function base64Url(input) {
|
|
9244
|
+
return Buffer.from(input, "utf8").toString("base64url");
|
|
9200
9245
|
}
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
9204
|
-
var SANDBOX_EGRESS_SESSION_PREFIX = "sandbox-egress-session";
|
|
9205
|
-
var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
|
|
9206
|
-
var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
9207
|
-
function sessionKey2(egressId) {
|
|
9208
|
-
return `${SANDBOX_EGRESS_SESSION_PREFIX}:${egressId}`;
|
|
9246
|
+
function fromBase64Url(input) {
|
|
9247
|
+
return Buffer.from(input, "base64url").toString("utf8");
|
|
9209
9248
|
}
|
|
9210
|
-
function
|
|
9211
|
-
return
|
|
9249
|
+
function signPayload(payload) {
|
|
9250
|
+
return createHmac("sha256", getSandboxEgressSecret()).update(payload).digest("base64url");
|
|
9212
9251
|
}
|
|
9213
|
-
function
|
|
9252
|
+
function timingSafeMatch(expected, actual) {
|
|
9253
|
+
const expectedBuffer = Buffer.from(expected);
|
|
9254
|
+
const actualBuffer = Buffer.from(actual);
|
|
9255
|
+
if (expectedBuffer.length !== actualBuffer.length) {
|
|
9256
|
+
return false;
|
|
9257
|
+
}
|
|
9258
|
+
return timingSafeEqual(expectedBuffer, actualBuffer);
|
|
9259
|
+
}
|
|
9260
|
+
function parseRequesterContext(value) {
|
|
9214
9261
|
if (!value || typeof value !== "object") {
|
|
9215
9262
|
return void 0;
|
|
9216
9263
|
}
|
|
9217
9264
|
const record = value;
|
|
9218
|
-
if (typeof record.requesterId !== "string" || typeof record.expiresAtMs !== "number" || !Number.isFinite(record.expiresAtMs) || typeof record.
|
|
9265
|
+
if (typeof record.requesterId !== "string" || !record.requesterId || typeof record.egressId !== "string" || !record.egressId || typeof record.expiresAtMs !== "number" || !Number.isFinite(record.expiresAtMs) || typeof record.contextId !== "string" || !record.contextId) {
|
|
9219
9266
|
return void 0;
|
|
9220
9267
|
}
|
|
9221
9268
|
if (record.expiresAtMs <= Date.now()) {
|
|
@@ -9223,8 +9270,9 @@ function parseSession(value) {
|
|
|
9223
9270
|
}
|
|
9224
9271
|
return {
|
|
9225
9272
|
requesterId: record.requesterId,
|
|
9273
|
+
egressId: record.egressId,
|
|
9226
9274
|
expiresAtMs: record.expiresAtMs,
|
|
9227
|
-
|
|
9275
|
+
contextId: record.contextId
|
|
9228
9276
|
};
|
|
9229
9277
|
}
|
|
9230
9278
|
function parseLease(value) {
|
|
@@ -9253,50 +9301,127 @@ function parseLease(value) {
|
|
|
9253
9301
|
headerTransforms
|
|
9254
9302
|
};
|
|
9255
9303
|
}
|
|
9256
|
-
|
|
9257
|
-
const state = getStateAdapter();
|
|
9258
|
-
await state.connect();
|
|
9304
|
+
function createSandboxEgressRequesterToken(input) {
|
|
9259
9305
|
const ttlMs = Math.max(1, input.ttlMs ?? DEFAULT_SESSION_TTL_MS);
|
|
9260
9306
|
const now = Date.now();
|
|
9261
|
-
const
|
|
9307
|
+
const context = {
|
|
9262
9308
|
requesterId: input.requesterId,
|
|
9309
|
+
egressId: input.egressId,
|
|
9263
9310
|
expiresAtMs: now + ttlMs,
|
|
9264
|
-
|
|
9311
|
+
contextId: randomUUID3()
|
|
9265
9312
|
};
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
await state.connect();
|
|
9271
|
-
await state.delete(sessionKey2(egressId));
|
|
9313
|
+
const payload = `${SANDBOX_EGRESS_TOKEN_VERSION}.${base64Url(
|
|
9314
|
+
JSON.stringify(context)
|
|
9315
|
+
)}`;
|
|
9316
|
+
return `${payload}.${signPayload(payload)}`;
|
|
9272
9317
|
}
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9318
|
+
function parseSandboxEgressRequesterToken(token) {
|
|
9319
|
+
if (!token) {
|
|
9320
|
+
return void 0;
|
|
9321
|
+
}
|
|
9322
|
+
const parts = token.split(".");
|
|
9323
|
+
if (parts.length !== 3 || parts[0] !== SANDBOX_EGRESS_TOKEN_VERSION) {
|
|
9324
|
+
return void 0;
|
|
9325
|
+
}
|
|
9326
|
+
const encodedSession = parts[1];
|
|
9327
|
+
const signature = parts[2];
|
|
9328
|
+
if (!encodedSession || !signature) {
|
|
9329
|
+
return void 0;
|
|
9330
|
+
}
|
|
9331
|
+
const payload = `${parts[0]}.${encodedSession}`;
|
|
9332
|
+
if (!timingSafeMatch(signPayload(payload), signature)) {
|
|
9333
|
+
return void 0;
|
|
9334
|
+
}
|
|
9335
|
+
try {
|
|
9336
|
+
return parseRequesterContext(JSON.parse(fromBase64Url(encodedSession)));
|
|
9337
|
+
} catch {
|
|
9338
|
+
return void 0;
|
|
9339
|
+
}
|
|
9277
9340
|
}
|
|
9278
|
-
async function setSandboxEgressCredentialLease(
|
|
9341
|
+
async function setSandboxEgressCredentialLease(context, lease) {
|
|
9279
9342
|
const leaseExpiresAtMs = Date.parse(lease.expiresAt);
|
|
9280
9343
|
if (!Number.isFinite(leaseExpiresAtMs) || leaseExpiresAtMs <= Date.now()) {
|
|
9281
9344
|
return;
|
|
9282
9345
|
}
|
|
9283
9346
|
const ttlMs = Math.max(
|
|
9284
9347
|
1,
|
|
9285
|
-
Math.min(leaseExpiresAtMs,
|
|
9348
|
+
Math.min(leaseExpiresAtMs, context.expiresAtMs) - Date.now()
|
|
9286
9349
|
);
|
|
9287
9350
|
const state = getStateAdapter();
|
|
9288
9351
|
await state.connect();
|
|
9289
|
-
await state.set(leaseKey(
|
|
9352
|
+
await state.set(leaseKey(lease.provider, context), lease, ttlMs);
|
|
9290
9353
|
}
|
|
9291
|
-
async function getSandboxEgressCredentialLease(
|
|
9354
|
+
async function getSandboxEgressCredentialLease(provider, context) {
|
|
9292
9355
|
const state = getStateAdapter();
|
|
9293
9356
|
await state.connect();
|
|
9294
|
-
return parseLease(await state.get(leaseKey(
|
|
9357
|
+
return parseLease(await state.get(leaseKey(provider, context)));
|
|
9295
9358
|
}
|
|
9296
|
-
async function clearSandboxEgressCredentialLease(
|
|
9359
|
+
async function clearSandboxEgressCredentialLease(provider, context) {
|
|
9297
9360
|
const state = getStateAdapter();
|
|
9298
9361
|
await state.connect();
|
|
9299
|
-
await state.delete(leaseKey(
|
|
9362
|
+
await state.delete(leaseKey(provider, context));
|
|
9363
|
+
}
|
|
9364
|
+
|
|
9365
|
+
// src/chat/sandbox/egress-policy.ts
|
|
9366
|
+
function matchesSandboxEgressDomain(host, domain) {
|
|
9367
|
+
return host.toLowerCase() === domain.toLowerCase();
|
|
9368
|
+
}
|
|
9369
|
+
function manifestDomains(manifest) {
|
|
9370
|
+
const domains = /* @__PURE__ */ new Set([
|
|
9371
|
+
...manifest.credentials?.domains ?? [],
|
|
9372
|
+
...manifest.domains ?? []
|
|
9373
|
+
]);
|
|
9374
|
+
return [...domains].sort((left, right) => left.localeCompare(right));
|
|
9375
|
+
}
|
|
9376
|
+
function providerEntries() {
|
|
9377
|
+
return getPluginProviders().map((plugin) => ({
|
|
9378
|
+
provider: plugin.manifest.name,
|
|
9379
|
+
domains: manifestDomains(plugin.manifest)
|
|
9380
|
+
})).filter((entry) => entry.domains.length > 0).sort((left, right) => left.provider.localeCompare(right.provider));
|
|
9381
|
+
}
|
|
9382
|
+
function resolveSandboxEgressProviderForHost(host) {
|
|
9383
|
+
return providerEntries().find(
|
|
9384
|
+
(entry) => entry.domains.some((domain) => matchesSandboxEgressDomain(host, domain))
|
|
9385
|
+
)?.provider;
|
|
9386
|
+
}
|
|
9387
|
+
function sandboxProxyUrl(requesterToken) {
|
|
9388
|
+
const baseUrl = resolveBaseUrl();
|
|
9389
|
+
if (!baseUrl) {
|
|
9390
|
+
throw new Error(
|
|
9391
|
+
"Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
9392
|
+
);
|
|
9393
|
+
}
|
|
9394
|
+
const path11 = requesterToken ? `${SANDBOX_EGRESS_PROXY_PATH}/${requesterToken}` : SANDBOX_EGRESS_PROXY_PATH;
|
|
9395
|
+
return new URL(path11, baseUrl).toString();
|
|
9396
|
+
}
|
|
9397
|
+
function buildSandboxEgressNetworkPolicy(input) {
|
|
9398
|
+
const allow = {
|
|
9399
|
+
"*": []
|
|
9400
|
+
};
|
|
9401
|
+
const entries = providerEntries();
|
|
9402
|
+
if (entries.length === 0) {
|
|
9403
|
+
return { allow };
|
|
9404
|
+
}
|
|
9405
|
+
const forwardURL = sandboxProxyUrl(input?.requesterToken);
|
|
9406
|
+
for (const entry of entries) {
|
|
9407
|
+
for (const domain of entry.domains) {
|
|
9408
|
+
allow[domain] = [{ forwardURL }];
|
|
9409
|
+
}
|
|
9410
|
+
}
|
|
9411
|
+
return { allow };
|
|
9412
|
+
}
|
|
9413
|
+
async function resolveSandboxCommandEnvironment() {
|
|
9414
|
+
const env = {};
|
|
9415
|
+
for (const plugin of getPluginProviders().sort(
|
|
9416
|
+
(left, right) => left.manifest.name.localeCompare(right.manifest.name)
|
|
9417
|
+
)) {
|
|
9418
|
+
Object.assign(env, resolvePluginCommandEnv(plugin.manifest));
|
|
9419
|
+
const credentials = plugin.manifest.credentials;
|
|
9420
|
+
if (credentials) {
|
|
9421
|
+
env[credentials.authTokenEnv] = resolveAuthTokenPlaceholder(credentials);
|
|
9422
|
+
}
|
|
9423
|
+
}
|
|
9424
|
+
return env;
|
|
9300
9425
|
}
|
|
9301
9426
|
|
|
9302
9427
|
// src/chat/sandbox/http-error-details.ts
|
|
@@ -10601,7 +10726,6 @@ function createSandboxSessionManager(options) {
|
|
|
10601
10726
|
}
|
|
10602
10727
|
};
|
|
10603
10728
|
const buildToolExecutors = async (sandboxInstance) => {
|
|
10604
|
-
const activeSandboxId = sandboxInstance.sandboxId;
|
|
10605
10729
|
const toolkit = await withSandboxSpan(
|
|
10606
10730
|
"sandbox.bash_tool.init",
|
|
10607
10731
|
"sandbox.tool.init",
|
|
@@ -10621,35 +10745,9 @@ function createSandboxSessionManager(options) {
|
|
|
10621
10745
|
}
|
|
10622
10746
|
return {
|
|
10623
10747
|
bash: async (input) => {
|
|
10624
|
-
const commandEgressId = sandboxInstance.sandboxEgressId;
|
|
10625
10748
|
let timedOut = false;
|
|
10626
10749
|
let timeoutId;
|
|
10627
|
-
let commandFinished = false;
|
|
10628
|
-
const finishCommand = async () => {
|
|
10629
|
-
if (commandFinished) {
|
|
10630
|
-
return;
|
|
10631
|
-
}
|
|
10632
|
-
commandFinished = true;
|
|
10633
|
-
await options?.afterCommand?.(commandEgressId);
|
|
10634
|
-
await refreshNetworkPolicy(sandboxInstance);
|
|
10635
|
-
};
|
|
10636
|
-
const finishCommandBestEffort = async () => {
|
|
10637
|
-
try {
|
|
10638
|
-
await finishCommand();
|
|
10639
|
-
} catch (error) {
|
|
10640
|
-
logWarn(
|
|
10641
|
-
"sandbox_command_cleanup_failed",
|
|
10642
|
-
traceContext,
|
|
10643
|
-
{
|
|
10644
|
-
"app.sandbox.id": activeSandboxId,
|
|
10645
|
-
"error.type": error instanceof Error ? error.name : "sandbox_command_cleanup_error"
|
|
10646
|
-
},
|
|
10647
|
-
"Sandbox command cleanup failed"
|
|
10648
|
-
);
|
|
10649
|
-
}
|
|
10650
|
-
};
|
|
10651
10750
|
try {
|
|
10652
|
-
await options?.beforeCommand?.(commandEgressId);
|
|
10653
10751
|
await refreshNetworkPolicy(sandboxInstance);
|
|
10654
10752
|
const sandboxCommandEnv = await resolveCommandEnv();
|
|
10655
10753
|
const script = buildNonInteractiveShellScript(input.command, {
|
|
@@ -10671,7 +10769,6 @@ function createSandboxSessionManager(options) {
|
|
|
10671
10769
|
cwd: SANDBOX_WORKSPACE_ROOT,
|
|
10672
10770
|
...controller ? { signal: controller.signal } : {}
|
|
10673
10771
|
});
|
|
10674
|
-
await finishCommandBestEffort();
|
|
10675
10772
|
return await readCommandOutput(commandResult2);
|
|
10676
10773
|
} catch (error) {
|
|
10677
10774
|
if (timedOut) {
|
|
@@ -10692,7 +10789,6 @@ function createSandboxSessionManager(options) {
|
|
|
10692
10789
|
if (timeoutId) {
|
|
10693
10790
|
clearTimeout(timeoutId);
|
|
10694
10791
|
}
|
|
10695
|
-
await finishCommandBestEffort();
|
|
10696
10792
|
}
|
|
10697
10793
|
},
|
|
10698
10794
|
readFile: async (input) => await executeReadFile(input, {
|
|
@@ -10781,25 +10877,40 @@ function createSandboxExecutor(options) {
|
|
|
10781
10877
|
let referenceFiles = [];
|
|
10782
10878
|
const traceContext = options?.traceContext ?? {};
|
|
10783
10879
|
const credentialEgress = options?.credentialEgress;
|
|
10784
|
-
const
|
|
10785
|
-
|
|
10786
|
-
|
|
10880
|
+
const sandboxEgressTokenTtlMs = Math.max(
|
|
10881
|
+
1,
|
|
10882
|
+
options?.timeoutMs ?? 1e3 * 60 * 30
|
|
10883
|
+
);
|
|
10884
|
+
const sandboxEgressRequesterTokens = /* @__PURE__ */ new Map();
|
|
10885
|
+
const sandboxEgressRequesterTokenFor = (egressId) => {
|
|
10886
|
+
const cached = sandboxEgressRequesterTokens.get(egressId);
|
|
10887
|
+
if (cached && cached.expiresAtMs > Date.now()) {
|
|
10888
|
+
return cached.token;
|
|
10889
|
+
}
|
|
10890
|
+
if (!credentialEgress) {
|
|
10891
|
+
throw new Error("Sandbox credential egress is not configured");
|
|
10892
|
+
}
|
|
10893
|
+
const now = Date.now();
|
|
10894
|
+
const token = createSandboxEgressRequesterToken({
|
|
10787
10895
|
requesterId: credentialEgress.requesterId,
|
|
10788
|
-
|
|
10896
|
+
egressId,
|
|
10897
|
+
ttlMs: sandboxEgressTokenTtlMs
|
|
10898
|
+
});
|
|
10899
|
+
sandboxEgressRequesterTokens.set(egressId, {
|
|
10900
|
+
expiresAtMs: now + sandboxEgressTokenTtlMs,
|
|
10901
|
+
token
|
|
10789
10902
|
});
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
await clearSandboxEgressSession(egressId);
|
|
10793
|
-
} : void 0;
|
|
10903
|
+
return token;
|
|
10904
|
+
};
|
|
10794
10905
|
const sessionManager = createSandboxSessionManager({
|
|
10795
10906
|
sandboxId: options?.sandboxId,
|
|
10796
10907
|
sandboxDependencyProfileHash: options?.sandboxDependencyProfileHash,
|
|
10797
10908
|
timeoutMs: options?.timeoutMs,
|
|
10798
10909
|
traceContext,
|
|
10799
10910
|
commandEnv: credentialEgress ? async () => await resolveSandboxCommandEnvironment() : void 0,
|
|
10800
|
-
createNetworkPolicy: credentialEgress ?
|
|
10801
|
-
|
|
10802
|
-
|
|
10911
|
+
createNetworkPolicy: credentialEgress ? (egressId) => buildSandboxEgressNetworkPolicy({
|
|
10912
|
+
requesterToken: sandboxEgressRequesterTokenFor(egressId)
|
|
10913
|
+
}) : void 0,
|
|
10803
10914
|
onSandboxAcquired: async (sandbox) => {
|
|
10804
10915
|
await options?.onSandboxAcquired?.(sandbox);
|
|
10805
10916
|
}
|
|
@@ -11295,9 +11406,98 @@ async function unlinkProvider(userId, provider, userTokenStore) {
|
|
|
11295
11406
|
]);
|
|
11296
11407
|
}
|
|
11297
11408
|
|
|
11409
|
+
// src/chat/state/turn-session-store.ts
|
|
11410
|
+
import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS3 } from "chat";
|
|
11411
|
+
|
|
11412
|
+
// src/chat/state/pi-session-message-store.ts
|
|
11413
|
+
import { isDeepStrictEqual } from "util";
|
|
11414
|
+
var PI_SESSION_MESSAGE_PREFIX = "junior:pi_session_message";
|
|
11415
|
+
function piSessionMessageKey(scope, index) {
|
|
11416
|
+
return `${PI_SESSION_MESSAGE_PREFIX}:${scope.conversationId}:${scope.sessionId}:${index}`;
|
|
11417
|
+
}
|
|
11418
|
+
function parsePiMessage(value) {
|
|
11419
|
+
return isRecord(value) ? value : void 0;
|
|
11420
|
+
}
|
|
11421
|
+
function normalizeMessageCount(value) {
|
|
11422
|
+
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
11423
|
+
}
|
|
11424
|
+
function countMatchingPrefix(left, right) {
|
|
11425
|
+
const limit = Math.min(left.length, right.length);
|
|
11426
|
+
for (let index = 0; index < limit; index += 1) {
|
|
11427
|
+
if (!isDeepStrictEqual(left[index], right[index])) {
|
|
11428
|
+
return index;
|
|
11429
|
+
}
|
|
11430
|
+
}
|
|
11431
|
+
return limit;
|
|
11432
|
+
}
|
|
11433
|
+
async function loadPiSessionMessages(args) {
|
|
11434
|
+
const stateAdapter = getStateAdapter();
|
|
11435
|
+
await stateAdapter.connect();
|
|
11436
|
+
const messageCount = normalizeMessageCount(args.messageCount);
|
|
11437
|
+
if (messageCount === 0) {
|
|
11438
|
+
return [];
|
|
11439
|
+
}
|
|
11440
|
+
const values = await Promise.all(
|
|
11441
|
+
Array.from(
|
|
11442
|
+
{ length: messageCount },
|
|
11443
|
+
(_, index) => stateAdapter.get(piSessionMessageKey(args, index))
|
|
11444
|
+
)
|
|
11445
|
+
);
|
|
11446
|
+
const messages = [];
|
|
11447
|
+
for (const value of values) {
|
|
11448
|
+
const message = parsePiMessage(value);
|
|
11449
|
+
if (!message) {
|
|
11450
|
+
break;
|
|
11451
|
+
}
|
|
11452
|
+
messages.push(message);
|
|
11453
|
+
}
|
|
11454
|
+
return messages.length === messageCount ? messages : void 0;
|
|
11455
|
+
}
|
|
11456
|
+
async function loadExistingPiSessionMessages(scope, maxCount) {
|
|
11457
|
+
const count = normalizeMessageCount(maxCount);
|
|
11458
|
+
if (count === 0) {
|
|
11459
|
+
return [];
|
|
11460
|
+
}
|
|
11461
|
+
const stateAdapter = getStateAdapter();
|
|
11462
|
+
await stateAdapter.connect();
|
|
11463
|
+
const values = await Promise.all(
|
|
11464
|
+
Array.from(
|
|
11465
|
+
{ length: count },
|
|
11466
|
+
(_, index) => stateAdapter.get(piSessionMessageKey(scope, index))
|
|
11467
|
+
)
|
|
11468
|
+
);
|
|
11469
|
+
const messages = [];
|
|
11470
|
+
for (const value of values) {
|
|
11471
|
+
const message = parsePiMessage(value);
|
|
11472
|
+
if (!message) {
|
|
11473
|
+
break;
|
|
11474
|
+
}
|
|
11475
|
+
messages.push(message);
|
|
11476
|
+
}
|
|
11477
|
+
return messages;
|
|
11478
|
+
}
|
|
11479
|
+
async function commitPiSessionMessages(args) {
|
|
11480
|
+
const stateAdapter = getStateAdapter();
|
|
11481
|
+
await stateAdapter.connect();
|
|
11482
|
+
const existingMessages = await loadExistingPiSessionMessages(
|
|
11483
|
+
{ conversationId: args.conversationId, sessionId: args.sessionId },
|
|
11484
|
+
args.messages.length
|
|
11485
|
+
);
|
|
11486
|
+
const writeFromIndex = countMatchingPrefix(existingMessages, args.messages);
|
|
11487
|
+
await Promise.all(
|
|
11488
|
+
args.messages.slice(writeFromIndex).map(
|
|
11489
|
+
(message, offset) => stateAdapter.set(
|
|
11490
|
+
piSessionMessageKey(args, writeFromIndex + offset),
|
|
11491
|
+
message,
|
|
11492
|
+
args.ttlMs
|
|
11493
|
+
)
|
|
11494
|
+
)
|
|
11495
|
+
);
|
|
11496
|
+
}
|
|
11497
|
+
|
|
11298
11498
|
// src/chat/state/turn-session-store.ts
|
|
11299
11499
|
var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
|
|
11300
|
-
var AGENT_TURN_SESSION_TTL_MS =
|
|
11500
|
+
var AGENT_TURN_SESSION_TTL_MS = THREAD_STATE_TTL_MS3;
|
|
11301
11501
|
function agentTurnSessionKey(conversationId, sessionId) {
|
|
11302
11502
|
return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
|
|
11303
11503
|
}
|
|
@@ -11323,41 +11523,55 @@ function parseAgentTurnUsage(value) {
|
|
|
11323
11523
|
}
|
|
11324
11524
|
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
11325
11525
|
}
|
|
11326
|
-
function
|
|
11526
|
+
function parseStoredRecord(value) {
|
|
11527
|
+
if (isRecord(value)) {
|
|
11528
|
+
return value;
|
|
11529
|
+
}
|
|
11327
11530
|
if (typeof value !== "string") {
|
|
11328
11531
|
return void 0;
|
|
11329
11532
|
}
|
|
11330
11533
|
try {
|
|
11331
11534
|
const parsed = JSON.parse(value);
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11535
|
+
return isRecord(parsed) ? parsed : void 0;
|
|
11536
|
+
} catch {
|
|
11537
|
+
return void 0;
|
|
11538
|
+
}
|
|
11539
|
+
}
|
|
11540
|
+
function parseAgentTurnSessionRecord(value) {
|
|
11541
|
+
const parsed = parseStoredRecord(value);
|
|
11542
|
+
if (!parsed) {
|
|
11543
|
+
return void 0;
|
|
11544
|
+
}
|
|
11545
|
+
const status = parsed.state;
|
|
11546
|
+
if (status !== "running" && status !== "awaiting_resume" && status !== "completed" && status !== "failed" && status !== "superseded") {
|
|
11547
|
+
return void 0;
|
|
11548
|
+
}
|
|
11549
|
+
const conversationId = parsed.conversationId;
|
|
11550
|
+
const sessionId = parsed.sessionId;
|
|
11551
|
+
const sliceId = parsed.sliceId;
|
|
11552
|
+
const checkpointVersion = parsed.checkpointVersion;
|
|
11553
|
+
const updatedAtMs = parsed.updatedAtMs;
|
|
11554
|
+
const cumulativeDurationMs = toFiniteNonNegativeNumber(
|
|
11555
|
+
parsed.cumulativeDurationMs
|
|
11556
|
+
);
|
|
11557
|
+
const cumulativeUsage = parseAgentTurnUsage(parsed.cumulativeUsage);
|
|
11558
|
+
if (typeof conversationId !== "string" || typeof sessionId !== "string" || typeof sliceId !== "number" || typeof checkpointVersion !== "number" || typeof updatedAtMs !== "number") {
|
|
11559
|
+
return void 0;
|
|
11560
|
+
}
|
|
11561
|
+
const legacyPiMessages = Array.isArray(parsed.piMessages) ? parsed.piMessages : [];
|
|
11562
|
+
const messageCount = toFiniteNonNegativeNumber(parsed.messageCount) ?? legacyPiMessages.length;
|
|
11563
|
+
return {
|
|
11564
|
+
legacyPiMessages,
|
|
11565
|
+
record: {
|
|
11352
11566
|
checkpointVersion,
|
|
11353
11567
|
conversationId,
|
|
11354
11568
|
sessionId,
|
|
11355
11569
|
sliceId,
|
|
11356
11570
|
state: status,
|
|
11357
11571
|
updatedAtMs,
|
|
11572
|
+
messageCount,
|
|
11358
11573
|
...cumulativeDurationMs !== void 0 ? { cumulativeDurationMs } : {},
|
|
11359
11574
|
...cumulativeUsage ? { cumulativeUsage } : {},
|
|
11360
|
-
piMessages: Array.isArray(parsed.piMessages) ? parsed.piMessages : [],
|
|
11361
11575
|
...Array.isArray(parsed.loadedSkillNames) ? {
|
|
11362
11576
|
loadedSkillNames: parsed.loadedSkillNames.filter(
|
|
11363
11577
|
(value2) => typeof value2 === "string"
|
|
@@ -11366,10 +11580,20 @@ function parseAgentTurnSessionCheckpoint(value) {
|
|
|
11366
11580
|
...parsed.resumeReason === "timeout" || parsed.resumeReason === "auth" ? { resumeReason: parsed.resumeReason } : {},
|
|
11367
11581
|
...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
|
|
11368
11582
|
...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {}
|
|
11369
|
-
}
|
|
11370
|
-
}
|
|
11371
|
-
|
|
11583
|
+
}
|
|
11584
|
+
};
|
|
11585
|
+
}
|
|
11586
|
+
function materializePiMessages(legacyPiMessages, messageCount, sessionMessages) {
|
|
11587
|
+
if (messageCount === 0) {
|
|
11588
|
+
return [];
|
|
11372
11589
|
}
|
|
11590
|
+
if (sessionMessages) {
|
|
11591
|
+
return sessionMessages;
|
|
11592
|
+
}
|
|
11593
|
+
if (legacyPiMessages.length >= messageCount) {
|
|
11594
|
+
return legacyPiMessages.slice(0, messageCount);
|
|
11595
|
+
}
|
|
11596
|
+
return void 0;
|
|
11373
11597
|
}
|
|
11374
11598
|
async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
|
|
11375
11599
|
const stateAdapter = getStateAdapter();
|
|
@@ -11377,23 +11601,51 @@ async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
|
|
|
11377
11601
|
const value = await stateAdapter.get(
|
|
11378
11602
|
agentTurnSessionKey(conversationId, sessionId)
|
|
11379
11603
|
);
|
|
11380
|
-
|
|
11604
|
+
const parsed = parseAgentTurnSessionRecord(value);
|
|
11605
|
+
if (!parsed) {
|
|
11606
|
+
return void 0;
|
|
11607
|
+
}
|
|
11608
|
+
const sessionMessages = await loadPiSessionMessages({
|
|
11609
|
+
conversationId,
|
|
11610
|
+
sessionId,
|
|
11611
|
+
messageCount: parsed.record.messageCount
|
|
11612
|
+
});
|
|
11613
|
+
const piMessages = materializePiMessages(
|
|
11614
|
+
parsed.legacyPiMessages,
|
|
11615
|
+
parsed.record.messageCount,
|
|
11616
|
+
sessionMessages
|
|
11617
|
+
);
|
|
11618
|
+
if (!piMessages) {
|
|
11619
|
+
return void 0;
|
|
11620
|
+
}
|
|
11621
|
+
return {
|
|
11622
|
+
...parsed.record,
|
|
11623
|
+
piMessages
|
|
11624
|
+
};
|
|
11381
11625
|
}
|
|
11382
11626
|
async function upsertAgentTurnSessionCheckpoint(args) {
|
|
11383
11627
|
const stateAdapter = getStateAdapter();
|
|
11384
11628
|
await stateAdapter.connect();
|
|
11385
|
-
const
|
|
11386
|
-
args.conversationId,
|
|
11387
|
-
args.sessionId
|
|
11629
|
+
const existingValue = await stateAdapter.get(
|
|
11630
|
+
agentTurnSessionKey(args.conversationId, args.sessionId)
|
|
11388
11631
|
);
|
|
11632
|
+
const existingRecord = parseAgentTurnSessionRecord(existingValue);
|
|
11633
|
+
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
11634
|
+
await commitPiSessionMessages({
|
|
11635
|
+
conversationId: args.conversationId,
|
|
11636
|
+
sessionId: args.sessionId,
|
|
11637
|
+
messages: args.piMessages,
|
|
11638
|
+
ttlMs
|
|
11639
|
+
});
|
|
11640
|
+
const storedMessageCount = args.piMessages.length;
|
|
11389
11641
|
const checkpoint = {
|
|
11390
|
-
checkpointVersion: (
|
|
11642
|
+
checkpointVersion: (existingRecord?.record.checkpointVersion ?? 0) + 1,
|
|
11391
11643
|
conversationId: args.conversationId,
|
|
11392
11644
|
sessionId: args.sessionId,
|
|
11393
11645
|
sliceId: args.sliceId,
|
|
11394
11646
|
state: args.state,
|
|
11395
11647
|
updatedAtMs: Date.now(),
|
|
11396
|
-
|
|
11648
|
+
messageCount: storedMessageCount,
|
|
11397
11649
|
...typeof args.cumulativeDurationMs === "number" && Number.isFinite(args.cumulativeDurationMs) ? {
|
|
11398
11650
|
cumulativeDurationMs: Math.max(
|
|
11399
11651
|
0,
|
|
@@ -11410,13 +11662,15 @@ async function upsertAgentTurnSessionCheckpoint(args) {
|
|
|
11410
11662
|
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
11411
11663
|
...typeof args.resumedFromSliceId === "number" ? { resumedFromSliceId: args.resumedFromSliceId } : {}
|
|
11412
11664
|
};
|
|
11413
|
-
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
11414
11665
|
await stateAdapter.set(
|
|
11415
11666
|
agentTurnSessionKey(args.conversationId, args.sessionId),
|
|
11416
|
-
|
|
11667
|
+
checkpoint,
|
|
11417
11668
|
ttlMs
|
|
11418
11669
|
);
|
|
11419
|
-
return
|
|
11670
|
+
return {
|
|
11671
|
+
...checkpoint,
|
|
11672
|
+
piMessages: [...args.piMessages]
|
|
11673
|
+
};
|
|
11420
11674
|
}
|
|
11421
11675
|
async function supersedeAgentTurnSessionCheckpoint(args) {
|
|
11422
11676
|
const existing = await getAgentTurnSessionCheckpoint(
|
|
@@ -11969,7 +12223,6 @@ function buildBriefPostCanvasReply(artifactStatePatch) {
|
|
|
11969
12223
|
function buildTurnResult(input) {
|
|
11970
12224
|
const {
|
|
11971
12225
|
newMessages,
|
|
11972
|
-
piMessages,
|
|
11973
12226
|
userInput,
|
|
11974
12227
|
replyFiles,
|
|
11975
12228
|
artifactStatePatch,
|
|
@@ -12073,7 +12326,6 @@ function buildTurnResult(input) {
|
|
|
12073
12326
|
text: resolvedText,
|
|
12074
12327
|
files: replyFiles.length > 0 ? replyFiles : void 0,
|
|
12075
12328
|
artifactStatePatch: Object.keys(artifactStatePatch).length > 0 ? artifactStatePatch : void 0,
|
|
12076
|
-
piMessages,
|
|
12077
12329
|
deliveryPlan,
|
|
12078
12330
|
deliveryMode,
|
|
12079
12331
|
sandboxId,
|
|
@@ -12756,69 +13008,6 @@ function buildUserTurnInput(args) {
|
|
|
12756
13008
|
}
|
|
12757
13009
|
return { routerBlocks, userContentParts };
|
|
12758
13010
|
}
|
|
12759
|
-
function refreshCheckpointTurnContext(messages, turnContextPrompt) {
|
|
12760
|
-
const marker = getTurnContextMarker(turnContextPrompt);
|
|
12761
|
-
for (let index = 0; index < messages.length; index += 1) {
|
|
12762
|
-
const content = getUserMessageContent(messages[index]);
|
|
12763
|
-
if (!content) {
|
|
12764
|
-
continue;
|
|
12765
|
-
}
|
|
12766
|
-
const contextIndex = content.findIndex(
|
|
12767
|
-
(part) => isTurnContextPart(part, marker)
|
|
12768
|
-
);
|
|
12769
|
-
if (contextIndex < 0) {
|
|
12770
|
-
continue;
|
|
12771
|
-
}
|
|
12772
|
-
const updatedMessages = [...messages];
|
|
12773
|
-
const updatedContent = [...content];
|
|
12774
|
-
updatedContent[contextIndex] = {
|
|
12775
|
-
...updatedContent[contextIndex],
|
|
12776
|
-
text: turnContextPrompt
|
|
12777
|
-
};
|
|
12778
|
-
updatedMessages[index] = {
|
|
12779
|
-
...messages[index],
|
|
12780
|
-
content: updatedContent
|
|
12781
|
-
};
|
|
12782
|
-
return updatedMessages;
|
|
12783
|
-
}
|
|
12784
|
-
return [
|
|
12785
|
-
...messages,
|
|
12786
|
-
{
|
|
12787
|
-
role: "user",
|
|
12788
|
-
content: [{ type: "text", text: turnContextPrompt }],
|
|
12789
|
-
timestamp: Date.now()
|
|
12790
|
-
}
|
|
12791
|
-
];
|
|
12792
|
-
}
|
|
12793
|
-
function stripTurnContextFromMessages(messages, turnContextPrompt) {
|
|
12794
|
-
const marker = getTurnContextMarker(turnContextPrompt);
|
|
12795
|
-
return messages.flatMap((message) => {
|
|
12796
|
-
const content = getUserMessageContent(message);
|
|
12797
|
-
if (!content) {
|
|
12798
|
-
return [message];
|
|
12799
|
-
}
|
|
12800
|
-
const strippedContent = content.filter(
|
|
12801
|
-
(part) => !isTurnContextPart(part, marker)
|
|
12802
|
-
);
|
|
12803
|
-
if (strippedContent.length === content.length) {
|
|
12804
|
-
return [message];
|
|
12805
|
-
}
|
|
12806
|
-
if (strippedContent.length === 0) {
|
|
12807
|
-
return [];
|
|
12808
|
-
}
|
|
12809
|
-
return [{ ...message, content: strippedContent }];
|
|
12810
|
-
});
|
|
12811
|
-
}
|
|
12812
|
-
function getTurnContextMarker(turnContextPrompt) {
|
|
12813
|
-
return turnContextPrompt.split("\n", 1)[0];
|
|
12814
|
-
}
|
|
12815
|
-
function getUserMessageContent(message) {
|
|
12816
|
-
const record = message;
|
|
12817
|
-
return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
|
|
12818
|
-
}
|
|
12819
|
-
function isTurnContextPart(part, marker) {
|
|
12820
|
-
return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(marker);
|
|
12821
|
-
}
|
|
12822
13011
|
async function generateAssistantReply(messageText, context = {}) {
|
|
12823
13012
|
const replyStartedAtMs = Date.now();
|
|
12824
13013
|
let timeoutResumeConversationId;
|
|
@@ -13331,7 +13520,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
13331
13520
|
beforeMessageCount = agent.state.messages.length;
|
|
13332
13521
|
try {
|
|
13333
13522
|
if (resumedFromCheckpoint) {
|
|
13334
|
-
agent.state.messages =
|
|
13523
|
+
agent.state.messages = refreshRuntimeTurnContext(
|
|
13335
13524
|
existingCheckpoint.piMessages,
|
|
13336
13525
|
turnContextPrompt
|
|
13337
13526
|
);
|
|
@@ -13444,10 +13633,6 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
13444
13633
|
}
|
|
13445
13634
|
return buildTurnResult({
|
|
13446
13635
|
newMessages,
|
|
13447
|
-
piMessages: stripTurnContextFromMessages(
|
|
13448
|
-
agent.state.messages,
|
|
13449
|
-
turnContextPrompt
|
|
13450
|
-
),
|
|
13451
13636
|
userInput,
|
|
13452
13637
|
replyFiles,
|
|
13453
13638
|
artifactStatePatch,
|
|
@@ -14909,7 +15094,7 @@ function completeAuthPauseTurn(args) {
|
|
|
14909
15094
|
skippedReason: void 0
|
|
14910
15095
|
}
|
|
14911
15096
|
);
|
|
14912
|
-
|
|
15097
|
+
markTurnClosed({
|
|
14913
15098
|
conversation: args.conversation,
|
|
14914
15099
|
nowMs: Date.now(),
|
|
14915
15100
|
sessionId: args.sessionId,
|
|
@@ -14927,7 +15112,7 @@ async function persistAuthPauseTurnState(args) {
|
|
|
14927
15112
|
}
|
|
14928
15113
|
|
|
14929
15114
|
// src/chat/services/timeout-resume.ts
|
|
14930
|
-
import { createHmac, timingSafeEqual } from "crypto";
|
|
15115
|
+
import { createHmac as createHmac2, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
14931
15116
|
var TURN_TIMEOUT_RESUME_PATH = "/api/internal/turn-resume";
|
|
14932
15117
|
var TURN_TIMEOUT_RESUME_SIGNATURE_VERSION = "v1";
|
|
14933
15118
|
var TURN_TIMEOUT_RESUME_MAX_SKEW_MS = 5 * 60 * 1e3;
|
|
@@ -14962,16 +15147,16 @@ function buildSignedPayload(timestamp, body) {
|
|
|
14962
15147
|
return `${timestamp}:${body}`;
|
|
14963
15148
|
}
|
|
14964
15149
|
function signTurnTimeoutResumeBody(secret, timestamp, body) {
|
|
14965
|
-
const digest =
|
|
15150
|
+
const digest = createHmac2("sha256", secret).update(buildSignedPayload(timestamp, body)).digest("hex");
|
|
14966
15151
|
return `${TURN_TIMEOUT_RESUME_SIGNATURE_VERSION}=${digest}`;
|
|
14967
15152
|
}
|
|
14968
|
-
function
|
|
15153
|
+
function timingSafeMatch2(expected, actual) {
|
|
14969
15154
|
const expectedBuffer = Buffer.from(expected);
|
|
14970
15155
|
const actualBuffer = Buffer.from(actual);
|
|
14971
15156
|
if (expectedBuffer.length !== actualBuffer.length) {
|
|
14972
15157
|
return false;
|
|
14973
15158
|
}
|
|
14974
|
-
return
|
|
15159
|
+
return timingSafeEqual2(expectedBuffer, actualBuffer);
|
|
14975
15160
|
}
|
|
14976
15161
|
function parseTurnTimeoutResumeRequest(value) {
|
|
14977
15162
|
if (!value || typeof value !== "object") {
|
|
@@ -15034,7 +15219,7 @@ async function verifyTurnTimeoutResumeRequest(request) {
|
|
|
15034
15219
|
}
|
|
15035
15220
|
const body = await request.text();
|
|
15036
15221
|
const expectedSignature = signTurnTimeoutResumeBody(secret, timestamp, body);
|
|
15037
|
-
if (!
|
|
15222
|
+
if (!timingSafeMatch2(expectedSignature, signature)) {
|
|
15038
15223
|
return void 0;
|
|
15039
15224
|
}
|
|
15040
15225
|
try {
|
|
@@ -15111,9 +15296,9 @@ async function persistCompletedReplyState(channelId, threadTs, sessionId, reply)
|
|
|
15111
15296
|
const conversation = coerceThreadConversationState(currentState);
|
|
15112
15297
|
const artifacts = coerceThreadArtifactsState(currentState);
|
|
15113
15298
|
const nextArtifacts = reply.artifactStatePatch ? mergeArtifactsState(artifacts, reply.artifactStatePatch) : void 0;
|
|
15114
|
-
const
|
|
15299
|
+
const userMessage = getTurnUserMessage(conversation, sessionId);
|
|
15115
15300
|
clearPendingAuth(conversation, sessionId);
|
|
15116
|
-
markConversationMessage(conversation,
|
|
15301
|
+
markConversationMessage(conversation, userMessage?.id, {
|
|
15117
15302
|
replied: true,
|
|
15118
15303
|
skippedReason: void 0
|
|
15119
15304
|
});
|
|
@@ -15130,9 +15315,6 @@ async function persistCompletedReplyState(channelId, threadTs, sessionId, reply)
|
|
|
15130
15315
|
replied: true
|
|
15131
15316
|
}
|
|
15132
15317
|
});
|
|
15133
|
-
if (reply.piMessages) {
|
|
15134
|
-
conversation.piMessages = reply.piMessages;
|
|
15135
|
-
}
|
|
15136
15318
|
markTurnCompleted({
|
|
15137
15319
|
conversation,
|
|
15138
15320
|
nowMs: Date.now(),
|
|
@@ -15554,9 +15736,6 @@ async function persistCompletedOAuthReplyState(args) {
|
|
|
15554
15736
|
replied: true
|
|
15555
15737
|
}
|
|
15556
15738
|
});
|
|
15557
|
-
if (args.reply.piMessages) {
|
|
15558
|
-
conversation.piMessages = args.reply.piMessages;
|
|
15559
|
-
}
|
|
15560
15739
|
markTurnCompleted({
|
|
15561
15740
|
conversation,
|
|
15562
15741
|
nowMs: Date.now(),
|
|
@@ -16069,10 +16248,32 @@ function egressAttributes(input) {
|
|
|
16069
16248
|
...input.status ? { "http.response.status_code": input.status } : {}
|
|
16070
16249
|
};
|
|
16071
16250
|
}
|
|
16251
|
+
function requesterTokenFromRequest(request) {
|
|
16252
|
+
const pathname = new URL(request.url).pathname;
|
|
16253
|
+
const prefix = `${SANDBOX_EGRESS_PROXY_PATH}/`;
|
|
16254
|
+
if (!pathname.startsWith(prefix)) {
|
|
16255
|
+
return void 0;
|
|
16256
|
+
}
|
|
16257
|
+
const token = pathname.slice(prefix.length).split("/")[0];
|
|
16258
|
+
if (!token) {
|
|
16259
|
+
return void 0;
|
|
16260
|
+
}
|
|
16261
|
+
try {
|
|
16262
|
+
return decodeURIComponent(token);
|
|
16263
|
+
} catch {
|
|
16264
|
+
return void 0;
|
|
16265
|
+
}
|
|
16266
|
+
}
|
|
16267
|
+
function redactedProxyPath(pathname) {
|
|
16268
|
+
if (pathname.startsWith(`${SANDBOX_EGRESS_PROXY_PATH}/`)) {
|
|
16269
|
+
return `${SANDBOX_EGRESS_PROXY_PATH}/<token>`;
|
|
16270
|
+
}
|
|
16271
|
+
return pathname;
|
|
16272
|
+
}
|
|
16072
16273
|
function routingAttributes(request, upstreamUrl) {
|
|
16073
16274
|
const proxyUrl = new URL(request.url);
|
|
16074
16275
|
const attributes = {
|
|
16075
|
-
"app.sandbox.egress.proxy_path": proxyUrl.pathname
|
|
16276
|
+
"app.sandbox.egress.proxy_path": redactedProxyPath(proxyUrl.pathname)
|
|
16076
16277
|
};
|
|
16077
16278
|
if (upstreamUrl) {
|
|
16078
16279
|
attributes["app.sandbox.egress.upstream_path"] = upstreamUrl.pathname;
|
|
@@ -16125,22 +16326,23 @@ function normalizePort(value) {
|
|
|
16125
16326
|
function sandboxIdFromPayload(payload) {
|
|
16126
16327
|
return typeof payload.sandbox_id === "string" ? payload.sandbox_id : void 0;
|
|
16127
16328
|
}
|
|
16329
|
+
function normalizedForwardedPath(path11) {
|
|
16330
|
+
if (!path11.startsWith("/") || path11.startsWith("//") || path11.includes("#") || /[\r\n]/.test(path11)) {
|
|
16331
|
+
return { ok: false, error: "Invalid forwarded path" };
|
|
16332
|
+
}
|
|
16333
|
+
try {
|
|
16334
|
+
const url = new URL(path11, "https://sandbox-forwarded.local");
|
|
16335
|
+
return { ok: true, path: `${url.pathname}${url.search}` };
|
|
16336
|
+
} catch {
|
|
16337
|
+
return { ok: false, error: "Invalid forwarded path" };
|
|
16338
|
+
}
|
|
16339
|
+
}
|
|
16128
16340
|
function upstreamPath(request) {
|
|
16129
16341
|
const forwardedPath = request.headers.get(FORWARDED_PATH_HEADER);
|
|
16130
|
-
if (forwardedPath?.trim()) {
|
|
16131
|
-
|
|
16132
|
-
if (!path11.startsWith("/") || path11.startsWith("//") || path11.includes("#") || /[\r\n]/.test(path11)) {
|
|
16133
|
-
return { ok: false, error: "Invalid forwarded path" };
|
|
16134
|
-
}
|
|
16135
|
-
try {
|
|
16136
|
-
const url2 = new URL(path11, "https://sandbox-forwarded.local");
|
|
16137
|
-
return { ok: true, path: `${url2.pathname}${url2.search}` };
|
|
16138
|
-
} catch {
|
|
16139
|
-
return { ok: false, error: "Invalid forwarded path" };
|
|
16140
|
-
}
|
|
16342
|
+
if (!forwardedPath?.trim()) {
|
|
16343
|
+
return { ok: false, error: "Missing forwarded path" };
|
|
16141
16344
|
}
|
|
16142
|
-
|
|
16143
|
-
return { ok: true, path: `${url.pathname}${url.search}` };
|
|
16345
|
+
return normalizedForwardedPath(forwardedPath.trim());
|
|
16144
16346
|
}
|
|
16145
16347
|
function buildUpstreamUrl(request) {
|
|
16146
16348
|
const forwardedHost = request.headers.get(FORWARDED_HOST_HEADER);
|
|
@@ -16212,18 +16414,14 @@ function responseHeaders(upstream) {
|
|
|
16212
16414
|
});
|
|
16213
16415
|
return headers;
|
|
16214
16416
|
}
|
|
16215
|
-
async function credentialLease(
|
|
16216
|
-
const cached = await getSandboxEgressCredentialLease(
|
|
16217
|
-
egressId,
|
|
16218
|
-
provider,
|
|
16219
|
-
session
|
|
16220
|
-
);
|
|
16417
|
+
async function credentialLease(provider, context) {
|
|
16418
|
+
const cached = await getSandboxEgressCredentialLease(provider, context);
|
|
16221
16419
|
if (cached) {
|
|
16222
16420
|
return cached;
|
|
16223
16421
|
}
|
|
16224
16422
|
const lease = await issueProviderCredentialLease({
|
|
16225
16423
|
provider,
|
|
16226
|
-
requesterId:
|
|
16424
|
+
requesterId: context.requesterId,
|
|
16227
16425
|
reason: `sandbox-egress:${provider}`
|
|
16228
16426
|
});
|
|
16229
16427
|
const headerTransforms = lease.headerTransforms ?? [];
|
|
@@ -16237,7 +16435,7 @@ async function credentialLease(egressId, provider, session) {
|
|
|
16237
16435
|
expiresAt: lease.expiresAt,
|
|
16238
16436
|
headerTransforms
|
|
16239
16437
|
};
|
|
16240
|
-
await setSandboxEgressCredentialLease(
|
|
16438
|
+
await setSandboxEgressCredentialLease(context, cachedLease);
|
|
16241
16439
|
return cachedLease;
|
|
16242
16440
|
}
|
|
16243
16441
|
function hasTransformForHost(lease, host) {
|
|
@@ -16278,7 +16476,7 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
16278
16476
|
{},
|
|
16279
16477
|
{
|
|
16280
16478
|
"http.request.method": request.method,
|
|
16281
|
-
"url.path": new URL(request.url).pathname
|
|
16479
|
+
"url.path": redactedProxyPath(new URL(request.url).pathname)
|
|
16282
16480
|
},
|
|
16283
16481
|
"Sandbox egress OIDC payload did not include a VM session id"
|
|
16284
16482
|
);
|
|
@@ -16296,7 +16494,7 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
16296
16494
|
...egressAttributes({
|
|
16297
16495
|
egressId: activeEgressId,
|
|
16298
16496
|
method: request.method,
|
|
16299
|
-
path: new URL(request.url).pathname,
|
|
16497
|
+
path: redactedProxyPath(new URL(request.url).pathname),
|
|
16300
16498
|
status: 400
|
|
16301
16499
|
}),
|
|
16302
16500
|
...routingAttributes(request)
|
|
@@ -16325,10 +16523,12 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
16325
16523
|
);
|
|
16326
16524
|
return jsonError("No provider owns forwarded host", 403);
|
|
16327
16525
|
}
|
|
16328
|
-
const
|
|
16329
|
-
|
|
16526
|
+
const requesterContext = parseSandboxEgressRequesterToken(
|
|
16527
|
+
requesterTokenFromRequest(request)
|
|
16528
|
+
);
|
|
16529
|
+
if (!requesterContext || requesterContext.egressId !== activeEgressId) {
|
|
16330
16530
|
logWarn(
|
|
16331
|
-
"
|
|
16531
|
+
"sandbox_egress_requester_context_unauthorized",
|
|
16332
16532
|
{},
|
|
16333
16533
|
{
|
|
16334
16534
|
...egressAttributes({
|
|
@@ -16341,13 +16541,13 @@ async function proxySandboxEgressRequest(request, deps = {}) {
|
|
|
16341
16541
|
}),
|
|
16342
16542
|
...routingAttributes(request, upstreamUrl)
|
|
16343
16543
|
},
|
|
16344
|
-
"Sandbox egress
|
|
16544
|
+
"Sandbox egress request did not include a valid requester context for the VM session"
|
|
16345
16545
|
);
|
|
16346
|
-
return jsonError("Sandbox egress
|
|
16546
|
+
return jsonError("Sandbox egress requester context is not authorized", 403);
|
|
16347
16547
|
}
|
|
16348
16548
|
let lease;
|
|
16349
16549
|
try {
|
|
16350
|
-
lease = await credentialLease(
|
|
16550
|
+
lease = await credentialLease(provider, requesterContext);
|
|
16351
16551
|
} catch (error) {
|
|
16352
16552
|
if (error instanceof CredentialUnavailableError) {
|
|
16353
16553
|
logWarn(
|
|
@@ -16451,7 +16651,7 @@ ${error.message}`,
|
|
|
16451
16651
|
},
|
|
16452
16652
|
"Sandbox egress upstream auth rejected"
|
|
16453
16653
|
);
|
|
16454
|
-
await clearSandboxEgressCredentialLease(
|
|
16654
|
+
await clearSandboxEgressCredentialLease(provider, requesterContext);
|
|
16455
16655
|
}
|
|
16456
16656
|
return new Response(upstream.body, {
|
|
16457
16657
|
status: upstream.status,
|
|
@@ -16502,9 +16702,6 @@ async function persistCompletedReplyState2(args) {
|
|
|
16502
16702
|
replied: true
|
|
16503
16703
|
}
|
|
16504
16704
|
});
|
|
16505
|
-
if (args.reply.piMessages) {
|
|
16506
|
-
conversation.piMessages = args.reply.piMessages;
|
|
16507
|
-
}
|
|
16508
16705
|
markTurnCompleted({
|
|
16509
16706
|
conversation,
|
|
16510
16707
|
nowMs: Date.now(),
|
|
@@ -18208,6 +18405,31 @@ function getCurrentTurnCanvasUrl(args) {
|
|
|
18208
18405
|
function buildCanvasRecoveryReply(canvasUrl) {
|
|
18209
18406
|
return `I created the canvas, but the turn was interrupted before I could finish the thread reply: ${canvasUrl}`;
|
|
18210
18407
|
}
|
|
18408
|
+
async function loadPiMessagesForTurn(args) {
|
|
18409
|
+
const fallback = args.fallback.length > 0 ? [...args.fallback] : void 0;
|
|
18410
|
+
if (!args.conversationId) {
|
|
18411
|
+
return fallback;
|
|
18412
|
+
}
|
|
18413
|
+
if (args.activeTurnId) {
|
|
18414
|
+
const checkpoint2 = await getAgentTurnSessionCheckpoint(
|
|
18415
|
+
args.conversationId,
|
|
18416
|
+
args.activeTurnId
|
|
18417
|
+
);
|
|
18418
|
+
if (checkpoint2?.piMessages.length) {
|
|
18419
|
+
return stripRuntimeTurnContext(
|
|
18420
|
+
trimTrailingAssistantMessages(checkpoint2.piMessages)
|
|
18421
|
+
);
|
|
18422
|
+
}
|
|
18423
|
+
}
|
|
18424
|
+
if (!args.lastSessionId) {
|
|
18425
|
+
return fallback;
|
|
18426
|
+
}
|
|
18427
|
+
const checkpoint = await getAgentTurnSessionCheckpoint(
|
|
18428
|
+
args.conversationId,
|
|
18429
|
+
args.lastSessionId
|
|
18430
|
+
);
|
|
18431
|
+
return checkpoint?.state === "completed" && checkpoint.piMessages.length > 0 ? stripRuntimeTurnContext(checkpoint.piMessages) : fallback;
|
|
18432
|
+
}
|
|
18211
18433
|
function createReplyToThread(deps) {
|
|
18212
18434
|
return async function replyToThread(thread, message, options = {}) {
|
|
18213
18435
|
if (message.author.isMe) {
|
|
@@ -18360,6 +18582,7 @@ function createReplyToThread(deps) {
|
|
|
18360
18582
|
return;
|
|
18361
18583
|
}
|
|
18362
18584
|
}
|
|
18585
|
+
const lastSessionIdForHistory = preparedState.conversation.processing.lastSessionId;
|
|
18363
18586
|
const configReply = await maybeApplyProviderDefaultConfigRequest({
|
|
18364
18587
|
channelConfiguration: preparedState.channelConfiguration,
|
|
18365
18588
|
requesterId: message.author.userId,
|
|
@@ -18435,6 +18658,12 @@ function createReplyToThread(deps) {
|
|
|
18435
18658
|
}
|
|
18436
18659
|
);
|
|
18437
18660
|
const omittedImageAttachmentCount = !isVisionEnabled() && hasPotentialImageAttachment(message.attachments) ? countPotentialImageAttachments(message.attachments) : 0;
|
|
18661
|
+
const piMessages = await loadPiMessagesForTurn({
|
|
18662
|
+
conversationId,
|
|
18663
|
+
activeTurnId,
|
|
18664
|
+
lastSessionId: lastSessionIdForHistory,
|
|
18665
|
+
fallback: preparedState.conversation.piMessages
|
|
18666
|
+
});
|
|
18438
18667
|
const status = createSlackAdapterAssistantStatusSession({
|
|
18439
18668
|
channelId: assistantThreadContext?.channelId,
|
|
18440
18669
|
threadTs: assistantThreadContext?.threadTs,
|
|
@@ -18486,7 +18715,7 @@ function createReplyToThread(deps) {
|
|
|
18486
18715
|
},
|
|
18487
18716
|
conversationContext: preparedState.routingContext ?? preparedState.conversationContext,
|
|
18488
18717
|
artifactState: preparedState.artifacts,
|
|
18489
|
-
piMessages
|
|
18718
|
+
piMessages,
|
|
18490
18719
|
pendingAuth: preparedState.conversation.processing.pendingAuth,
|
|
18491
18720
|
configuration: preparedState.configuration,
|
|
18492
18721
|
channelConfiguration: preparedState.channelConfiguration,
|
|
@@ -18569,9 +18798,6 @@ function createReplyToThread(deps) {
|
|
|
18569
18798
|
replied: true
|
|
18570
18799
|
}
|
|
18571
18800
|
});
|
|
18572
|
-
if (reply.piMessages) {
|
|
18573
|
-
preparedState.conversation.piMessages = reply.piMessages;
|
|
18574
|
-
}
|
|
18575
18801
|
const artifactStatePatch = reply.artifactStatePatch ? { ...reply.artifactStatePatch } : {};
|
|
18576
18802
|
const reactionPerformed = reply.diagnostics.toolCalls.includes(
|
|
18577
18803
|
"slackMessageAddReaction"
|
|
@@ -18649,6 +18875,7 @@ function createReplyToThread(deps) {
|
|
|
18649
18875
|
markTurnCompleted({
|
|
18650
18876
|
conversation: preparedState.conversation,
|
|
18651
18877
|
nowMs: Date.now(),
|
|
18878
|
+
sessionId: turnId,
|
|
18652
18879
|
updateConversationStats
|
|
18653
18880
|
});
|
|
18654
18881
|
await persistThreadState(thread, {
|
|
@@ -18774,9 +19001,10 @@ function createReplyToThread(deps) {
|
|
|
18774
19001
|
replied: true
|
|
18775
19002
|
}
|
|
18776
19003
|
});
|
|
18777
|
-
|
|
19004
|
+
markTurnClosed({
|
|
18778
19005
|
conversation: preparedState.conversation,
|
|
18779
19006
|
nowMs: Date.now(),
|
|
19007
|
+
sessionId: turnId,
|
|
18780
19008
|
updateConversationStats
|
|
18781
19009
|
});
|
|
18782
19010
|
await persistThreadState(thread, {
|