@agentknight/sdk 0.9.38 → 0.9.40
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/LICENSE +201 -201
- package/dist/chunks/{acp-adapter-DZ3EE36H.js → acp-adapter-TVAW7EHK.js} +3 -3
- package/dist/chunks/{chunk-6YF3VZLR.js → chunk-6RWWNR37.js} +1 -1
- package/dist/chunks/{chunk-6GVMVE5N.js → chunk-AJ4YL22H.js} +4 -1
- package/dist/chunks/{chunk-4FC2WG64.js → chunk-CVWUKZX7.js} +1 -1
- package/dist/chunks/{chunk-34DUD2Y4.js → chunk-D6HWUCVM.js} +5 -4
- package/dist/chunks/{chunk-GNQC2IS2.js → chunk-OV77TRIH.js} +352 -236
- package/dist/chunks/{chunk-YSJRQ35E.js → chunk-Q3TQPONK.js} +2 -2
- package/dist/chunks/{chunk-SREMK7S4.js → chunk-SDAEMVSD.js} +1 -1
- package/dist/chunks/{chunk-UIM3KQRC.js → chunk-ULJMR33R.js} +10 -8
- package/dist/chunks/{copilot-adapter-USX4UEWH.js → copilot-adapter-KH6NXHGK.js} +141 -62
- package/dist/chunks/{managed-agent-adapter-EMBDKITT.js → managed-agent-adapter-F7U26CH2.js} +21 -6
- package/dist/chunks/{pi-adapter-KT5M7Z6R.js → pi-adapter-G4GQ5ULV.js} +3 -3
- package/dist/index.d.ts +82 -9
- package/dist/index.js +1103 -356
- package/dist/runtime.js +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
PeerChatRequestSchema,
|
|
5
5
|
PeerChatResultSchema,
|
|
6
6
|
validateDataDirectory
|
|
7
|
-
} from "./chunks/chunk-
|
|
7
|
+
} from "./chunks/chunk-CVWUKZX7.js";
|
|
8
8
|
import {
|
|
9
9
|
AgentSelectionSchema,
|
|
10
10
|
assertSelectionSnapshotFits,
|
|
11
11
|
supportsAgentSelection
|
|
12
|
-
} from "./chunks/chunk-
|
|
12
|
+
} from "./chunks/chunk-6RWWNR37.js";
|
|
13
13
|
import {
|
|
14
14
|
ConversationNormalizer,
|
|
15
15
|
LiveTimelineProjection,
|
|
@@ -31,25 +31,26 @@ import {
|
|
|
31
31
|
resolveGitProjectIdentity,
|
|
32
32
|
sessionRestoreFailure,
|
|
33
33
|
validateOperationResponse
|
|
34
|
-
} from "./chunks/chunk-
|
|
34
|
+
} from "./chunks/chunk-ULJMR33R.js";
|
|
35
35
|
import {
|
|
36
36
|
GROUP_CHAT_MAX_TURNS,
|
|
37
37
|
GroupChatError
|
|
38
|
-
} from "./chunks/chunk-
|
|
38
|
+
} from "./chunks/chunk-Q3TQPONK.js";
|
|
39
39
|
import {
|
|
40
40
|
assistantPreview,
|
|
41
41
|
isContentfulAssistant,
|
|
42
42
|
userPreview
|
|
43
|
-
} from "./chunks/chunk-
|
|
43
|
+
} from "./chunks/chunk-D6HWUCVM.js";
|
|
44
44
|
import {
|
|
45
45
|
AgentModelListingError,
|
|
46
46
|
AgentSessionRenameError,
|
|
47
47
|
SessionOperationError,
|
|
48
48
|
SessionOperations,
|
|
49
|
+
TimelinePageBudgetError,
|
|
49
50
|
UnsupportedAgentOperationError,
|
|
50
51
|
captureResearchAccess,
|
|
51
52
|
captureWorkbenchAccess
|
|
52
|
-
} from "./chunks/chunk-
|
|
53
|
+
} from "./chunks/chunk-AJ4YL22H.js";
|
|
53
54
|
import {
|
|
54
55
|
A2aRelayRequestSchema,
|
|
55
56
|
A2aRelayResponseSchema,
|
|
@@ -95,6 +96,7 @@ import {
|
|
|
95
96
|
SECURE_MESSAGE_MAX_CIPHERTEXT_LENGTH,
|
|
96
97
|
SECURE_SOCKET_IO_NAMESPACE,
|
|
97
98
|
SESSION_ASSISTANT_PREVIEW_CAPABILITY,
|
|
99
|
+
SESSION_AVATAR_PERSONA_CAPABILITY,
|
|
98
100
|
SESSION_COMMUNICATION_CAPABILITY,
|
|
99
101
|
SESSION_DELETE_CAPABILITY,
|
|
100
102
|
SESSION_DIRECTORY_LIST_OPERATION,
|
|
@@ -107,8 +109,11 @@ import {
|
|
|
107
109
|
SESSION_FILE_MAX_IMAGE_EDGE,
|
|
108
110
|
SESSION_FILE_MAX_IMAGE_PIXELS,
|
|
109
111
|
SESSION_FILE_MAX_ORIGINAL_BYTES,
|
|
112
|
+
SESSION_FILE_MAX_VIDEO_PIXELS,
|
|
110
113
|
SESSION_FILE_READ_OPERATION,
|
|
114
|
+
SESSION_FILE_READ_V2_VERSION,
|
|
111
115
|
SESSION_FILE_READ_VERSION,
|
|
116
|
+
SESSION_FILE_V2_MAX_ORIGINAL_BYTES,
|
|
112
117
|
SESSION_GIT_DIFF_GZIP_MIN_BYTES,
|
|
113
118
|
SESSION_GIT_DIFF_GZIP_VERSION,
|
|
114
119
|
SESSION_GIT_DIFF_MAX_FILES,
|
|
@@ -116,8 +121,12 @@ import {
|
|
|
116
121
|
SESSION_GIT_DIFF_MAX_RESPONSE_BYTES,
|
|
117
122
|
SESSION_GIT_DIFF_OPERATION,
|
|
118
123
|
SESSION_GIT_DIFF_VERSION,
|
|
124
|
+
SESSION_IMAGE_API_PATH,
|
|
125
|
+
SESSION_IMAGE_MAX_BYTES,
|
|
126
|
+
SESSION_IMAGE_MAX_PIXELS,
|
|
119
127
|
SESSION_LIST_UPDATES_CAPABILITY,
|
|
120
128
|
SESSION_OPTIONS_PARTIAL_CAPABILITY,
|
|
129
|
+
SESSION_OPTIONS_PROGRESSIVE_CAPABILITY,
|
|
121
130
|
SESSION_PROJECT_IDENTITY_OPERATION,
|
|
122
131
|
SESSION_PROJECT_IDENTITY_VERSION,
|
|
123
132
|
SESSION_PROMPT_SUBMISSION_TIME_CAPABILITY,
|
|
@@ -144,9 +153,14 @@ import {
|
|
|
144
153
|
SessionDirectoryListResultDataSchema,
|
|
145
154
|
SessionFileReadDataSchema,
|
|
146
155
|
SessionFileReadResultDataSchema,
|
|
156
|
+
SessionFileReadV2ResultDataSchema,
|
|
147
157
|
SessionGitDiffDataSchema,
|
|
148
158
|
SessionGitDiffGzipResultDataSchema,
|
|
149
159
|
SessionGitDiffResultDataSchema,
|
|
160
|
+
SessionImageCommitResultSchema,
|
|
161
|
+
SessionImageScopeSchema,
|
|
162
|
+
SessionImageUploadRequestSchema,
|
|
163
|
+
SessionImageUploadResultSchema,
|
|
150
164
|
SessionListRequestSchema,
|
|
151
165
|
SessionListResultSchema,
|
|
152
166
|
SessionOptionsRequestSchema,
|
|
@@ -164,6 +178,8 @@ import {
|
|
|
164
178
|
SessionTimelinePageSchema,
|
|
165
179
|
SessionWatchRequestSchema,
|
|
166
180
|
SessionsSnapshotSchema,
|
|
181
|
+
TIMELINE_IMAGE_BLOB_OPERATION,
|
|
182
|
+
TIMELINE_IMAGE_BLOB_VERSION,
|
|
167
183
|
TIMELINE_IMAGE_CHUNK_BYTES,
|
|
168
184
|
TIMELINE_IMAGE_MAX_BYTES,
|
|
169
185
|
TIMELINE_IMAGE_OPERATION,
|
|
@@ -172,6 +188,8 @@ import {
|
|
|
172
188
|
TIMELINE_SYNC_OPERATION,
|
|
173
189
|
TIMELINE_SYNC_VERSION,
|
|
174
190
|
TRANSPORT_V2,
|
|
191
|
+
TimelineImageBlobRequestSchema,
|
|
192
|
+
TimelineImageBlobResultSchema,
|
|
175
193
|
TimelineImageChunkSchema,
|
|
176
194
|
TimelineImageRequestSchema,
|
|
177
195
|
TimelineSyncRequestSchema,
|
|
@@ -208,12 +226,12 @@ import {
|
|
|
208
226
|
sessionCommunicationError,
|
|
209
227
|
sessionCommunicationOperationCapability,
|
|
210
228
|
workbenchCapability
|
|
211
|
-
} from "./chunks/chunk-
|
|
229
|
+
} from "./chunks/chunk-OV77TRIH.js";
|
|
212
230
|
|
|
213
231
|
// src/initialize.ts
|
|
214
|
-
import { createHash as
|
|
232
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
215
233
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
216
|
-
import { z as
|
|
234
|
+
import { z as z12 } from "zod";
|
|
217
235
|
|
|
218
236
|
// src/coding-agent-factory.ts
|
|
219
237
|
import { resolve } from "node:path";
|
|
@@ -286,7 +304,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
286
304
|
switch (engine.kind) {
|
|
287
305
|
case "managed": {
|
|
288
306
|
if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
|
|
289
|
-
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-
|
|
307
|
+
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-F7U26CH2.js");
|
|
290
308
|
return new ManagedCodingAgentAdapter({
|
|
291
309
|
dataDirectory: config.dataDirectory,
|
|
292
310
|
owner,
|
|
@@ -325,7 +343,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
325
343
|
});
|
|
326
344
|
}
|
|
327
345
|
case "copilot": {
|
|
328
|
-
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-
|
|
346
|
+
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-KH6NXHGK.js");
|
|
329
347
|
if (owner !== void 0) {
|
|
330
348
|
return new GithubCopilotAdapter(void 0, void 0, void 0, {
|
|
331
349
|
dataDirectory: config.dataDirectory,
|
|
@@ -345,13 +363,13 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
345
363
|
}
|
|
346
364
|
case "pi": {
|
|
347
365
|
assertPiRuntimeSupported();
|
|
348
|
-
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-
|
|
366
|
+
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-G4GQ5ULV.js");
|
|
349
367
|
return new PiCodingAgentAdapter(
|
|
350
368
|
engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
|
|
351
369
|
);
|
|
352
370
|
}
|
|
353
371
|
case "acp": {
|
|
354
|
-
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-
|
|
372
|
+
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-TVAW7EHK.js");
|
|
355
373
|
return new AcpCodingAgentAdapter({
|
|
356
374
|
command: engine.command,
|
|
357
375
|
args: engine.args,
|
|
@@ -367,6 +385,18 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
367
385
|
}
|
|
368
386
|
}
|
|
369
387
|
|
|
388
|
+
// src/config.ts
|
|
389
|
+
import { z } from "zod";
|
|
390
|
+
var SessionImagesConfigSchema = z.object({
|
|
391
|
+
allowUnencryptedStorage: z.literal(true),
|
|
392
|
+
storageAccount: z.string().regex(/^[a-z0-9]{3,24}$/u),
|
|
393
|
+
storageContainer: z.string().min(3).max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u)
|
|
394
|
+
}).strict();
|
|
395
|
+
var SessionImageRelayUrlSchema = z.url().refine((value) => {
|
|
396
|
+
const url = new URL(value);
|
|
397
|
+
return /^https:\/\/[^/?#\\@\s\x00-\x1f\x7f]+\/?$/iu.test(value) && url.protocol === "https:" && url.username === "" && url.password === "" && url.pathname === "/" && url.search === "" && url.hash === "";
|
|
398
|
+
}, "Session images require an HTTPS Relay origin without a path, credentials, query or fragment").transform((value) => new URL(value).origin);
|
|
399
|
+
|
|
370
400
|
// src/content-encryption.ts
|
|
371
401
|
import {
|
|
372
402
|
createCipheriv,
|
|
@@ -383,22 +413,22 @@ import {
|
|
|
383
413
|
} from "node:fs";
|
|
384
414
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
385
415
|
import { join as join2 } from "node:path";
|
|
386
|
-
import { z } from "zod";
|
|
416
|
+
import { z as z2 } from "zod";
|
|
387
417
|
var KEY_BYTES = 32;
|
|
388
418
|
var NONCE_BYTES = 12;
|
|
389
419
|
var AUTH_TAG_BYTES = 16;
|
|
390
420
|
var DEFAULT_REPLAY_WINDOW_MS = 10 * 60 * 1e3;
|
|
391
421
|
var MAX_REPLAY_ENTRIES = 1e4;
|
|
392
422
|
var CONTENT_KEY_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
|
|
393
|
-
var ContentKeyFileSchema =
|
|
394
|
-
schemaVersion:
|
|
395
|
-
key:
|
|
423
|
+
var ContentKeyFileSchema = z2.object({
|
|
424
|
+
schemaVersion: z2.literal(1),
|
|
425
|
+
key: z2.string().regex(CONTENT_KEY_PATTERN)
|
|
396
426
|
}).strict();
|
|
397
|
-
var ReplayJournalEntrySchema =
|
|
398
|
-
schemaVersion:
|
|
399
|
-
keyId:
|
|
400
|
-
messageId:
|
|
401
|
-
sentAt:
|
|
427
|
+
var ReplayJournalEntrySchema = z2.object({
|
|
428
|
+
schemaVersion: z2.literal(1),
|
|
429
|
+
keyId: z2.string().regex(/^[A-Za-z0-9_-]{22}$/u),
|
|
430
|
+
messageId: z2.string().trim().min(1).max(256),
|
|
431
|
+
sentAt: z2.number().int().nonnegative()
|
|
402
432
|
}).strict();
|
|
403
433
|
var REPLAY_JOURNAL_COMPACT_EVERY = 256;
|
|
404
434
|
var REPLAY_JOURNAL_MAX_BYTES = 1024 * 1024;
|
|
@@ -709,13 +739,13 @@ var ContentCipher = class {
|
|
|
709
739
|
};
|
|
710
740
|
|
|
711
741
|
// src/device-identity.ts
|
|
712
|
-
import { z as
|
|
713
|
-
var DeviceIdentitySchema =
|
|
714
|
-
schemaVersion:
|
|
715
|
-
machineId:
|
|
716
|
-
stableDeviceId:
|
|
717
|
-
machineName:
|
|
718
|
-
createdAt:
|
|
742
|
+
import { z as z3 } from "zod";
|
|
743
|
+
var DeviceIdentitySchema = z3.object({
|
|
744
|
+
schemaVersion: z3.literal(2),
|
|
745
|
+
machineId: z3.uuid(),
|
|
746
|
+
stableDeviceId: z3.uuid(),
|
|
747
|
+
machineName: z3.string().trim().min(1).max(120),
|
|
748
|
+
createdAt: z3.iso.datetime({ offset: true })
|
|
719
749
|
});
|
|
720
750
|
|
|
721
751
|
// src/pairing.ts
|
|
@@ -812,7 +842,7 @@ import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypt
|
|
|
812
842
|
import { lstat as lstat3, realpath as realpath3, stat as stat4 } from "node:fs/promises";
|
|
813
843
|
import { isAbsolute as isAbsolute4, join as join5 } from "node:path";
|
|
814
844
|
import { gunzipSync } from "node:zlib";
|
|
815
|
-
import { z as
|
|
845
|
+
import { z as z5 } from "zod";
|
|
816
846
|
|
|
817
847
|
// src/session-directory.ts
|
|
818
848
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -967,6 +997,152 @@ import { basename, extname, isAbsolute as isAbsolute3, join as join4 } from "nod
|
|
|
967
997
|
import { promisify } from "node:util";
|
|
968
998
|
import { gzip } from "node:zlib";
|
|
969
999
|
import sharp from "sharp";
|
|
1000
|
+
|
|
1001
|
+
// src/session-video.ts
|
|
1002
|
+
function requireVideo(valid) {
|
|
1003
|
+
if (!valid) throw new Error("Unsupported video container");
|
|
1004
|
+
}
|
|
1005
|
+
function one(elements, id) {
|
|
1006
|
+
const matches = elements.filter((element) => element.id === id);
|
|
1007
|
+
const match = matches[0];
|
|
1008
|
+
requireVideo(matches.length === 1 && match !== void 0);
|
|
1009
|
+
return match;
|
|
1010
|
+
}
|
|
1011
|
+
function validateSessionVideo(bytes, mediaType) {
|
|
1012
|
+
requireVideo(bytes.length > 0 && bytes.length <= SESSION_FILE_V2_MAX_ORIGINAL_BYTES);
|
|
1013
|
+
if (mediaType === "video/mp4") validateMp4(bytes);
|
|
1014
|
+
else validateWebm(bytes);
|
|
1015
|
+
}
|
|
1016
|
+
function validateMp4(bytes) {
|
|
1017
|
+
const boxes = (start, end) => {
|
|
1018
|
+
const result = [];
|
|
1019
|
+
while (start < end) {
|
|
1020
|
+
requireVideo(end - start >= 8);
|
|
1021
|
+
let size = bytes.readUInt32BE(start);
|
|
1022
|
+
const id = bytes.toString("latin1", start + 4, start + 8);
|
|
1023
|
+
let header = 8;
|
|
1024
|
+
if (size === 1) {
|
|
1025
|
+
requireVideo(end - start >= 16 && bytes.readUInt32BE(start + 8) === 0);
|
|
1026
|
+
size = bytes.readUInt32BE(start + 12);
|
|
1027
|
+
header = 16;
|
|
1028
|
+
}
|
|
1029
|
+
if (size === 0) {
|
|
1030
|
+
requireVideo(id === "mdat");
|
|
1031
|
+
size = end - start;
|
|
1032
|
+
}
|
|
1033
|
+
requireVideo(size >= header && size <= end - start);
|
|
1034
|
+
result.push({ id, start: start + header, end: start + size });
|
|
1035
|
+
start += size;
|
|
1036
|
+
}
|
|
1037
|
+
return result;
|
|
1038
|
+
};
|
|
1039
|
+
const children = (element) => boxes(element.start, element.end);
|
|
1040
|
+
const top = boxes(0, bytes.length);
|
|
1041
|
+
requireVideo(top[0]?.id === "ftyp" && top.every((box) => ["ftyp", "moov", "mdat", "free", "skip", "wide"].includes(box.id)));
|
|
1042
|
+
const ftyp = one(top, "ftyp");
|
|
1043
|
+
requireVideo(ftyp.end - ftyp.start >= 8 && (ftyp.end - ftyp.start) % 4 === 0);
|
|
1044
|
+
const brands = [bytes.toString("latin1", ftyp.start, ftyp.start + 4)];
|
|
1045
|
+
for (let offset = ftyp.start + 8; offset < ftyp.end; offset += 4) brands.push(bytes.toString("latin1", offset, offset + 4));
|
|
1046
|
+
requireVideo(brands.some((brand) => ["isom", "iso2", "mp41", "mp42", "avc1"].includes(brand)));
|
|
1047
|
+
requireVideo(top.some((box) => box.id === "mdat" && box.end > box.start));
|
|
1048
|
+
const movie = children(one(top, "moov"));
|
|
1049
|
+
requireVideo(!movie.some((box) => ["rmra", "mvex"].includes(box.id)));
|
|
1050
|
+
let videoTracks = 0;
|
|
1051
|
+
for (const track of movie.filter((box) => box.id === "trak")) {
|
|
1052
|
+
const media = children(one(children(track), "mdia"));
|
|
1053
|
+
const handler = one(media, "hdlr");
|
|
1054
|
+
requireVideo(handler.end - handler.start >= 12);
|
|
1055
|
+
const type = bytes.toString("latin1", handler.start + 8, handler.start + 12);
|
|
1056
|
+
requireVideo(type === "vide" || type === "soun");
|
|
1057
|
+
const info = children(one(media, "minf"));
|
|
1058
|
+
const dref = one(children(one(info, "dinf")), "dref");
|
|
1059
|
+
requireVideo(dref.end - dref.start >= 8 && bytes.readUInt32BE(dref.start) === 0 && bytes.readUInt32BE(dref.start + 4) === 1);
|
|
1060
|
+
const references = boxes(dref.start + 8, dref.end);
|
|
1061
|
+
const reference = one(references, "url ");
|
|
1062
|
+
requireVideo(references.length === 1 && reference.end - reference.start === 4 && bytes.readUInt32BE(reference.start) === 1);
|
|
1063
|
+
const descriptions = one(children(one(info, "stbl")), "stsd");
|
|
1064
|
+
requireVideo(descriptions.end - descriptions.start >= 8 && bytes.readUInt32BE(descriptions.start) === 0 && bytes.readUInt32BE(descriptions.start + 4) === 1);
|
|
1065
|
+
const entries = boxes(descriptions.start + 8, descriptions.end);
|
|
1066
|
+
const entry = entries[0];
|
|
1067
|
+
requireVideo(entries.length === 1 && entry !== void 0 && entry.end - entry.start >= 8 && bytes.readUInt16BE(entry.start + 6) === 1);
|
|
1068
|
+
if (type === "vide") {
|
|
1069
|
+
requireVideo(["avc1", "avc3"].includes(entry.id) && entry.end - entry.start >= 78);
|
|
1070
|
+
const width = bytes.readUInt16BE(entry.start + 24);
|
|
1071
|
+
const height = bytes.readUInt16BE(entry.start + 26);
|
|
1072
|
+
requireVideo(width > 0 && height > 0 && width * height <= SESSION_FILE_MAX_VIDEO_PIXELS);
|
|
1073
|
+
const config = one(boxes(entry.start + 78, entry.end), "avcC");
|
|
1074
|
+
requireVideo(config.end - config.start >= 7 && bytes[config.start] === 1);
|
|
1075
|
+
videoTracks++;
|
|
1076
|
+
} else requireVideo(entry.id === "mp4a" && entry.end - entry.start >= 28);
|
|
1077
|
+
}
|
|
1078
|
+
requireVideo(videoTracks === 1);
|
|
1079
|
+
}
|
|
1080
|
+
function validateWebm(bytes) {
|
|
1081
|
+
const vint = (offset, end, id) => {
|
|
1082
|
+
requireVideo(offset < end);
|
|
1083
|
+
const first = bytes[offset];
|
|
1084
|
+
requireVideo(first !== void 0 && first !== 0);
|
|
1085
|
+
let length = 1;
|
|
1086
|
+
while ((first & 128 >> length - 1) === 0) length++;
|
|
1087
|
+
requireVideo(length <= (id ? 4 : 8) && length <= end - offset);
|
|
1088
|
+
let value = id ? first : first & 255 >> length;
|
|
1089
|
+
let unknown = !id && value === 255 >> length;
|
|
1090
|
+
for (let index = 1; index < length; index++) {
|
|
1091
|
+
const byte = bytes[offset + index];
|
|
1092
|
+
requireVideo(byte !== void 0);
|
|
1093
|
+
value = value * 256 + byte;
|
|
1094
|
+
unknown = unknown && byte === 255;
|
|
1095
|
+
}
|
|
1096
|
+
requireVideo(unknown || Number.isSafeInteger(value));
|
|
1097
|
+
return { value, next: offset + length, unknown };
|
|
1098
|
+
};
|
|
1099
|
+
const elements = (start, end) => {
|
|
1100
|
+
const result = [];
|
|
1101
|
+
while (start < end) {
|
|
1102
|
+
const id = vint(start, end, true);
|
|
1103
|
+
const size = vint(id.next, end, false);
|
|
1104
|
+
const tag = id.value.toString(16);
|
|
1105
|
+
requireVideo(!size.unknown || tag === "18538067");
|
|
1106
|
+
const length = size.unknown ? end - size.next : size.value;
|
|
1107
|
+
requireVideo(length <= end - size.next);
|
|
1108
|
+
result.push({ id: tag, start: size.next, end: size.next + length });
|
|
1109
|
+
start = size.next + length;
|
|
1110
|
+
}
|
|
1111
|
+
return result;
|
|
1112
|
+
};
|
|
1113
|
+
const text = (element) => bytes.toString("latin1", element.start, element.end);
|
|
1114
|
+
const uint = (element) => {
|
|
1115
|
+
requireVideo(element.end - element.start >= 1 && element.end - element.start <= 4);
|
|
1116
|
+
return bytes.readUIntBE(element.start, element.end - element.start);
|
|
1117
|
+
};
|
|
1118
|
+
const top = elements(0, bytes.length);
|
|
1119
|
+
requireVideo(top.length === 2 && top[0]?.id === "1a45dfa3");
|
|
1120
|
+
const header = one(top, "1a45dfa3");
|
|
1121
|
+
requireVideo(text(one(elements(header.start, header.end), "4282")) === "webm");
|
|
1122
|
+
const segment = one(top, "18538067");
|
|
1123
|
+
const contents = elements(segment.start, segment.end);
|
|
1124
|
+
requireVideo(contents.some((element) => element.id === "1f43b675" && element.end > element.start));
|
|
1125
|
+
const tracks = one(contents, "1654ae6b");
|
|
1126
|
+
let videoTracks = 0;
|
|
1127
|
+
for (const track of elements(tracks.start, tracks.end).filter((element) => element.id === "ae")) {
|
|
1128
|
+
const fields = elements(track.start, track.end);
|
|
1129
|
+
const type = uint(one(fields, "83"));
|
|
1130
|
+
const codec = text(one(fields, "86"));
|
|
1131
|
+
requireVideo(!fields.some((field2) => field2.id === "6d80" || field2.id === "6fab"));
|
|
1132
|
+
if (type === 1) {
|
|
1133
|
+
requireVideo(codec === "V_VP8" || codec === "V_VP9");
|
|
1134
|
+
const video = one(fields, "e0");
|
|
1135
|
+
const dimensions = elements(video.start, video.end);
|
|
1136
|
+
const width = uint(one(dimensions, "b0"));
|
|
1137
|
+
const height = uint(one(dimensions, "ba"));
|
|
1138
|
+
requireVideo(width > 0 && height > 0 && width * height <= SESSION_FILE_MAX_VIDEO_PIXELS);
|
|
1139
|
+
videoTracks++;
|
|
1140
|
+
} else requireVideo(type === 2 && (codec === "A_VORBIS" || codec === "A_OPUS"));
|
|
1141
|
+
}
|
|
1142
|
+
requireVideo(videoTracks === 1);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// src/session-file.ts
|
|
970
1146
|
var SESSION_FILE_CONCURRENCY = 4;
|
|
971
1147
|
var SESSION_FILE_TIMEOUT_MS = 5e3;
|
|
972
1148
|
var compress = promisify(gzip);
|
|
@@ -1204,10 +1380,12 @@ var SessionFileReader = class {
|
|
|
1204
1380
|
}
|
|
1205
1381
|
metadata;
|
|
1206
1382
|
#active = 0;
|
|
1207
|
-
async read(sessionId, path, rootRevision) {
|
|
1383
|
+
async read(sessionId, path, rootRevision, version = SESSION_FILE_READ_VERSION) {
|
|
1384
|
+
if (version !== SESSION_FILE_READ_VERSION && version !== SESSION_FILE_READ_V2_VERSION) fail2("UNSUPPORTED_OPERATION_VERSION");
|
|
1208
1385
|
if (!SessionFileReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
|
|
1209
|
-
|
|
1210
|
-
|
|
1386
|
+
const schema = version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema;
|
|
1387
|
+
return schema.parse(await this.#run(
|
|
1388
|
+
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, false, version)
|
|
1211
1389
|
));
|
|
1212
1390
|
}
|
|
1213
1391
|
async readResource(sessionId, path, rootRevision) {
|
|
@@ -1241,7 +1419,7 @@ var SessionFileReader = class {
|
|
|
1241
1419
|
clearTimeout(timer);
|
|
1242
1420
|
}
|
|
1243
1421
|
}
|
|
1244
|
-
async #read(sessionId, path, rootRevision, checkDeadline, resource) {
|
|
1422
|
+
async #read(sessionId, path, rootRevision, checkDeadline, resource, version = SESSION_FILE_READ_VERSION) {
|
|
1245
1423
|
const metadata = await this.metadata(sessionId);
|
|
1246
1424
|
checkDeadline();
|
|
1247
1425
|
if (metadata === void 0) fail2("SESSION_NOT_FOUND");
|
|
@@ -1283,11 +1461,12 @@ var SessionFileReader = class {
|
|
|
1283
1461
|
return file;
|
|
1284
1462
|
};
|
|
1285
1463
|
const before = await inspectPath();
|
|
1286
|
-
const maxOriginalBytes = resource ? SESSION_RESOURCE_MAX_ORIGINAL_BYTES : SESSION_FILE_MAX_ORIGINAL_BYTES;
|
|
1464
|
+
const maxOriginalBytes = resource ? SESSION_RESOURCE_MAX_ORIGINAL_BYTES : version === SESSION_FILE_READ_V2_VERSION ? SESSION_FILE_V2_MAX_ORIGINAL_BYTES : SESSION_FILE_MAX_ORIGINAL_BYTES;
|
|
1287
1465
|
if (before.size > BigInt(maxOriginalBytes)) fail2("FILE_TOO_LARGE");
|
|
1288
1466
|
const extension = extname(path).toLowerCase();
|
|
1289
1467
|
const image = [".png", ".jpg", ".jpeg", ".webp"].includes(extension);
|
|
1290
|
-
const
|
|
1468
|
+
const video = !resource && version === SESSION_FILE_READ_V2_VERSION && [".mp4", ".webm"].includes(extension);
|
|
1469
|
+
const mediaType = resource ? resourceMediaType(extension) : image ? "image/webp" : video ? extension === ".mp4" ? "video/mp4" : "video/webm" : textMediaType(path);
|
|
1291
1470
|
const handle = await open(target, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
|
|
1292
1471
|
try {
|
|
1293
1472
|
const opened = await handle.stat({ bigint: true });
|
|
@@ -1313,6 +1492,13 @@ var SessionFileReader = class {
|
|
|
1313
1492
|
await validateRawImage(bytes, extension, checkDeadline);
|
|
1314
1493
|
canonical = bytes;
|
|
1315
1494
|
} else canonical = await imagePreview(bytes, extension, checkDeadline);
|
|
1495
|
+
} else if (mediaType === "video/mp4" || mediaType === "video/webm") {
|
|
1496
|
+
try {
|
|
1497
|
+
validateSessionVideo(bytes, mediaType);
|
|
1498
|
+
} catch (error) {
|
|
1499
|
+
throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
|
|
1500
|
+
}
|
|
1501
|
+
canonical = bytes;
|
|
1316
1502
|
} else {
|
|
1317
1503
|
let text;
|
|
1318
1504
|
try {
|
|
@@ -1345,7 +1531,7 @@ var SessionFileReader = class {
|
|
|
1345
1531
|
throw new SessionFileReadError("FILE_CHANGED", { cause: error });
|
|
1346
1532
|
}
|
|
1347
1533
|
checkDeadline();
|
|
1348
|
-
return (resource ? SessionResourceReadResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1534
|
+
return (resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1349
1535
|
path,
|
|
1350
1536
|
rootRevision,
|
|
1351
1537
|
mediaType,
|
|
@@ -1784,20 +1970,20 @@ function sensitiveSegments(segments) {
|
|
|
1784
1970
|
}
|
|
1785
1971
|
return false;
|
|
1786
1972
|
}
|
|
1787
|
-
var pathSchema =
|
|
1788
|
-
var listSchema =
|
|
1973
|
+
var pathSchema = z5.string().max(4096).default("");
|
|
1974
|
+
var listSchema = z5.object({
|
|
1789
1975
|
path: pathSchema,
|
|
1790
|
-
cursor:
|
|
1976
|
+
cursor: z5.string().uuid().optional()
|
|
1791
1977
|
}).strict();
|
|
1792
|
-
var readSchema =
|
|
1793
|
-
path:
|
|
1794
|
-
startLine:
|
|
1795
|
-
lineCount:
|
|
1978
|
+
var readSchema = z5.object({
|
|
1979
|
+
path: z5.string().min(1).max(4096),
|
|
1980
|
+
startLine: z5.number().int().min(1).max(500001).default(1),
|
|
1981
|
+
lineCount: z5.number().int().min(1).max(200).default(200)
|
|
1796
1982
|
}).strict();
|
|
1797
|
-
var searchSchema =
|
|
1983
|
+
var searchSchema = z5.object({
|
|
1798
1984
|
path: pathSchema,
|
|
1799
|
-
query:
|
|
1800
|
-
cursor:
|
|
1985
|
+
query: z5.string().min(1).max(256),
|
|
1986
|
+
cursor: z5.string().uuid().optional()
|
|
1801
1987
|
}).strict();
|
|
1802
1988
|
var ResearchTools = class {
|
|
1803
1989
|
constructor(parentSessionId, root, sourceMetadata, privateDirectories = [], onError, captureSourceAccess) {
|
|
@@ -2057,7 +2243,7 @@ var ResearchTools = class {
|
|
|
2057
2243
|
const definition = (name, description, schema, run) => ({
|
|
2058
2244
|
name,
|
|
2059
2245
|
description,
|
|
2060
|
-
parameters:
|
|
2246
|
+
parameters: z5.toJSONSchema(schema),
|
|
2061
2247
|
// Only these host-owned read tools bypass prompts; their path policy remains mandatory.
|
|
2062
2248
|
skipPermission: true,
|
|
2063
2249
|
defer: "never",
|
|
@@ -2070,7 +2256,7 @@ var ResearchTools = class {
|
|
|
2070
2256
|
return { textResultForLlm: JSON.stringify(result), resultType: "success" };
|
|
2071
2257
|
} catch (error) {
|
|
2072
2258
|
const knownCode = workbenchErrorDetails(error).code;
|
|
2073
|
-
const code = (error instanceof ResearchError || error instanceof SessionFileReadError) && knownCode !== "UNCLASSIFIED" ? knownCode : error instanceof
|
|
2259
|
+
const code = (error instanceof ResearchError || error instanceof SessionFileReadError) && knownCode !== "UNCLASSIFIED" ? knownCode : error instanceof z5.ZodError ? "RESEARCH_INVALID_ARGUMENT" : "RESEARCH_TOOL_FAILED";
|
|
2074
2260
|
this.onError?.(new ResearchError(code, "The research tool failed", { cause: error }), { stage: name });
|
|
2075
2261
|
return { textResultForLlm: JSON.stringify({ error: code }), resultType: "failure" };
|
|
2076
2262
|
}
|
|
@@ -2141,11 +2327,21 @@ function researchAwareCodingAgentAdapter(inner, research) {
|
|
|
2141
2327
|
},
|
|
2142
2328
|
readSessionTimelinePage: (id, options) => route(id, () => research.timeline(id, options), () => inner.readSessionTimelinePage(id, options)),
|
|
2143
2329
|
watchSession: (id, watched) => route(id, () => research.watch(id, watched), () => inner.watchSession(id, watched)),
|
|
2144
|
-
sendPrompt: (id, prompt, mode, attachments, persist, files, operationId) => route(id, () => {
|
|
2330
|
+
sendPrompt: (id, prompt, mode, attachments, persist, files, operationId, avatarPersona) => route(id, () => {
|
|
2331
|
+
if (avatarPersona !== void 0) return deny();
|
|
2145
2332
|
if (attachments?.length || files?.length) return deny();
|
|
2146
2333
|
if (operationId === void 0) throw new SessionOperationError("OPERATION_ID_REQUIRED", "A stable operation ID is required");
|
|
2147
2334
|
return research.sendPrompt(id, prompt, mode, operationId);
|
|
2148
|
-
}, () => inner.sendPrompt(
|
|
2335
|
+
}, () => inner.sendPrompt(
|
|
2336
|
+
id,
|
|
2337
|
+
prompt,
|
|
2338
|
+
mode,
|
|
2339
|
+
attachments,
|
|
2340
|
+
persist,
|
|
2341
|
+
files,
|
|
2342
|
+
operationId,
|
|
2343
|
+
...avatarPersona === void 0 ? [] : [avatarPersona]
|
|
2344
|
+
)),
|
|
2149
2345
|
abortSession: (id) => route(id, () => research.abort(id), () => inner.abortSession(id)),
|
|
2150
2346
|
getSessionConfiguration: (id) => route(id, () => research.configuration(id), () => inner.getSessionConfiguration(id)),
|
|
2151
2347
|
getSessionCapabilities: (id) => id !== void 0 && owned(id) ? { agentModes: ["interactive"], supportsReasoningEffort: false, supportsFileAttachments: false, supportsPlanRead: false, agentName: "Side chat" } : inner.getSessionCapabilities?.(id) ?? inner,
|
|
@@ -2220,12 +2416,29 @@ function toErrorDetails(error) {
|
|
|
2220
2416
|
};
|
|
2221
2417
|
}
|
|
2222
2418
|
|
|
2223
|
-
// src/
|
|
2419
|
+
// src/service/transport/relay-socket.ts
|
|
2224
2420
|
import { io } from "socket.io-client";
|
|
2421
|
+
function createRelaySocket(uri, options) {
|
|
2422
|
+
const socket = io(uri, { ...options, autoConnect: false });
|
|
2423
|
+
socket.on("connect_error", (error) => {
|
|
2424
|
+
const manager = socket.io;
|
|
2425
|
+
const transports = manager.opts.transports;
|
|
2426
|
+
if (error.message !== "timeout" || manager.engine.readyState !== "opening" || !manager.opts.tryAllTransports || transports === void 0 || transports.length < 2 || !transports.every((transport) => typeof transport === "string")) return;
|
|
2427
|
+
const failedTransport = manager.engine.transport.name;
|
|
2428
|
+
const failedIndex = transports.findIndex((transport) => transport === failedTransport);
|
|
2429
|
+
if (failedIndex < 0) return;
|
|
2430
|
+
manager.opts.transports = [
|
|
2431
|
+
...transports.slice(failedIndex + 1),
|
|
2432
|
+
...transports.slice(0, failedIndex + 1)
|
|
2433
|
+
];
|
|
2434
|
+
});
|
|
2435
|
+
if (options.autoConnect !== false) socket.connect();
|
|
2436
|
+
return socket;
|
|
2437
|
+
}
|
|
2225
2438
|
|
|
2226
2439
|
// src/service/requests/session-communication.ts
|
|
2227
2440
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2228
|
-
import { createHash as
|
|
2441
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
2229
2442
|
import { join as join7 } from "node:path";
|
|
2230
2443
|
import { performance as performance2 } from "node:perf_hooks";
|
|
2231
2444
|
|
|
@@ -2610,7 +2823,7 @@ var Work = class {
|
|
|
2610
2823
|
GIT_DIR: join6(cwd, "no-repository"),
|
|
2611
2824
|
GIT_CONFIG_NOSYSTEM: "1",
|
|
2612
2825
|
GIT_ATTR_NOSYSTEM: "1",
|
|
2613
|
-
GIT_CONFIG_GLOBAL:
|
|
2826
|
+
GIT_CONFIG_GLOBAL: join6(cwd, "gitconfig")
|
|
2614
2827
|
} : {}
|
|
2615
2828
|
}
|
|
2616
2829
|
}, (error, output2, stderr) => {
|
|
@@ -2939,6 +3152,7 @@ async function patch(context, change, work, scratchDirectory) {
|
|
|
2939
3152
|
await writeFile2(file, content, { flag: "wx", mode: 384 });
|
|
2940
3153
|
if (process.platform !== "win32") await chmod(file, mode === "100755" ? 448 : 384);
|
|
2941
3154
|
}
|
|
3155
|
+
await writeFile2(join6(scratch, "gitconfig"), "", { flag: "wx", mode: 384 });
|
|
2942
3156
|
text = decode(await work.git(scratch, [
|
|
2943
3157
|
"-c",
|
|
2944
3158
|
"core.quotePath=false",
|
|
@@ -3097,6 +3311,430 @@ var SessionGitDiffReader = class {
|
|
|
3097
3311
|
}
|
|
3098
3312
|
};
|
|
3099
3313
|
|
|
3314
|
+
// src/timeline-image-cache.ts
|
|
3315
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3316
|
+
var TimelineAssetError = class extends Error {
|
|
3317
|
+
constructor(code, message, options) {
|
|
3318
|
+
super(message, options);
|
|
3319
|
+
this.code = code;
|
|
3320
|
+
}
|
|
3321
|
+
code;
|
|
3322
|
+
};
|
|
3323
|
+
var TimelineImageCache = class {
|
|
3324
|
+
constructor(maxBytes = 32 * 1024 * 1024) {
|
|
3325
|
+
this.maxBytes = maxBytes;
|
|
3326
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < TIMELINE_IMAGE_MAX_BYTES) {
|
|
3327
|
+
throw new Error("Image cache must hold at least one maximum-size image");
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
maxBytes;
|
|
3331
|
+
#assets = /* @__PURE__ */ new Map();
|
|
3332
|
+
#bytes = 0;
|
|
3333
|
+
get bytes() {
|
|
3334
|
+
return this.#bytes;
|
|
3335
|
+
}
|
|
3336
|
+
referenceItem = (sessionId, item) => {
|
|
3337
|
+
if (!("attachments" in item) || item.attachments === void 0) return item;
|
|
3338
|
+
return { ...item, attachments: item.attachments.map((attachment) => this.#reference(sessionId, attachment)) };
|
|
3339
|
+
};
|
|
3340
|
+
read(sessionId, request) {
|
|
3341
|
+
const key = JSON.stringify([sessionId, request.sha256]);
|
|
3342
|
+
const asset = this.#assets.get(key);
|
|
3343
|
+
if (asset === void 0) throw new TimelineAssetError("IMAGE_ABSENT", "The image is no longer cached on this PC");
|
|
3344
|
+
if (request.offset >= asset.bytes.length) {
|
|
3345
|
+
throw new TimelineAssetError("IMAGE_OFFSET_INVALID", "The image chunk offset exceeds its length");
|
|
3346
|
+
}
|
|
3347
|
+
this.#assets.delete(key);
|
|
3348
|
+
this.#assets.set(key, asset);
|
|
3349
|
+
const end = Math.min(request.offset + TIMELINE_IMAGE_CHUNK_BYTES, asset.bytes.length);
|
|
3350
|
+
return TimelineImageChunkSchema.parse({
|
|
3351
|
+
sha256: request.sha256,
|
|
3352
|
+
offset: request.offset,
|
|
3353
|
+
totalBytes: asset.bytes.length,
|
|
3354
|
+
mimeType: asset.mimeType,
|
|
3355
|
+
data: asset.bytes.subarray(request.offset, end).toString("base64"),
|
|
3356
|
+
eof: end === asset.bytes.length
|
|
3357
|
+
});
|
|
3358
|
+
}
|
|
3359
|
+
readAsset(sessionId, sha256) {
|
|
3360
|
+
const key = JSON.stringify([sessionId, sha256]);
|
|
3361
|
+
const asset = this.#assets.get(key);
|
|
3362
|
+
if (asset === void 0) throw new TimelineAssetError("IMAGE_ABSENT", "The image is no longer cached on this PC");
|
|
3363
|
+
this.#assets.delete(key);
|
|
3364
|
+
this.#assets.set(key, asset);
|
|
3365
|
+
return { bytes: Buffer.from(asset.bytes), mimeType: asset.mimeType };
|
|
3366
|
+
}
|
|
3367
|
+
clearSession(sessionId) {
|
|
3368
|
+
const prefix = JSON.stringify([sessionId]).slice(0, -1) + ",";
|
|
3369
|
+
for (const [key, asset] of this.#assets) {
|
|
3370
|
+
if (key.startsWith(prefix)) {
|
|
3371
|
+
this.#assets.delete(key);
|
|
3372
|
+
this.#bytes -= asset.bytes.length;
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
clear() {
|
|
3377
|
+
this.#assets.clear();
|
|
3378
|
+
this.#bytes = 0;
|
|
3379
|
+
}
|
|
3380
|
+
#reference(sessionId, attachment) {
|
|
3381
|
+
if (attachment.kind !== "image" || attachment.data === void 0) return attachment;
|
|
3382
|
+
const parsed = ConversationAttachmentSchema.parse(attachment);
|
|
3383
|
+
const bytes = Buffer.from(attachment.data, "base64");
|
|
3384
|
+
if (bytes.length !== parsed.byteLength || createHash6("sha256").update(bytes).digest("hex") !== parsed.sha256 || !hasSignature(bytes, parsed.mimeType)) {
|
|
3385
|
+
throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its metadata");
|
|
3386
|
+
}
|
|
3387
|
+
const key = JSON.stringify([sessionId, parsed.sha256]);
|
|
3388
|
+
const previous = this.#assets.get(key);
|
|
3389
|
+
if (previous !== void 0) this.#bytes -= previous.bytes.length;
|
|
3390
|
+
this.#assets.delete(key);
|
|
3391
|
+
this.#assets.set(key, { bytes, mimeType: parsed.mimeType ?? "" });
|
|
3392
|
+
this.#bytes += bytes.length;
|
|
3393
|
+
while (this.#bytes > this.maxBytes) {
|
|
3394
|
+
const oldest = this.#assets.entries().next().value;
|
|
3395
|
+
if (oldest === void 0) break;
|
|
3396
|
+
this.#assets.delete(oldest[0]);
|
|
3397
|
+
this.#bytes -= oldest[1].bytes.length;
|
|
3398
|
+
}
|
|
3399
|
+
const { data: _data, ...metadata } = parsed;
|
|
3400
|
+
return { ...metadata, imageRef: "sha256" };
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
function hasSignature(bytes, mimeType) {
|
|
3404
|
+
switch (mimeType) {
|
|
3405
|
+
case "image/png":
|
|
3406
|
+
return bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
3407
|
+
case "image/jpeg":
|
|
3408
|
+
return bytes.subarray(0, 3).equals(Buffer.from([255, 216, 255]));
|
|
3409
|
+
case "image/gif":
|
|
3410
|
+
return ["GIF87a", "GIF89a"].includes(bytes.subarray(0, 6).toString("ascii"));
|
|
3411
|
+
case "image/webp":
|
|
3412
|
+
return bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP";
|
|
3413
|
+
default:
|
|
3414
|
+
return false;
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
// src/session-image-uploader.ts
|
|
3419
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
3420
|
+
import sharp2 from "sharp";
|
|
3421
|
+
import { z as z6 } from "zod";
|
|
3422
|
+
var MAX_ACTIVE_UPLOADS = 3;
|
|
3423
|
+
var MAX_JSON_BYTES = 16 * 1024;
|
|
3424
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
3425
|
+
var UPLOAD_TIMEOUT_MS = 7e4;
|
|
3426
|
+
var MAX_GRANT_LIFETIME_MS = 10 * 6e4 + 3e4;
|
|
3427
|
+
var UploaderConfigSchema = z6.object({
|
|
3428
|
+
relayUrl: SessionImageRelayUrlSchema,
|
|
3429
|
+
relayToken: z6.string().min(24).max(4096).regex(/^[\x21-\x7e]+$/u),
|
|
3430
|
+
userId: IdSchema.refine((value) => /^[\x20-\x7e]+$/u.test(value)),
|
|
3431
|
+
machineId: IdSchema,
|
|
3432
|
+
storageAccount: SessionImagesConfigSchema.shape.storageAccount,
|
|
3433
|
+
storageContainer: SessionImagesConfigSchema.shape.storageContainer
|
|
3434
|
+
});
|
|
3435
|
+
function safeCause(error) {
|
|
3436
|
+
if (error instanceof SessionImageUploadError) return error;
|
|
3437
|
+
if (error instanceof z6.ZodError) return new Error("Schema validation failed.");
|
|
3438
|
+
const result = new Error("Image operation failed.");
|
|
3439
|
+
if (error instanceof Error) {
|
|
3440
|
+
if (["TypeError", "SyntaxError", "AbortError", "TimeoutError"].includes(error.name)) result.name = error.name;
|
|
3441
|
+
const code = "code" in error ? error.code : void 0;
|
|
3442
|
+
if (typeof code === "string" && ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND", "EAI_AGAIN", "UND_ERR_CONNECT_TIMEOUT"].includes(code)) {
|
|
3443
|
+
result.cause = { code };
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
return result;
|
|
3447
|
+
}
|
|
3448
|
+
var SessionImageUploadError = class extends Error {
|
|
3449
|
+
constructor(code, stage, status, cause) {
|
|
3450
|
+
super(`${code} (${stage})`, cause === void 0 ? void 0 : { cause: safeCause(cause) });
|
|
3451
|
+
this.code = code;
|
|
3452
|
+
this.stage = stage;
|
|
3453
|
+
this.status = status;
|
|
3454
|
+
this.name = "SessionImageUploadError";
|
|
3455
|
+
}
|
|
3456
|
+
code;
|
|
3457
|
+
stage;
|
|
3458
|
+
status;
|
|
3459
|
+
};
|
|
3460
|
+
function digest(bytes) {
|
|
3461
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
3462
|
+
}
|
|
3463
|
+
function assert(condition, code, stage) {
|
|
3464
|
+
if (!condition) throw new SessionImageUploadError(code, stage);
|
|
3465
|
+
}
|
|
3466
|
+
function parse(schema, input, stage) {
|
|
3467
|
+
const result = schema.safeParse(input);
|
|
3468
|
+
if (!result.success) throw new SessionImageUploadError(
|
|
3469
|
+
stage === "config" ? "IMAGE_CONFIG_INVALID" : stage === "source" ? "IMAGE_CONTENT_INVALID" : "IMAGE_RESPONSE_INVALID",
|
|
3470
|
+
stage,
|
|
3471
|
+
void 0,
|
|
3472
|
+
result.error
|
|
3473
|
+
);
|
|
3474
|
+
return result.data;
|
|
3475
|
+
}
|
|
3476
|
+
function abortable(operation, signal, stage) {
|
|
3477
|
+
return new Promise((resolve5, reject) => {
|
|
3478
|
+
const abort = () => reject(new SessionImageUploadError("IMAGE_ABORTED", stage));
|
|
3479
|
+
if (signal.aborted) abort();
|
|
3480
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
3481
|
+
void operation.then((value) => {
|
|
3482
|
+
signal.removeEventListener("abort", abort);
|
|
3483
|
+
resolve5(value);
|
|
3484
|
+
}, (error) => {
|
|
3485
|
+
signal.removeEventListener("abort", abort);
|
|
3486
|
+
reject(error);
|
|
3487
|
+
});
|
|
3488
|
+
});
|
|
3489
|
+
}
|
|
3490
|
+
var SessionImageUploader = class {
|
|
3491
|
+
#config;
|
|
3492
|
+
#fetch;
|
|
3493
|
+
#active = /* @__PURE__ */ new Set();
|
|
3494
|
+
#closed = false;
|
|
3495
|
+
constructor(config, dependencies = {}) {
|
|
3496
|
+
this.#config = parse(UploaderConfigSchema, config, "config");
|
|
3497
|
+
this.#fetch = dependencies.fetch ?? globalThis.fetch;
|
|
3498
|
+
}
|
|
3499
|
+
close() {
|
|
3500
|
+
this.#closed = true;
|
|
3501
|
+
for (const controller of this.#active) controller.abort();
|
|
3502
|
+
}
|
|
3503
|
+
async upload(sessionId, source, signal, assertCurrent = () => void 0) {
|
|
3504
|
+
assert(!this.#closed, "IMAGE_UPLOADER_CLOSED", "source");
|
|
3505
|
+
assert(!signal?.aborted, "IMAGE_ABORTED", "source");
|
|
3506
|
+
const revalidate = (stage) => {
|
|
3507
|
+
try {
|
|
3508
|
+
assertCurrent();
|
|
3509
|
+
} catch (error) {
|
|
3510
|
+
throw new SessionImageUploadError("IMAGE_ACCESS_REVOKED", stage, void 0, error);
|
|
3511
|
+
}
|
|
3512
|
+
};
|
|
3513
|
+
revalidate("source");
|
|
3514
|
+
assert(this.#active.size < MAX_ACTIVE_UPLOADS, "IMAGE_CAPACITY_EXCEEDED", "source");
|
|
3515
|
+
assert(
|
|
3516
|
+
Buffer.isBuffer(source) && source.length > 0 && source.length <= SESSION_IMAGE_MAX_BYTES,
|
|
3517
|
+
"IMAGE_CONTENT_INVALID",
|
|
3518
|
+
"source"
|
|
3519
|
+
);
|
|
3520
|
+
const scope = parse(SessionImageScopeSchema, { machineId: this.#config.machineId, sessionId }, "source");
|
|
3521
|
+
const controller = new AbortController();
|
|
3522
|
+
this.#active.add(controller);
|
|
3523
|
+
const abort = () => controller.abort();
|
|
3524
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3525
|
+
let timedOut = false;
|
|
3526
|
+
const timer = setTimeout(() => {
|
|
3527
|
+
timedOut = true;
|
|
3528
|
+
controller.abort();
|
|
3529
|
+
}, UPLOAD_TIMEOUT_MS);
|
|
3530
|
+
timer.unref();
|
|
3531
|
+
try {
|
|
3532
|
+
const bytes = Buffer.from(source);
|
|
3533
|
+
const dimensions = await this.#dimensions(bytes);
|
|
3534
|
+
const hash2 = digest(bytes);
|
|
3535
|
+
const request = parse(SessionImageUploadRequestSchema, { ...scope, sizeBytes: bytes.length }, "source");
|
|
3536
|
+
revalidate("reserve");
|
|
3537
|
+
const upload = await this.#json(
|
|
3538
|
+
`${SESSION_IMAGE_API_PATH}/uploads`,
|
|
3539
|
+
request,
|
|
3540
|
+
201,
|
|
3541
|
+
SessionImageUploadResultSchema,
|
|
3542
|
+
"reserve",
|
|
3543
|
+
controller.signal
|
|
3544
|
+
);
|
|
3545
|
+
const target = this.#target(upload, scope.sessionId);
|
|
3546
|
+
let ambiguous;
|
|
3547
|
+
revalidate("put");
|
|
3548
|
+
try {
|
|
3549
|
+
await this.#request(target, {
|
|
3550
|
+
method: "PUT",
|
|
3551
|
+
headers: upload.mode === "direct" ? upload.headers : { ...this.#headers(), ...upload.headers },
|
|
3552
|
+
body: new Uint8Array(bytes)
|
|
3553
|
+
}, "put", controller.signal, async (response) => {
|
|
3554
|
+
if (response.status !== 201) throw new SessionImageUploadError("IMAGE_HTTP_ERROR", "put", response.status);
|
|
3555
|
+
});
|
|
3556
|
+
} catch (error) {
|
|
3557
|
+
if (!(error instanceof SessionImageUploadError) || controller.signal.aborted || !(error.code === "IMAGE_NETWORK_ERROR" || error.code === "IMAGE_TIMEOUT" || error.code === "IMAGE_HTTP_ERROR" && [409, 412].includes(error.status ?? 0))) throw error;
|
|
3558
|
+
ambiguous = error;
|
|
3559
|
+
}
|
|
3560
|
+
revalidate("complete");
|
|
3561
|
+
try {
|
|
3562
|
+
const committed = await this.#json(
|
|
3563
|
+
`${SESSION_IMAGE_API_PATH}/${upload.imageId}/complete`,
|
|
3564
|
+
scope,
|
|
3565
|
+
200,
|
|
3566
|
+
SessionImageCommitResultSchema,
|
|
3567
|
+
"complete",
|
|
3568
|
+
controller.signal
|
|
3569
|
+
);
|
|
3570
|
+
assert(
|
|
3571
|
+
committed.imageId === upload.imageId && committed.machineId === scope.machineId && committed.sessionId === scope.sessionId && committed.sha256 === hash2 && committed.sizeBytes === bytes.length && committed.width === dimensions.width && committed.height === dimensions.height && Number.isSafeInteger(committed.width) && Number.isSafeInteger(committed.height) && committed.width * committed.height <= SESSION_IMAGE_MAX_PIXELS && Number.isSafeInteger(committed.expiresAt) && committed.expiresAt > Date.now(),
|
|
3572
|
+
"IMAGE_COMMIT_MISMATCH",
|
|
3573
|
+
"complete"
|
|
3574
|
+
);
|
|
3575
|
+
revalidate("complete");
|
|
3576
|
+
return committed;
|
|
3577
|
+
} catch (error) {
|
|
3578
|
+
if (ambiguous === void 0 || error instanceof SessionImageUploadError && error.code === "IMAGE_ACCESS_REVOKED") {
|
|
3579
|
+
throw error;
|
|
3580
|
+
}
|
|
3581
|
+
const failure = new SessionImageUploadError("IMAGE_COMPLETION_FAILED", "complete", void 0, error);
|
|
3582
|
+
failure.cause = new AggregateError([ambiguous, safeCause(error)], "PUT outcome and completion failed.");
|
|
3583
|
+
throw failure;
|
|
3584
|
+
}
|
|
3585
|
+
} catch (error) {
|
|
3586
|
+
if (controller.signal.aborted) {
|
|
3587
|
+
throw new SessionImageUploadError(
|
|
3588
|
+
timedOut ? "IMAGE_TIMEOUT" : "IMAGE_ABORTED",
|
|
3589
|
+
error instanceof SessionImageUploadError ? error.stage : "source",
|
|
3590
|
+
void 0,
|
|
3591
|
+
error
|
|
3592
|
+
);
|
|
3593
|
+
}
|
|
3594
|
+
if (error instanceof SessionImageUploadError) throw error;
|
|
3595
|
+
throw new SessionImageUploadError("IMAGE_CONTENT_INVALID", "source", void 0, error);
|
|
3596
|
+
} finally {
|
|
3597
|
+
clearTimeout(timer);
|
|
3598
|
+
signal?.removeEventListener("abort", abort);
|
|
3599
|
+
this.#active.delete(controller);
|
|
3600
|
+
}
|
|
3601
|
+
}
|
|
3602
|
+
async #dimensions(bytes) {
|
|
3603
|
+
assert(
|
|
3604
|
+
bytes.length >= 20 && bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP" && bytes.readUInt32LE(4) + 8 === bytes.length,
|
|
3605
|
+
"IMAGE_CONTENT_INVALID",
|
|
3606
|
+
"source"
|
|
3607
|
+
);
|
|
3608
|
+
const decoder = sharp2(bytes, { limitInputPixels: SESSION_IMAGE_MAX_PIXELS, failOn: "warning" });
|
|
3609
|
+
try {
|
|
3610
|
+
const metadata = await decoder.metadata();
|
|
3611
|
+
assert(metadata.format === "webp" && metadata.width !== void 0 && metadata.height !== void 0 && metadata.width > 0 && metadata.height > 0 && (metadata.pages ?? 1) === 1 && metadata.width * metadata.height <= SESSION_IMAGE_MAX_PIXELS, "IMAGE_CONTENT_INVALID", "source");
|
|
3612
|
+
return { width: metadata.width, height: metadata.height };
|
|
3613
|
+
} finally {
|
|
3614
|
+
decoder.destroy();
|
|
3615
|
+
}
|
|
3616
|
+
}
|
|
3617
|
+
#headers() {
|
|
3618
|
+
return { authorization: `Bearer ${this.#config.relayToken}`, "x-agentknight-user-id": this.#config.userId };
|
|
3619
|
+
}
|
|
3620
|
+
#target(upload, sessionId) {
|
|
3621
|
+
const now = Date.now();
|
|
3622
|
+
assert(Number.isSafeInteger(upload.uploadExpiresAt) && upload.uploadExpiresAt > now && upload.uploadExpiresAt <= now + MAX_GRANT_LIFETIME_MS, "IMAGE_UPLOAD_EXPIRED", "reserve");
|
|
3623
|
+
assert(
|
|
3624
|
+
upload.headers["content-type"] === "image/webp" && (upload.headers["if-none-match"] === void 0 || upload.headers["if-none-match"] === "*"),
|
|
3625
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3626
|
+
"reserve"
|
|
3627
|
+
);
|
|
3628
|
+
if (upload.mode === "proxy") {
|
|
3629
|
+
const path2 = `${SESSION_IMAGE_API_PATH}/${upload.imageId}/content`;
|
|
3630
|
+
const url2 = new URL(upload.uploadUrl, this.#config.relayUrl);
|
|
3631
|
+
assert(
|
|
3632
|
+
upload.uploadUrl.split("?")[0] === path2 && url2.origin === this.#config.relayUrl && [...url2.searchParams.keys()].length === 2 && url2.searchParams.getAll("machineId").length === 1 && url2.searchParams.get("machineId") === this.#config.machineId && url2.searchParams.getAll("sessionId").length === 1 && url2.searchParams.get("sessionId") === sessionId && upload.headers["x-ms-blob-type"] === void 0,
|
|
3633
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3634
|
+
"reserve"
|
|
3635
|
+
);
|
|
3636
|
+
return url2.href;
|
|
3637
|
+
}
|
|
3638
|
+
const { storageAccount, storageContainer, userId, machineId } = this.#config;
|
|
3639
|
+
const host = `${storageAccount}.blob.core.windows.net`;
|
|
3640
|
+
const path = `/${storageContainer}/uploads/${digest(userId)}/${digest(machineId)}/${upload.imageId}.webp`;
|
|
3641
|
+
const url = new URL(upload.uploadUrl);
|
|
3642
|
+
const query = url.searchParams;
|
|
3643
|
+
const allowed = /* @__PURE__ */ new Set(["sv", "spr", "st", "se", "sr", "sp", "sig", "skoid", "sktid", "skt", "ske", "sks", "skv"]);
|
|
3644
|
+
assert(
|
|
3645
|
+
upload.uploadUrl.startsWith(`https://${host}${path}?`) && url.host === host && url.pathname === path && upload.headers["x-ms-blob-type"] === "BlockBlob" && [...query.keys()].every((key) => allowed.has(key) && query.getAll(key).length === 1) && [...allowed].every((key) => (query.get(key)?.length ?? 0) > 0) && query.get("sp") === "c" && query.get("sr") === "b" && query.get("spr") === "https" && query.get("sks") === "b",
|
|
3646
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3647
|
+
"reserve"
|
|
3648
|
+
);
|
|
3649
|
+
const expiresAt = Date.parse(query.get("se") ?? "");
|
|
3650
|
+
const startsAt = Date.parse(query.get("st") ?? "");
|
|
3651
|
+
const keyStartsAt = Date.parse(query.get("skt") ?? "");
|
|
3652
|
+
const keyExpiresAt = Date.parse(query.get("ske") ?? "");
|
|
3653
|
+
assert(
|
|
3654
|
+
Number.isFinite(startsAt) && startsAt <= now + 3e4 && Number.isFinite(keyStartsAt) && keyStartsAt <= now + 3e4 && Number.isFinite(expiresAt) && expiresAt > now && expiresAt <= upload.uploadExpiresAt && Number.isFinite(keyExpiresAt) && keyExpiresAt >= expiresAt,
|
|
3655
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3656
|
+
"reserve"
|
|
3657
|
+
);
|
|
3658
|
+
return url.href;
|
|
3659
|
+
}
|
|
3660
|
+
async #json(path, body, status, schema, stage, signal) {
|
|
3661
|
+
return this.#request(`${this.#config.relayUrl}${path}`, {
|
|
3662
|
+
method: "POST",
|
|
3663
|
+
headers: { ...this.#headers(), "content-type": "application/json" },
|
|
3664
|
+
body: JSON.stringify(body)
|
|
3665
|
+
}, stage, signal, async (response, requestSignal) => {
|
|
3666
|
+
if (response.status !== status) throw new SessionImageUploadError("IMAGE_HTTP_ERROR", stage, response.status);
|
|
3667
|
+
const contentLength = response.headers.get("content-length");
|
|
3668
|
+
assert(
|
|
3669
|
+
contentLength === null || /^\d+$/u.test(contentLength) && Number(contentLength) <= MAX_JSON_BYTES,
|
|
3670
|
+
"IMAGE_RESPONSE_TOO_LARGE",
|
|
3671
|
+
stage
|
|
3672
|
+
);
|
|
3673
|
+
assert(response.body !== null, "IMAGE_RESPONSE_INVALID", stage);
|
|
3674
|
+
const reader = response.body.getReader();
|
|
3675
|
+
const chunks = [];
|
|
3676
|
+
let size = 0;
|
|
3677
|
+
try {
|
|
3678
|
+
while (true) {
|
|
3679
|
+
const next = await abortable(reader.read(), requestSignal, stage);
|
|
3680
|
+
requestSignal.throwIfAborted();
|
|
3681
|
+
if (next.done) break;
|
|
3682
|
+
size += next.value.byteLength;
|
|
3683
|
+
assert(size <= MAX_JSON_BYTES, "IMAGE_RESPONSE_TOO_LARGE", stage);
|
|
3684
|
+
chunks.push(next.value);
|
|
3685
|
+
}
|
|
3686
|
+
let value;
|
|
3687
|
+
try {
|
|
3688
|
+
value = JSON.parse(Buffer.concat(chunks, size).toString("utf8"));
|
|
3689
|
+
} catch (error) {
|
|
3690
|
+
throw new SessionImageUploadError("IMAGE_RESPONSE_INVALID", stage, void 0, error);
|
|
3691
|
+
}
|
|
3692
|
+
return parse(schema, value, stage);
|
|
3693
|
+
} finally {
|
|
3694
|
+
reader.releaseLock();
|
|
3695
|
+
}
|
|
3696
|
+
});
|
|
3697
|
+
}
|
|
3698
|
+
async #request(url, init, stage, signal, consume) {
|
|
3699
|
+
assert(!signal.aborted, "IMAGE_ABORTED", stage);
|
|
3700
|
+
const controller = new AbortController();
|
|
3701
|
+
const abort = () => controller.abort();
|
|
3702
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
3703
|
+
let timedOut = false;
|
|
3704
|
+
const timer = setTimeout(() => {
|
|
3705
|
+
timedOut = true;
|
|
3706
|
+
controller.abort();
|
|
3707
|
+
}, REQUEST_TIMEOUT_MS);
|
|
3708
|
+
timer.unref();
|
|
3709
|
+
try {
|
|
3710
|
+
const response = await this.#fetch(url, {
|
|
3711
|
+
...init,
|
|
3712
|
+
signal: controller.signal,
|
|
3713
|
+
redirect: "error",
|
|
3714
|
+
credentials: "omit",
|
|
3715
|
+
cache: "no-store",
|
|
3716
|
+
referrerPolicy: "no-referrer"
|
|
3717
|
+
});
|
|
3718
|
+
try {
|
|
3719
|
+
controller.signal.throwIfAborted();
|
|
3720
|
+
assert(!response.redirected, "IMAGE_RESPONSE_INVALID", stage);
|
|
3721
|
+
return await consume(response, controller.signal);
|
|
3722
|
+
} finally {
|
|
3723
|
+
await response.body?.cancel();
|
|
3724
|
+
}
|
|
3725
|
+
} catch (error) {
|
|
3726
|
+
if (controller.signal.aborted) {
|
|
3727
|
+
throw new SessionImageUploadError(timedOut ? "IMAGE_TIMEOUT" : "IMAGE_ABORTED", stage, void 0, error);
|
|
3728
|
+
}
|
|
3729
|
+
if (error instanceof SessionImageUploadError) throw error;
|
|
3730
|
+
throw new SessionImageUploadError("IMAGE_NETWORK_ERROR", stage, void 0, error);
|
|
3731
|
+
} finally {
|
|
3732
|
+
clearTimeout(timer);
|
|
3733
|
+
signal.removeEventListener("abort", abort);
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
};
|
|
3737
|
+
|
|
3100
3738
|
// src/service/requests/request-errors.ts
|
|
3101
3739
|
function operationErrorCode(agentId, operation, error) {
|
|
3102
3740
|
if (isSessionRestoreFailure(error)) return "SESSION_RESTORE_FAILED";
|
|
@@ -3113,7 +3751,7 @@ function unsupportedOperation(error) {
|
|
|
3113
3751
|
// src/service/requests/communication-operations.ts
|
|
3114
3752
|
var compress2 = promisify2(gzip3);
|
|
3115
3753
|
function createCommunicationOperations(options) {
|
|
3116
|
-
const { adapter, publications } = options;
|
|
3754
|
+
const { adapter, publications, imageBlob } = options;
|
|
3117
3755
|
const directoryLister = new SessionDirectoryLister(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
|
|
3118
3756
|
const fileReader = new SessionFileReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
|
|
3119
3757
|
const gitDiffReader = new SessionGitDiffReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId), options.gitDiffDirectory);
|
|
@@ -3122,6 +3760,24 @@ function createCommunicationOperations(options) {
|
|
|
3122
3760
|
const renameSession = adapter.renameSession?.bind(adapter);
|
|
3123
3761
|
return [
|
|
3124
3762
|
...options.workbenchOperations,
|
|
3763
|
+
...imageBlob === void 0 ? [] : [
|
|
3764
|
+
sessionCommunicationOperation({
|
|
3765
|
+
operation: TIMELINE_IMAGE_BLOB_OPERATION,
|
|
3766
|
+
version: TIMELINE_IMAGE_BLOB_VERSION,
|
|
3767
|
+
schema: TimelineImageBlobRequestSchema,
|
|
3768
|
+
handle: async ({ sha256 }, request) => {
|
|
3769
|
+
const assertCurrent = options.captureRequestAccess();
|
|
3770
|
+
assertCurrent();
|
|
3771
|
+
const result = await imageBlob.publish(request.sessionId, sha256, assertCurrent);
|
|
3772
|
+
assertCurrent();
|
|
3773
|
+
return SessionCommunicationDataSchema.parse(result);
|
|
3774
|
+
},
|
|
3775
|
+
onError: (error) => ({
|
|
3776
|
+
code: error instanceof TimelineAssetError || error instanceof SessionImageUploadError || error instanceof SessionNotSharedError || error instanceof AgentSessionRenameError ? error.code : "IMAGE_BLOB_FAILED",
|
|
3777
|
+
message: "The cloud image is unavailable"
|
|
3778
|
+
})
|
|
3779
|
+
})
|
|
3780
|
+
],
|
|
3125
3781
|
...renameSession === void 0 ? [] : [
|
|
3126
3782
|
sessionCommunicationOperation({
|
|
3127
3783
|
operation: SESSION_RENAME_OPERATION,
|
|
@@ -3198,16 +3854,16 @@ function createCommunicationOperations(options) {
|
|
|
3198
3854
|
}),
|
|
3199
3855
|
onError: (error) => unsupportedOperation(error) ?? (error instanceof SessionDirectoryError ? { code: error.code, message: error.message } : { code: "DIRECTORY_UNAVAILABLE", message: "The directory is unavailable" })
|
|
3200
3856
|
}),
|
|
3201
|
-
sessionCommunicationOperation({
|
|
3857
|
+
...[SESSION_FILE_READ_VERSION, SESSION_FILE_READ_V2_VERSION].map((version) => sessionCommunicationOperation({
|
|
3202
3858
|
operation: SESSION_FILE_READ_OPERATION,
|
|
3203
|
-
version
|
|
3859
|
+
version,
|
|
3204
3860
|
schema: SessionFileReadDataSchema,
|
|
3205
|
-
handle: ({ path, rootRevision }, request) => fileReader.read(request.sessionId, path, rootRevision),
|
|
3861
|
+
handle: ({ path, rootRevision }, request) => fileReader.read(request.sessionId, path, rootRevision, version),
|
|
3206
3862
|
onError: (error) => unsupportedOperation(error) ?? {
|
|
3207
3863
|
code: error instanceof SessionFileReadError ? error.code : "FILE_UNAVAILABLE",
|
|
3208
3864
|
message: "The file preview is unavailable"
|
|
3209
3865
|
}
|
|
3210
|
-
}),
|
|
3866
|
+
})),
|
|
3211
3867
|
sessionCommunicationOperation({
|
|
3212
3868
|
operation: SESSION_RESOURCE_READ_OPERATION,
|
|
3213
3869
|
version: SESSION_RESOURCE_READ_VERSION,
|
|
@@ -3269,6 +3925,7 @@ var SessionCommunicationHandler = class {
|
|
|
3269
3925
|
return assertCurrent;
|
|
3270
3926
|
},
|
|
3271
3927
|
incrementalOperations: options.incrementalOperations.filter((operation) => config.timelineIncrementalEnabled !== false || operation.operation === TIMELINE_IMAGE_OPERATION),
|
|
3928
|
+
...options.imageBlob === void 0 ? {} : { imageBlob: options.imageBlob },
|
|
3272
3929
|
workbenchOperations: this.#operations === void 0 ? [] : createWorkbenchOperations({
|
|
3273
3930
|
...workbench === void 0 ? {} : { runtime: workbench },
|
|
3274
3931
|
...researchWorkbench === void 0 ? {} : { research: researchWorkbench },
|
|
@@ -3437,7 +4094,7 @@ var SessionCommunicationHandler = class {
|
|
|
3437
4094
|
}
|
|
3438
4095
|
const clientId = scope.clientId;
|
|
3439
4096
|
const owner = [config.userId, machineId, clientId, request.sessionId];
|
|
3440
|
-
const key = (parts) =>
|
|
4097
|
+
const key = (parts) => createHash8("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 24);
|
|
3441
4098
|
const knownSource = this.#researchParents.get(request.sessionId) ?? adapter.sessionPublicationSource?.(request.sessionId) ?? request.sessionId;
|
|
3442
4099
|
const arrivalPublication = adapter.isSessionVisible?.(knownSource) === true ? publications.capture(knownSource) : void 0;
|
|
3443
4100
|
let researchParent = this.#researchParents.get(request.sessionId);
|
|
@@ -3470,7 +4127,7 @@ var SessionCommunicationHandler = class {
|
|
|
3470
4127
|
}
|
|
3471
4128
|
if (researchParent !== void 0) {
|
|
3472
4129
|
if (lifecycle !== void 0) lifecycle.preflightPhase = "isolation";
|
|
3473
|
-
const unifiedSync = research?.chat !== void 0 && this.options.supportsResearchChat?.(clientId) === true && (request.operation === TIMELINE_SYNC_OPERATION || request.operation === TIMELINE_IMAGE_OPERATION);
|
|
4130
|
+
const unifiedSync = research?.chat !== void 0 && this.options.supportsResearchChat?.(clientId) === true && (request.operation === TIMELINE_SYNC_OPERATION || request.operation === TIMELINE_IMAGE_OPERATION || request.operation === TIMELINE_IMAGE_BLOB_OPERATION);
|
|
3474
4131
|
if (!unifiedSync && !["research.timeline", "research.send", "research.abort", "research.delete", "operation.get", "insights.read"].includes(request.operation)) {
|
|
3475
4132
|
reject("research-isolated");
|
|
3476
4133
|
emit("session.communication", sessionCommunicationError(request, {
|
|
@@ -3617,7 +4274,7 @@ var SessionCommunicationHandler = class {
|
|
|
3617
4274
|
};
|
|
3618
4275
|
|
|
3619
4276
|
// src/desktop-workspaces.ts
|
|
3620
|
-
import { createHash as
|
|
4277
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
3621
4278
|
import { readdir, realpath as realpath5, stat as stat6 } from "node:fs/promises";
|
|
3622
4279
|
import { homedir as homedir2 } from "node:os";
|
|
3623
4280
|
import { basename as basename2, join as join8, normalize } from "node:path";
|
|
@@ -3633,7 +4290,7 @@ function desktopRootCandidates(environment2 = process.env) {
|
|
|
3633
4290
|
}
|
|
3634
4291
|
function workspaceId(path) {
|
|
3635
4292
|
const normalizedPath = workspacePathKey(path);
|
|
3636
|
-
return
|
|
4293
|
+
return createHash9("sha256").update(normalizedPath).digest("base64url").slice(0, 32);
|
|
3637
4294
|
}
|
|
3638
4295
|
function workspacePathKey(path) {
|
|
3639
4296
|
const normalizedPath = normalize(path);
|
|
@@ -3950,14 +4607,14 @@ var SessionOptionsHandler = class {
|
|
|
3950
4607
|
]));
|
|
3951
4608
|
if (modelResult.status === "fulfilled" && modelResult.value.length > 0) availableModels = modelResult.value;
|
|
3952
4609
|
if (configurationResult.status === "fulfilled") configuration = configurationResult.value;
|
|
3953
|
-
for (const [stage,
|
|
4610
|
+
for (const [stage, result2] of [
|
|
3954
4611
|
["configuration", configurationResult],
|
|
3955
4612
|
["models", modelResult],
|
|
3956
4613
|
["workspaces", workspaceResult]
|
|
3957
4614
|
]) {
|
|
3958
|
-
if (
|
|
4615
|
+
if (result2.status === "rejected" && isSessionRestoreFailure(result2.reason)) {
|
|
3959
4616
|
failureStage = stage;
|
|
3960
|
-
throw
|
|
4617
|
+
throw result2.reason;
|
|
3961
4618
|
}
|
|
3962
4619
|
}
|
|
3963
4620
|
if (configurationResult.status === "rejected") {
|
|
@@ -3979,7 +4636,8 @@ var SessionOptionsHandler = class {
|
|
|
3979
4636
|
failureStage = "models";
|
|
3980
4637
|
throw modelFailure ?? new Error("No coding-agent models are available. Select an authenticated coding agent on the PC.");
|
|
3981
4638
|
}
|
|
3982
|
-
const
|
|
4639
|
+
const progressive = request.sessionId !== void 0 && request.capabilities?.includes(SESSION_OPTIONS_PROGRESSIVE_CAPABILITY) === true;
|
|
4640
|
+
const environment2 = progressive ? void 0 : await sessionEnvironment;
|
|
3983
4641
|
const worktreeCreationSupported = request.sessionId === void 0 && request.capabilities?.includes(SESSION_WORKTREE_CREATION_CAPABILITY) === true;
|
|
3984
4642
|
let worktreeChoices;
|
|
3985
4643
|
if (worktreeCreationSupported && request.workspaceId !== void 0) {
|
|
@@ -3991,7 +4649,7 @@ var SessionOptionsHandler = class {
|
|
|
3991
4649
|
}
|
|
3992
4650
|
const capabilities = adapter.getSessionCapabilities?.(request.sessionId) ?? adapter;
|
|
3993
4651
|
this.#errors.recovered(errorKey);
|
|
3994
|
-
|
|
4652
|
+
const result = {
|
|
3995
4653
|
ok: true,
|
|
3996
4654
|
requestId: request.requestId,
|
|
3997
4655
|
machineId,
|
|
@@ -4013,7 +4671,21 @@ var SessionOptionsHandler = class {
|
|
|
4013
4671
|
...environment2 === void 0 ? {} : { environment: environment2 },
|
|
4014
4672
|
agentName: "agentName" in capabilities && typeof capabilities.agentName === "string" ? capabilities.agentName : codingAgentPresentation(adapter.agentId).name,
|
|
4015
4673
|
...current?.configOptions === void 0 ? {} : { configOptions: current.configOptions }
|
|
4674
|
+
};
|
|
4675
|
+
if (!scope.isCurrent()) return;
|
|
4676
|
+
emit("session.options.result", {
|
|
4677
|
+
...result,
|
|
4678
|
+
...progressive ? { environmentPending: true } : {}
|
|
4016
4679
|
});
|
|
4680
|
+
if (progressive) {
|
|
4681
|
+
const environment3 = await sessionEnvironment;
|
|
4682
|
+
if (!scope.isCurrent()) return;
|
|
4683
|
+
adapter.getSessionCapabilities?.(request.sessionId);
|
|
4684
|
+
emit("session.options.result", {
|
|
4685
|
+
...result,
|
|
4686
|
+
...environment3 === void 0 ? {} : { environment: environment3 }
|
|
4687
|
+
});
|
|
4688
|
+
}
|
|
4017
4689
|
const plan = await sessionPlan;
|
|
4018
4690
|
if (plan !== void 0 && scope.isCurrent()) {
|
|
4019
4691
|
emit("conversation.item.upsert", plan);
|
|
@@ -4395,13 +5067,17 @@ var SessionCommandsHandler = class {
|
|
|
4395
5067
|
};
|
|
4396
5068
|
|
|
4397
5069
|
// src/service/requests/session-prompt.ts
|
|
4398
|
-
import { createHash as
|
|
5070
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
4399
5071
|
function promptOperationFingerprint(request) {
|
|
4400
|
-
|
|
5072
|
+
const fingerprint = createHash10("sha256").update(request.sessionId).update("\0").update(request.mode).update("\0").update(request.prompt).update("\0").update(request.attachments?.map((attachment) => attachment.sha256).join(",") ?? "").update("\0").update(JSON.stringify(request.fileAttachments?.map(({ name, mimeType, sha256 }) => ({
|
|
4401
5073
|
name,
|
|
4402
5074
|
mimeType,
|
|
4403
5075
|
sha256
|
|
4404
|
-
})) ?? []))
|
|
5076
|
+
})) ?? []));
|
|
5077
|
+
if (request.avatarPersona !== void 0) {
|
|
5078
|
+
fingerprint.update("\0").update(JSON.stringify(request.avatarPersona));
|
|
5079
|
+
}
|
|
5080
|
+
return fingerprint.digest("base64url");
|
|
4405
5081
|
}
|
|
4406
5082
|
var SessionPromptHandler = class {
|
|
4407
5083
|
constructor(options) {
|
|
@@ -4423,6 +5099,9 @@ var SessionPromptHandler = class {
|
|
|
4423
5099
|
if (adapter.isSessionVisible?.(request.sessionId) === false) {
|
|
4424
5100
|
throw new Error("This session's coding agent is not shared or is unavailable on the PC");
|
|
4425
5101
|
}
|
|
5102
|
+
if (request.avatarPersona !== void 0 && adapter.agentId !== "copilot" && adapter.agentId !== "managed") {
|
|
5103
|
+
throw new Error("Avatar personas require Copilot; nothing was submitted");
|
|
5104
|
+
}
|
|
4426
5105
|
const accepted = await diagnostics.run(() => this.#operations.run({
|
|
4427
5106
|
operationId: request.operationId,
|
|
4428
5107
|
fingerprint: promptOperationFingerprint(request),
|
|
@@ -4441,7 +5120,8 @@ var SessionPromptHandler = class {
|
|
|
4441
5120
|
request.attachments,
|
|
4442
5121
|
this.options.supportsPersistentInteractions(context.clientId),
|
|
4443
5122
|
request.fileAttachments,
|
|
4444
|
-
request.operationId
|
|
5123
|
+
request.operationId,
|
|
5124
|
+
...request.avatarPersona === void 0 ? [] : [request.avatarPersona]
|
|
4445
5125
|
)
|
|
4446
5126
|
}));
|
|
4447
5127
|
emit("session.prompt.result", {
|
|
@@ -4560,63 +5240,95 @@ var SessionTimelinePageHandler = class {
|
|
|
4560
5240
|
if (request.metadataOnly === true && request.assistantPreview !== true && adapter.supportsSessionListUpdates !== true) {
|
|
4561
5241
|
throw new Error("Passive session metadata is not supported by this runtime");
|
|
4562
5242
|
}
|
|
4563
|
-
const
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
5243
|
+
const references = request.imageReferences === true && config.timelineIncrementalEnabled !== false && this.options.imageReferencesEnabled();
|
|
5244
|
+
let pageSize = request.pageSize;
|
|
5245
|
+
while (true) {
|
|
5246
|
+
let itemCount;
|
|
5247
|
+
try {
|
|
5248
|
+
phase = "read";
|
|
5249
|
+
const page = await diagnostics.run(() => measureOperationPhase("read", () => adapter.readSessionTimelinePage(
|
|
5250
|
+
request.sessionId,
|
|
5251
|
+
{
|
|
5252
|
+
pageSize,
|
|
5253
|
+
...request.cursor === void 0 ? {} : { cursor: request.cursor },
|
|
5254
|
+
...request.metadataOnly === true ? {
|
|
5255
|
+
metadataOnly: true,
|
|
5256
|
+
...request.assistantPreview === true ? { assistantPreview: true } : {}
|
|
5257
|
+
} : {
|
|
5258
|
+
...references || request.cursor !== void 0 || pageSize !== request.pageSize ? { readOnly: true } : {},
|
|
5259
|
+
...references ? {
|
|
5260
|
+
imageProjection: { sessionId: request.sessionId, referenceItem: this.options.referenceItem }
|
|
5261
|
+
} : {}
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
5264
|
+
)));
|
|
5265
|
+
itemCount = page.items.length;
|
|
5266
|
+
if (diagnostics.activeMs >= 1e3) {
|
|
5267
|
+
logger.info({
|
|
5268
|
+
...diagnosticRequest(request),
|
|
5269
|
+
...diagnostics.details(),
|
|
5270
|
+
metadataOnly: request.metadataOnly === true
|
|
5271
|
+
}, "session.timeline.page.read-slow");
|
|
5272
|
+
}
|
|
5273
|
+
if (!publications.isCurrent(request.sessionId, publication)) throw new SessionNotSharedError();
|
|
5274
|
+
const latestAssistant = request.assistantPreview === true ? [...page.items].reverse().find(isContentfulAssistant) : void 0;
|
|
5275
|
+
const preview = latestAssistant === void 0 ? void 0 : assistantPreview(
|
|
5276
|
+
latestAssistant,
|
|
5277
|
+
(item) => this.options.referenceItem(request.sessionId, item)
|
|
5278
|
+
);
|
|
5279
|
+
const latestUser = request.assistantPreview === true ? [...page.items].reverse().find((item) => item.kind === "human-message" && item.actor === "human") : void 0;
|
|
5280
|
+
const humanPreview = latestUser === void 0 ? void 0 : userPreview(latestUser);
|
|
5281
|
+
const responsePage = {
|
|
5282
|
+
...page,
|
|
5283
|
+
...preview === void 0 ? {} : { assistantPreview: preview },
|
|
5284
|
+
...humanPreview === void 0 ? {} : { userPreview: humanPreview },
|
|
5285
|
+
...request.assistantPreview === true ? { previewsComplete: !page.hasMore } : {},
|
|
5286
|
+
items: request.metadataOnly === true && request.assistantPreview === true ? page.items.map((item) => {
|
|
5287
|
+
if (item.kind === "assistant-message" || item.kind === "human-message") {
|
|
5288
|
+
return { ...item, content: "", attachments: [] };
|
|
5289
|
+
}
|
|
5290
|
+
return isContentfulAssistant(item) ? { ...item, content: "" } : item;
|
|
5291
|
+
}) : page.items
|
|
5292
|
+
};
|
|
5293
|
+
const result = {
|
|
5294
|
+
ok: true,
|
|
5295
|
+
requestId: request.requestId,
|
|
5296
|
+
machineId,
|
|
5297
|
+
sessionId: request.sessionId,
|
|
5298
|
+
page: references ? { ...responsePage, items: responsePage.items.map((item) => this.options.referenceItem(request.sessionId, item)) } : responsePage
|
|
5299
|
+
};
|
|
5300
|
+
logger.debug({
|
|
5301
|
+
requestId: request.requestId,
|
|
5302
|
+
sessionId: request.sessionId,
|
|
5303
|
+
itemCount: page.items.length
|
|
5304
|
+
}, "session.timeline.page.ready");
|
|
5305
|
+
phase = "encode";
|
|
5306
|
+
const { message, metrics } = await this.options.encoder.seal(result, request, {
|
|
5307
|
+
enabled: config.timelineCompressionEnabled === true,
|
|
5308
|
+
...scope.clientId === void 0 ? {} : { recipientClientId: scope.clientId },
|
|
5309
|
+
...config.secureMessagePacketBudget === void 0 ? {} : { packetBudget: config.secureMessagePacketBudget }
|
|
5310
|
+
});
|
|
5311
|
+
if (!scope.isCurrent()) return;
|
|
5312
|
+
if (!publications.isCurrent(request.sessionId, publication)) throw new SessionNotSharedError();
|
|
5313
|
+
phase = "emit";
|
|
5314
|
+
this.options.sendSealed(message);
|
|
5315
|
+
this.options.trafficMetrics.record(message, metrics.plaintextBytes, metrics.sealDurationMs);
|
|
5316
|
+
this.#errors.recovered(errorKey);
|
|
5317
|
+
const timingLevel = metrics.encodeDurationMs + metrics.sealDurationMs >= 1e3 ? "info" : "debug";
|
|
5318
|
+
logger[timingLevel](metrics, "transport.timeline.encoding");
|
|
5319
|
+
return;
|
|
5320
|
+
} catch (error) {
|
|
5321
|
+
if (!scope.isCurrent()) return;
|
|
5322
|
+
if (!publications.isCurrent(request.sessionId, publication)) throw new SessionNotSharedError();
|
|
5323
|
+
if (phase === "emit" || request.metadataOnly === true || request.assistantPreview === true || pageSize <= 25 || !(error instanceof TimelinePageBudgetError || error instanceof SecureMessageBudgetError) || itemCount !== void 0 && itemCount <= 1) throw error;
|
|
5324
|
+
const nextPageSize = Math.max(25, Math.floor(pageSize / 2));
|
|
5325
|
+
logger.info(
|
|
5326
|
+
{ ...diagnosticRequest(request), pageSize, nextPageSize, phase },
|
|
5327
|
+
"session.timeline.page.resized"
|
|
5328
|
+
);
|
|
5329
|
+
pageSize = nextPageSize;
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
4620
5332
|
} catch (error) {
|
|
4621
5333
|
if (!scope.isCurrent()) return;
|
|
4622
5334
|
this.#errors.failure(errorKey, error, {
|
|
@@ -4630,7 +5342,7 @@ var SessionTimelinePageHandler = class {
|
|
|
4630
5342
|
machineId,
|
|
4631
5343
|
sessionId: request.sessionId,
|
|
4632
5344
|
error: sessionRestoreFailure(error) ?? {
|
|
4633
|
-
code: error instanceof SecureMessageBudgetError ? "TRANSPORT_BUDGET_EXCEEDED" : operationErrorCode(adapter.agentId, "TIMELINE_PAGE", error),
|
|
5345
|
+
code: error instanceof SecureMessageBudgetError || error instanceof TimelinePageBudgetError ? "TRANSPORT_BUDGET_EXCEEDED" : operationErrorCode(adapter.agentId, "TIMELINE_PAGE", error),
|
|
4634
5346
|
message: toErrorDetails(error).message
|
|
4635
5347
|
}
|
|
4636
5348
|
});
|
|
@@ -4643,8 +5355,8 @@ import { randomUUID as randomUUID5 } from "node:crypto";
|
|
|
4643
5355
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4644
5356
|
import { Role, TaskState } from "@a2a-js/sdk";
|
|
4645
5357
|
import { ClientFactory, DefaultAgentCardResolver, JsonRpcTransportFactory } from "@a2a-js/sdk/client";
|
|
4646
|
-
import { z as
|
|
4647
|
-
var TextSchema =
|
|
5358
|
+
import { z as z7 } from "zod";
|
|
5359
|
+
var TextSchema = z7.string().max(16e3).refine((text) => text.trim().length > 0);
|
|
4648
5360
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
4649
5361
|
var MAX_RESPONSE_BYTES = 262144;
|
|
4650
5362
|
function relayEndpoint(relayUrl, target) {
|
|
@@ -4803,7 +5515,7 @@ var A2aPeerClient = class {
|
|
|
4803
5515
|
};
|
|
4804
5516
|
|
|
4805
5517
|
// src/a2a-server.ts
|
|
4806
|
-
import { createHash as
|
|
5518
|
+
import { createHash as createHash11, randomUUID as randomUUID6 } from "node:crypto";
|
|
4807
5519
|
import {
|
|
4808
5520
|
AgentCard,
|
|
4809
5521
|
Role as Role2,
|
|
@@ -4829,7 +5541,7 @@ import {
|
|
|
4829
5541
|
ServerCallContext,
|
|
4830
5542
|
validateVersion
|
|
4831
5543
|
} from "@a2a-js/sdk/server";
|
|
4832
|
-
import { z as
|
|
5544
|
+
import { z as z8 } from "zod";
|
|
4833
5545
|
var MAX_ACTIVE = 4;
|
|
4834
5546
|
var MAX_TASKS = 128;
|
|
4835
5547
|
var MAX_RECEIPTS = 256;
|
|
@@ -4838,52 +5550,52 @@ var TURN_TIMEOUT_MS = 12e4;
|
|
|
4838
5550
|
var SHORT_REQUEST_TIMEOUT_MS = 9e3;
|
|
4839
5551
|
var MAX_TEXT2 = 16e3;
|
|
4840
5552
|
var TASK_WIRE_BUDGET = 6e4;
|
|
4841
|
-
var idSchema =
|
|
4842
|
-
var optionalIdSchema =
|
|
4843
|
-
var historySchema =
|
|
4844
|
-
var emptyMetadataSchema =
|
|
4845
|
-
var tenantSchema =
|
|
4846
|
-
var rpcSchema =
|
|
4847
|
-
jsonrpc:
|
|
4848
|
-
id:
|
|
4849
|
-
method:
|
|
4850
|
-
params:
|
|
5553
|
+
var idSchema = z8.string().min(1).max(256).regex(/^[^\s\u0000-\u001f\u007f]+$/u);
|
|
5554
|
+
var optionalIdSchema = z8.union([z8.literal(""), idSchema]).optional();
|
|
5555
|
+
var historySchema = z8.number().int().min(0).max(128).optional();
|
|
5556
|
+
var emptyMetadataSchema = z8.object({}).strict().optional();
|
|
5557
|
+
var tenantSchema = z8.literal("").optional();
|
|
5558
|
+
var rpcSchema = z8.object({
|
|
5559
|
+
jsonrpc: z8.literal("2.0"),
|
|
5560
|
+
id: z8.union([idSchema, z8.number().int().safe(), z8.null()]),
|
|
5561
|
+
method: z8.string().min(1).max(128),
|
|
5562
|
+
params: z8.unknown()
|
|
4851
5563
|
}).strict();
|
|
4852
|
-
var sendSchema =
|
|
5564
|
+
var sendSchema = z8.object({
|
|
4853
5565
|
tenant: tenantSchema,
|
|
4854
|
-
message:
|
|
5566
|
+
message: z8.object({
|
|
4855
5567
|
messageId: idSchema,
|
|
4856
5568
|
contextId: optionalIdSchema,
|
|
4857
5569
|
taskId: optionalIdSchema,
|
|
4858
|
-
role:
|
|
4859
|
-
parts:
|
|
4860
|
-
text:
|
|
4861
|
-
mediaType:
|
|
4862
|
-
filename:
|
|
5570
|
+
role: z8.union([z8.literal("ROLE_USER"), z8.literal(Role2.ROLE_USER)]),
|
|
5571
|
+
parts: z8.array(z8.object({
|
|
5572
|
+
text: z8.string().max(MAX_TEXT2),
|
|
5573
|
+
mediaType: z8.union([z8.literal(""), z8.literal("text/plain")]).optional(),
|
|
5574
|
+
filename: z8.literal("").optional(),
|
|
4863
5575
|
metadata: emptyMetadataSchema
|
|
4864
5576
|
}).strict()).min(1).max(32),
|
|
4865
5577
|
metadata: emptyMetadataSchema,
|
|
4866
|
-
extensions:
|
|
4867
|
-
referenceTaskIds:
|
|
5578
|
+
extensions: z8.array(z8.never()).optional(),
|
|
5579
|
+
referenceTaskIds: z8.array(z8.never()).optional()
|
|
4868
5580
|
}).strict().refine((message) => {
|
|
4869
5581
|
const text = message.parts.map((part) => part.text).join("\n");
|
|
4870
5582
|
return text.trim().length > 0 && text.length <= MAX_TEXT2;
|
|
4871
5583
|
}, "A message must contain nonempty plain text of at most 16000 characters."),
|
|
4872
|
-
configuration:
|
|
4873
|
-
acceptedOutputModes:
|
|
4874
|
-
returnImmediately:
|
|
5584
|
+
configuration: z8.object({
|
|
5585
|
+
acceptedOutputModes: z8.array(z8.literal("text/plain")).max(1).optional(),
|
|
5586
|
+
returnImmediately: z8.boolean().optional(),
|
|
4875
5587
|
historyLength: historySchema
|
|
4876
5588
|
}).strict().optional(),
|
|
4877
5589
|
metadata: emptyMetadataSchema
|
|
4878
5590
|
}).strict();
|
|
4879
|
-
var getSchema =
|
|
4880
|
-
var cancelSchema =
|
|
4881
|
-
var listSchema2 =
|
|
5591
|
+
var getSchema = z8.object({ tenant: tenantSchema, id: idSchema, historyLength: historySchema }).strict();
|
|
5592
|
+
var cancelSchema = z8.object({ tenant: tenantSchema, id: idSchema, metadata: emptyMetadataSchema }).strict();
|
|
5593
|
+
var listSchema2 = z8.object({
|
|
4882
5594
|
tenant: tenantSchema,
|
|
4883
5595
|
contextId: optionalIdSchema,
|
|
4884
|
-
status:
|
|
4885
|
-
|
|
4886
|
-
|
|
5596
|
+
status: z8.union([
|
|
5597
|
+
z8.number().int().min(0).max(8),
|
|
5598
|
+
z8.enum([
|
|
4887
5599
|
"TASK_STATE_UNSPECIFIED",
|
|
4888
5600
|
"TASK_STATE_SUBMITTED",
|
|
4889
5601
|
"TASK_STATE_WORKING",
|
|
@@ -4895,11 +5607,11 @@ var listSchema2 = z6.object({
|
|
|
4895
5607
|
"TASK_STATE_AUTH_REQUIRED"
|
|
4896
5608
|
])
|
|
4897
5609
|
]).optional(),
|
|
4898
|
-
pageSize:
|
|
4899
|
-
pageToken:
|
|
5610
|
+
pageSize: z8.number().int().min(1).max(100).optional(),
|
|
5611
|
+
pageToken: z8.string().regex(/^(?:|0|[1-9]\d{0,3})$/u).optional(),
|
|
4900
5612
|
historyLength: historySchema,
|
|
4901
|
-
statusTimestampAfter:
|
|
4902
|
-
includeArtifacts:
|
|
5613
|
+
statusTimestampAfter: z8.iso.datetime({ offset: true }).optional(),
|
|
5614
|
+
includeArtifacts: z8.boolean().optional()
|
|
4903
5615
|
}).strict();
|
|
4904
5616
|
function deferred() {
|
|
4905
5617
|
let resolve5 = () => {
|
|
@@ -5105,7 +5817,7 @@ var A2aPeerServer = class {
|
|
|
5105
5817
|
if (message === void 0) throw new RequestMalformedError("A message is required.");
|
|
5106
5818
|
const scope = scopedOwner(context);
|
|
5107
5819
|
const key = JSON.stringify([scope, message.messageId]);
|
|
5108
|
-
const fingerprint =
|
|
5820
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify(SendMessageRequest.toJSON(params))).digest("hex");
|
|
5109
5821
|
const previous = this.#receipts.get(key);
|
|
5110
5822
|
if (previous !== void 0) {
|
|
5111
5823
|
if (previous.fingerprint !== fingerprint) throw new RequestMalformedError("messageId was already used with different content.");
|
|
@@ -5980,7 +6692,7 @@ var SessionInventory = class {
|
|
|
5980
6692
|
};
|
|
5981
6693
|
|
|
5982
6694
|
// src/snapshot-audience.ts
|
|
5983
|
-
import { createHash as
|
|
6695
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
5984
6696
|
var SnapshotAudience = class {
|
|
5985
6697
|
constructor(send) {
|
|
5986
6698
|
this.send = send;
|
|
@@ -6009,9 +6721,9 @@ var SnapshotAudience = class {
|
|
|
6009
6721
|
async publish(snapshot, recipient) {
|
|
6010
6722
|
const generation = this.#generation;
|
|
6011
6723
|
const { sequence: _sequence, generatedAt: _generatedAt, ...content } = snapshot;
|
|
6012
|
-
const
|
|
6724
|
+
const digest2 = createHash12("sha256").update(JSON.stringify(content)).digest("base64url");
|
|
6013
6725
|
if (recipient !== void 0) {
|
|
6014
|
-
await this.#direct(snapshot, recipient,
|
|
6726
|
+
await this.#direct(snapshot, recipient, digest2, generation);
|
|
6015
6727
|
return;
|
|
6016
6728
|
}
|
|
6017
6729
|
if (!this.#negotiated || this.#registrationBaseline) {
|
|
@@ -6023,10 +6735,10 @@ var SnapshotAudience = class {
|
|
|
6023
6735
|
}
|
|
6024
6736
|
const pending = [];
|
|
6025
6737
|
for (const [clientId, baseline] of this.#audiences) {
|
|
6026
|
-
if (baseline.digest ===
|
|
6738
|
+
if (baseline.digest === digest2) {
|
|
6027
6739
|
this.#metrics.unchangedRecipients += 1;
|
|
6028
6740
|
} else {
|
|
6029
|
-
pending.push(this.#direct(snapshot, clientId,
|
|
6741
|
+
pending.push(this.#direct(snapshot, clientId, digest2, generation));
|
|
6030
6742
|
}
|
|
6031
6743
|
}
|
|
6032
6744
|
if (this.#audiences.size === 0) this.#metrics.withoutRecipients += 1;
|
|
@@ -6039,12 +6751,12 @@ var SnapshotAudience = class {
|
|
|
6039
6751
|
this.#metrics = this.#emptyMetrics();
|
|
6040
6752
|
return metrics;
|
|
6041
6753
|
}
|
|
6042
|
-
async #direct(snapshot, recipient,
|
|
6754
|
+
async #direct(snapshot, recipient, digest2, generation) {
|
|
6043
6755
|
const baseline = this.#audiences.get(recipient);
|
|
6044
6756
|
this.#metrics.directedAttempts += 1;
|
|
6045
6757
|
await this.send(snapshot, recipient);
|
|
6046
6758
|
if (generation === this.#generation && baseline !== void 0 && this.#audiences.get(recipient) === baseline) {
|
|
6047
|
-
baseline.digest =
|
|
6759
|
+
baseline.digest = digest2;
|
|
6048
6760
|
}
|
|
6049
6761
|
}
|
|
6050
6762
|
#emptyMetrics() {
|
|
@@ -6141,106 +6853,8 @@ var ConversationAudience = class {
|
|
|
6141
6853
|
};
|
|
6142
6854
|
|
|
6143
6855
|
// src/timeline-incremental-transport.ts
|
|
6144
|
-
import { createHash as
|
|
6856
|
+
import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto";
|
|
6145
6857
|
import { isDeepStrictEqual } from "node:util";
|
|
6146
|
-
|
|
6147
|
-
// src/timeline-image-cache.ts
|
|
6148
|
-
import { createHash as createHash11 } from "node:crypto";
|
|
6149
|
-
var TimelineAssetError = class extends Error {
|
|
6150
|
-
constructor(code, message) {
|
|
6151
|
-
super(message);
|
|
6152
|
-
this.code = code;
|
|
6153
|
-
}
|
|
6154
|
-
code;
|
|
6155
|
-
};
|
|
6156
|
-
var TimelineImageCache = class {
|
|
6157
|
-
constructor(maxBytes = 32 * 1024 * 1024) {
|
|
6158
|
-
this.maxBytes = maxBytes;
|
|
6159
|
-
if (!Number.isSafeInteger(maxBytes) || maxBytes < TIMELINE_IMAGE_MAX_BYTES) {
|
|
6160
|
-
throw new Error("Image cache must hold at least one maximum-size image");
|
|
6161
|
-
}
|
|
6162
|
-
}
|
|
6163
|
-
maxBytes;
|
|
6164
|
-
#assets = /* @__PURE__ */ new Map();
|
|
6165
|
-
#bytes = 0;
|
|
6166
|
-
get bytes() {
|
|
6167
|
-
return this.#bytes;
|
|
6168
|
-
}
|
|
6169
|
-
referenceItem(sessionId, item) {
|
|
6170
|
-
if (!("attachments" in item) || item.attachments === void 0) return item;
|
|
6171
|
-
return { ...item, attachments: item.attachments.map((attachment) => this.#reference(sessionId, attachment)) };
|
|
6172
|
-
}
|
|
6173
|
-
read(sessionId, request) {
|
|
6174
|
-
const key = JSON.stringify([sessionId, request.sha256]);
|
|
6175
|
-
const asset = this.#assets.get(key);
|
|
6176
|
-
if (asset === void 0) throw new TimelineAssetError("IMAGE_ABSENT", "The image is no longer cached on this PC");
|
|
6177
|
-
if (request.offset >= asset.bytes.length) {
|
|
6178
|
-
throw new TimelineAssetError("IMAGE_OFFSET_INVALID", "The image chunk offset exceeds its length");
|
|
6179
|
-
}
|
|
6180
|
-
this.#assets.delete(key);
|
|
6181
|
-
this.#assets.set(key, asset);
|
|
6182
|
-
const end = Math.min(request.offset + TIMELINE_IMAGE_CHUNK_BYTES, asset.bytes.length);
|
|
6183
|
-
return TimelineImageChunkSchema.parse({
|
|
6184
|
-
sha256: request.sha256,
|
|
6185
|
-
offset: request.offset,
|
|
6186
|
-
totalBytes: asset.bytes.length,
|
|
6187
|
-
mimeType: asset.mimeType,
|
|
6188
|
-
data: asset.bytes.subarray(request.offset, end).toString("base64"),
|
|
6189
|
-
eof: end === asset.bytes.length
|
|
6190
|
-
});
|
|
6191
|
-
}
|
|
6192
|
-
clearSession(sessionId) {
|
|
6193
|
-
const prefix = JSON.stringify([sessionId]).slice(0, -1) + ",";
|
|
6194
|
-
for (const [key, asset] of this.#assets) {
|
|
6195
|
-
if (key.startsWith(prefix)) {
|
|
6196
|
-
this.#assets.delete(key);
|
|
6197
|
-
this.#bytes -= asset.bytes.length;
|
|
6198
|
-
}
|
|
6199
|
-
}
|
|
6200
|
-
}
|
|
6201
|
-
clear() {
|
|
6202
|
-
this.#assets.clear();
|
|
6203
|
-
this.#bytes = 0;
|
|
6204
|
-
}
|
|
6205
|
-
#reference(sessionId, attachment) {
|
|
6206
|
-
if (attachment.kind !== "image" || attachment.data === void 0) return attachment;
|
|
6207
|
-
const parsed = ConversationAttachmentSchema.parse(attachment);
|
|
6208
|
-
const bytes = Buffer.from(attachment.data, "base64");
|
|
6209
|
-
if (bytes.length !== parsed.byteLength || createHash11("sha256").update(bytes).digest("hex") !== parsed.sha256 || !hasSignature(bytes, parsed.mimeType)) {
|
|
6210
|
-
throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its metadata");
|
|
6211
|
-
}
|
|
6212
|
-
const key = JSON.stringify([sessionId, parsed.sha256]);
|
|
6213
|
-
const previous = this.#assets.get(key);
|
|
6214
|
-
if (previous !== void 0) this.#bytes -= previous.bytes.length;
|
|
6215
|
-
this.#assets.delete(key);
|
|
6216
|
-
this.#assets.set(key, { bytes, mimeType: parsed.mimeType ?? "" });
|
|
6217
|
-
this.#bytes += bytes.length;
|
|
6218
|
-
while (this.#bytes > this.maxBytes) {
|
|
6219
|
-
const oldest = this.#assets.entries().next().value;
|
|
6220
|
-
if (oldest === void 0) break;
|
|
6221
|
-
this.#assets.delete(oldest[0]);
|
|
6222
|
-
this.#bytes -= oldest[1].bytes.length;
|
|
6223
|
-
}
|
|
6224
|
-
const { data: _data, ...metadata } = parsed;
|
|
6225
|
-
return { ...metadata, imageRef: "sha256" };
|
|
6226
|
-
}
|
|
6227
|
-
};
|
|
6228
|
-
function hasSignature(bytes, mimeType) {
|
|
6229
|
-
switch (mimeType) {
|
|
6230
|
-
case "image/png":
|
|
6231
|
-
return bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
6232
|
-
case "image/jpeg":
|
|
6233
|
-
return bytes.subarray(0, 3).equals(Buffer.from([255, 216, 255]));
|
|
6234
|
-
case "image/gif":
|
|
6235
|
-
return ["GIF87a", "GIF89a"].includes(bytes.subarray(0, 6).toString("ascii"));
|
|
6236
|
-
case "image/webp":
|
|
6237
|
-
return bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP";
|
|
6238
|
-
default:
|
|
6239
|
-
return false;
|
|
6240
|
-
}
|
|
6241
|
-
}
|
|
6242
|
-
|
|
6243
|
-
// src/timeline-incremental-transport.ts
|
|
6244
6858
|
function journalDiagnostic(state) {
|
|
6245
6859
|
return {
|
|
6246
6860
|
currentSequence: state.sequence,
|
|
@@ -6373,7 +6987,7 @@ var TimelineIncrementalTransport = class {
|
|
|
6373
6987
|
}
|
|
6374
6988
|
#observeColdStep(state, upsert) {
|
|
6375
6989
|
const cold = state.coldSteps;
|
|
6376
|
-
const key =
|
|
6990
|
+
const key = createHash13("sha256").update(JSON.stringify([upsert.activityId, upsert.step.id])).digest("hex");
|
|
6377
6991
|
const revision = cold.revisions.get(key);
|
|
6378
6992
|
if (revision !== void 0 && upsert.revision < revision) return false;
|
|
6379
6993
|
cold.revisions.delete(key);
|
|
@@ -6383,7 +6997,7 @@ var TimelineIncrementalTransport = class {
|
|
|
6383
6997
|
if (oldest === void 0) break;
|
|
6384
6998
|
cold.revisions.delete(oldest);
|
|
6385
6999
|
}
|
|
6386
|
-
const fingerprint =
|
|
7000
|
+
const fingerprint = createHash13("sha256").update(JSON.stringify(upsert)).digest("hex");
|
|
6387
7001
|
const observed = cold.fingerprints.has(fingerprint);
|
|
6388
7002
|
cold.fingerprints.delete(fingerprint);
|
|
6389
7003
|
cold.fingerprints.add(fingerprint);
|
|
@@ -6664,6 +7278,104 @@ function canAppend(previous, item) {
|
|
|
6664
7278
|
return isDeepStrictEqual(oldMetadata, newMetadata);
|
|
6665
7279
|
}
|
|
6666
7280
|
|
|
7281
|
+
// src/timeline-image-blob.ts
|
|
7282
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
7283
|
+
import sharp3 from "sharp";
|
|
7284
|
+
var TimelineImageBlobPublisher = class {
|
|
7285
|
+
constructor(images, uploader) {
|
|
7286
|
+
this.images = images;
|
|
7287
|
+
this.uploader = uploader;
|
|
7288
|
+
}
|
|
7289
|
+
images;
|
|
7290
|
+
uploader;
|
|
7291
|
+
#references = /* @__PURE__ */ new Map();
|
|
7292
|
+
#flights = /* @__PURE__ */ new Map();
|
|
7293
|
+
#closed = false;
|
|
7294
|
+
async publish(sessionId, sha256, assertCurrent) {
|
|
7295
|
+
TimelineImageBlobRequestSchema.parse({ sha256 });
|
|
7296
|
+
assertCurrent();
|
|
7297
|
+
if (this.#closed) throw new TimelineAssetError("IMAGE_BLOB_CLOSED", "The image publisher is closed");
|
|
7298
|
+
const key = JSON.stringify([sessionId, sha256]);
|
|
7299
|
+
const cached = this.#references.get(key);
|
|
7300
|
+
if (cached !== void 0) {
|
|
7301
|
+
this.#references.delete(key);
|
|
7302
|
+
if (cached.image.expiresAt > Date.now() + 3e4) {
|
|
7303
|
+
this.#references.set(key, cached);
|
|
7304
|
+
return cached;
|
|
7305
|
+
}
|
|
7306
|
+
}
|
|
7307
|
+
let flight = this.#flights.get(key);
|
|
7308
|
+
if (flight === void 0) {
|
|
7309
|
+
if (this.#flights.size >= 3) throw new TimelineAssetError("IMAGE_BLOB_BUSY", "Too many image uploads");
|
|
7310
|
+
const controller = new AbortController();
|
|
7311
|
+
const check = () => {
|
|
7312
|
+
controller.signal.throwIfAborted();
|
|
7313
|
+
assertCurrent();
|
|
7314
|
+
};
|
|
7315
|
+
const promise = this.#publish(sessionId, sha256, controller.signal, check).then((result2) => {
|
|
7316
|
+
check();
|
|
7317
|
+
this.#references.set(key, result2);
|
|
7318
|
+
while (this.#references.size > 256) {
|
|
7319
|
+
const oldest = this.#references.keys().next().value;
|
|
7320
|
+
if (oldest === void 0) break;
|
|
7321
|
+
this.#references.delete(oldest);
|
|
7322
|
+
}
|
|
7323
|
+
return result2;
|
|
7324
|
+
}).finally(() => {
|
|
7325
|
+
this.#flights.delete(key);
|
|
7326
|
+
});
|
|
7327
|
+
flight = { sessionId, controller, promise };
|
|
7328
|
+
this.#flights.set(key, flight);
|
|
7329
|
+
}
|
|
7330
|
+
const result = await flight.promise;
|
|
7331
|
+
assertCurrent();
|
|
7332
|
+
return result;
|
|
7333
|
+
}
|
|
7334
|
+
clearSession(sessionId) {
|
|
7335
|
+
for (const [key, reference] of this.#references) {
|
|
7336
|
+
if (reference.image.sessionId === sessionId) this.#references.delete(key);
|
|
7337
|
+
}
|
|
7338
|
+
for (const flight of this.#flights.values()) {
|
|
7339
|
+
if (flight.sessionId === sessionId) flight.controller.abort();
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
reset() {
|
|
7343
|
+
this.#references.clear();
|
|
7344
|
+
for (const flight of this.#flights.values()) flight.controller.abort();
|
|
7345
|
+
}
|
|
7346
|
+
async close() {
|
|
7347
|
+
this.#closed = true;
|
|
7348
|
+
this.reset();
|
|
7349
|
+
await Promise.allSettled([...this.#flights.values()].map((flight) => flight.promise));
|
|
7350
|
+
}
|
|
7351
|
+
async #publish(sessionId, sha256, signal, check) {
|
|
7352
|
+
const asset = this.images.readAsset(sessionId, sha256);
|
|
7353
|
+
const extension = { "image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp" }[asset.mimeType];
|
|
7354
|
+
if (extension === void 0) throw new TimelineAssetError("IMAGE_BLOB_UNSUPPORTED", "This image requires encrypted transfer");
|
|
7355
|
+
if (createHash14("sha256").update(asset.bytes).digest("hex") !== sha256) {
|
|
7356
|
+
throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its reference");
|
|
7357
|
+
}
|
|
7358
|
+
let bytes;
|
|
7359
|
+
try {
|
|
7360
|
+
await validateRawImage(asset.bytes, extension, check);
|
|
7361
|
+
bytes = extension === ".webp" ? asset.bytes : await sharp3(asset.bytes, { limitInputPixels: SESSION_IMAGE_MAX_PIXELS, failOn: "warning" }).rotate().webp({ lossless: true, effort: 3 }).timeout({ seconds: 10 }).toBuffer();
|
|
7362
|
+
} catch (error) {
|
|
7363
|
+
check();
|
|
7364
|
+
if (error instanceof SessionFileReadError) {
|
|
7365
|
+
throw new TimelineAssetError("IMAGE_BLOB_UNSUPPORTED", "The image cannot be safely stored as WebP", { cause: error });
|
|
7366
|
+
}
|
|
7367
|
+
throw new TimelineAssetError("IMAGE_BLOB_ENCODING_FAILED", "Unable to encode the image for storage", { cause: error });
|
|
7368
|
+
}
|
|
7369
|
+
check();
|
|
7370
|
+
if (bytes.length > SESSION_IMAGE_MAX_BYTES) {
|
|
7371
|
+
throw new TimelineAssetError("IMAGE_BLOB_TOO_LARGE", "The lossless WebP exceeds the image storage limit");
|
|
7372
|
+
}
|
|
7373
|
+
const image = await this.uploader.upload(sessionId, bytes, signal, check);
|
|
7374
|
+
check();
|
|
7375
|
+
return TimelineImageBlobResultSchema.parse({ sha256, image });
|
|
7376
|
+
}
|
|
7377
|
+
};
|
|
7378
|
+
|
|
6667
7379
|
// src/secure-traffic-metrics.ts
|
|
6668
7380
|
var SecureTrafficMetrics = class {
|
|
6669
7381
|
#samples = /* @__PURE__ */ new Map();
|
|
@@ -7160,7 +7872,7 @@ var INVENTORY_REFRESH_DEBOUNCE_MS = 100;
|
|
|
7160
7872
|
var SESSION_LIST_REFRESH_RETRIES = 2;
|
|
7161
7873
|
var TRAFFIC_METRICS_INTERVAL_MS = 6e4;
|
|
7162
7874
|
var PcAgent = class {
|
|
7163
|
-
constructor(config, identity2, appVersion, adapter, logger, pairingProvider, contentCipher, research, createSocket =
|
|
7875
|
+
constructor(config, identity2, appVersion, adapter, logger, pairingProvider, contentCipher, research, createSocket = createRelaySocket, onPairing) {
|
|
7164
7876
|
this.config = config;
|
|
7165
7877
|
this.identity = identity2;
|
|
7166
7878
|
this.appVersion = appVersion;
|
|
@@ -7206,8 +7918,19 @@ var PcAgent = class {
|
|
|
7206
7918
|
stopSynchronizing: (clientId, sessionId) => this.#conversationAudience.stopSynchronizing(clientId, sessionId)
|
|
7207
7919
|
});
|
|
7208
7920
|
this.#timelineIncremental = new TimelineIncrementalTransport({
|
|
7209
|
-
readHead: (sessionId) => this.adapter.readSessionTimelinePage(sessionId, {
|
|
7921
|
+
readHead: (sessionId) => this.adapter.readSessionTimelinePage(sessionId, {
|
|
7922
|
+
pageSize: 100,
|
|
7923
|
+
imageProjection: { sessionId, referenceItem: this.#timelineIncremental.images.referenceItem }
|
|
7924
|
+
})
|
|
7210
7925
|
});
|
|
7926
|
+
const imageSettings = config.sessionImages === void 0 ? void 0 : SessionImagesConfigSchema.parse(config.sessionImages);
|
|
7927
|
+
this.#imageBlob = imageSettings === void 0 ? void 0 : new TimelineImageBlobPublisher(this.#timelineIncremental.images, new SessionImageUploader({
|
|
7928
|
+
relayUrl: config.relayUrl,
|
|
7929
|
+
relayToken: config.relayToken,
|
|
7930
|
+
userId: config.userId,
|
|
7931
|
+
machineId: identity2.machineId,
|
|
7932
|
+
...imageSettings
|
|
7933
|
+
}));
|
|
7211
7934
|
const emit = (event, payload) => this.#emitSecure(event, payload);
|
|
7212
7935
|
const handlerOptions = { adapter, config, machineId: identity2.machineId, logger, emit };
|
|
7213
7936
|
this.#communication = new SessionCommunicationHandler({
|
|
@@ -7218,6 +7941,7 @@ var PcAgent = class {
|
|
|
7218
7941
|
audience: this.#conversationAudience,
|
|
7219
7942
|
metrics: this.#operationMetrics,
|
|
7220
7943
|
incrementalOperations: this.#timelineIncremental.operations,
|
|
7944
|
+
...this.#imageBlob === void 0 ? {} : { imageBlob: this.#imageBlob },
|
|
7221
7945
|
captureResearchAccess: (sessionId) => captureResearchAccess(adapter, sessionId),
|
|
7222
7946
|
captureWorkbenchAccess: (sessionId) => captureWorkbenchAccess(adapter, sessionId),
|
|
7223
7947
|
...research === void 0 ? {} : { research },
|
|
@@ -7244,6 +7968,7 @@ var PcAgent = class {
|
|
|
7244
7968
|
supportsAgentControls: (clientId) => this.#supportsAgentControls(clientId),
|
|
7245
7969
|
refreshSessions: (clientId) => this.#inventoryPublisher.request(clientId, true),
|
|
7246
7970
|
removeSession: (sessionId) => {
|
|
7971
|
+
this.#imageBlob?.clearSession(sessionId);
|
|
7247
7972
|
this.#timelineIncremental.remove(sessionId);
|
|
7248
7973
|
this.#conversationAudience.removeSession(sessionId);
|
|
7249
7974
|
this.#publication.revoke(sessionId);
|
|
@@ -7266,7 +7991,7 @@ var PcAgent = class {
|
|
|
7266
7991
|
trafficMetrics: this.#trafficMetrics,
|
|
7267
7992
|
sendSealed: (message) => this.#socket.emit("secure.message", message),
|
|
7268
7993
|
imageReferencesEnabled: () => this.#communicationNegotiated && this.#conversationAudience.confirmed,
|
|
7269
|
-
referenceItem:
|
|
7994
|
+
referenceItem: this.#timelineIncremental.images.referenceItem
|
|
7270
7995
|
});
|
|
7271
7996
|
this.#secureRequestRouter = createPcSecureRequestRouter({
|
|
7272
7997
|
configuredMachineId: identity2.machineId,
|
|
@@ -7424,6 +8149,7 @@ var PcAgent = class {
|
|
|
7424
8149
|
},
|
|
7425
8150
|
onTimelineInvalidated: (sessionId, invalidation) => {
|
|
7426
8151
|
if (this.adapter.isSessionVisible?.(sessionId) === false) return;
|
|
8152
|
+
this.#imageBlob?.clearSession(sessionId);
|
|
7427
8153
|
this.#publication.capture(sessionId);
|
|
7428
8154
|
const envelope = SessionTimelineInvalidatedSchema.parse({
|
|
7429
8155
|
userId: this.config.userId,
|
|
@@ -7573,6 +8299,7 @@ var PcAgent = class {
|
|
|
7573
8299
|
#readMetrics = { readStarts: 0, readJoins: 0, untrackedReads: 0, peakInFlightPerSession: 0 };
|
|
7574
8300
|
#conversationAudience = new ConversationAudience();
|
|
7575
8301
|
#timelineIncremental;
|
|
8302
|
+
#imageBlob;
|
|
7576
8303
|
#pendingAcks = /* @__PURE__ */ new Set();
|
|
7577
8304
|
#connectionGeneration = 0;
|
|
7578
8305
|
#heartbeatTimer;
|
|
@@ -7833,6 +8560,7 @@ var PcAgent = class {
|
|
|
7833
8560
|
return this.#stopped;
|
|
7834
8561
|
}
|
|
7835
8562
|
this.#stopping = true;
|
|
8563
|
+
const imagesStopped = this.#imageBlob?.close();
|
|
7836
8564
|
const peerStopped = this.#peerChat.close();
|
|
7837
8565
|
const a2aStopped = this.#a2aServer.close();
|
|
7838
8566
|
this.#machineRegistered = false;
|
|
@@ -7859,6 +8587,7 @@ var PcAgent = class {
|
|
|
7859
8587
|
this.adapter.stop(),
|
|
7860
8588
|
peerStopped,
|
|
7861
8589
|
a2aStopped,
|
|
8590
|
+
...imagesStopped === void 0 ? [] : [imagesStopped],
|
|
7862
8591
|
...this.research === void 0 ? [] : [this.research.stop()]
|
|
7863
8592
|
]);
|
|
7864
8593
|
const operationsStopped = await Promise.allSettled(
|
|
@@ -7915,6 +8644,7 @@ var PcAgent = class {
|
|
|
7915
8644
|
this.#peerTransportNegotiated = false;
|
|
7916
8645
|
this.#a2aNegotiated = false;
|
|
7917
8646
|
this.#peerChat.reset(new Error("Relay disconnected; peer conversation will not be replayed."));
|
|
8647
|
+
this.#imageBlob?.reset();
|
|
7918
8648
|
this.#a2aServer.reset(new Error("Relay disconnected; A2A execution will not be replayed."));
|
|
7919
8649
|
this.#conversationAudience.reset();
|
|
7920
8650
|
this.#cancelSnapshotSync();
|
|
@@ -8187,6 +8917,7 @@ var PcAgent = class {
|
|
|
8187
8917
|
}
|
|
8188
8918
|
#machineCapabilities() {
|
|
8189
8919
|
return [
|
|
8920
|
+
...this.adapter.agentId === "copilot" || this.adapter.agentId === "managed" ? [SESSION_AVATAR_PERSONA_CAPABILITY] : [],
|
|
8190
8921
|
SESSION_COMMUNICATION_CAPABILITY,
|
|
8191
8922
|
...this.#peerChatEnabled && this.adapter.runPeerTurn !== void 0 ? [PC_PEER_CHAT_CAPABILITY] : [],
|
|
8192
8923
|
...this.adapter.runPeerTurn !== void 0 ? [PC_A2A_CAPABILITY] : [],
|
|
@@ -8206,6 +8937,7 @@ var PcAgent = class {
|
|
|
8206
8937
|
if (this.adapter.isSessionVisible === void 0) return;
|
|
8207
8938
|
let removed = 0;
|
|
8208
8939
|
for (const id of this.#publication.revokeHidden()) {
|
|
8940
|
+
this.#imageBlob?.clearSession(id);
|
|
8209
8941
|
this.#timelineIncremental.remove(id);
|
|
8210
8942
|
this.#conversationAudience.removeSession(id);
|
|
8211
8943
|
removed += 1;
|
|
@@ -8699,46 +9431,46 @@ function contextItem(item) {
|
|
|
8699
9431
|
|
|
8700
9432
|
// src/research-sessions.ts
|
|
8701
9433
|
import { execFile as execFile3 } from "node:child_process";
|
|
8702
|
-
import { createHash as
|
|
9434
|
+
import { createHash as createHash15, randomUUID as randomUUID12 } from "node:crypto";
|
|
8703
9435
|
import { mkdir as mkdir5, lstat as lstat8, realpath as realpath8 } from "node:fs/promises";
|
|
8704
9436
|
import { createRequire } from "node:module";
|
|
8705
9437
|
import { join as join12, resolve as resolve4 } from "node:path";
|
|
8706
9438
|
import { promisify as promisify4 } from "node:util";
|
|
8707
|
-
import { z as
|
|
9439
|
+
import { z as z11 } from "zod";
|
|
8708
9440
|
|
|
8709
9441
|
// src/research-thread-store.ts
|
|
8710
9442
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
8711
9443
|
import { mkdir as mkdir4, readFile as readFile2, rename as rename2, rm as rm2, lstat as lstat6, realpath as realpath7, writeFile as writeFile3 } from "node:fs/promises";
|
|
8712
9444
|
import { join as join10, resolve as resolve3 } from "node:path";
|
|
8713
|
-
import { z as
|
|
9445
|
+
import { z as z9 } from "zod";
|
|
8714
9446
|
var RESEARCH_PROFILE = "research-readonly";
|
|
8715
9447
|
var RESEARCH_PROFILE_VERSION = 1;
|
|
8716
9448
|
var MAX_THREADS = 256;
|
|
8717
|
-
var recordSchema =
|
|
8718
|
-
threadId:
|
|
8719
|
-
researchSessionId:
|
|
8720
|
-
parentSessionId:
|
|
8721
|
-
operationId:
|
|
8722
|
-
fingerprint:
|
|
8723
|
-
title:
|
|
8724
|
-
profile:
|
|
8725
|
-
profileVersion:
|
|
8726
|
-
workingDirectory:
|
|
8727
|
-
rootIdentity:
|
|
8728
|
-
contextAnchor:
|
|
8729
|
-
contextSampledAt:
|
|
8730
|
-
contextTruncated:
|
|
8731
|
-
chatVersion:
|
|
8732
|
-
createdAt:
|
|
8733
|
-
state:
|
|
8734
|
-
errorCode:
|
|
9449
|
+
var recordSchema = z9.object({
|
|
9450
|
+
threadId: z9.string().uuid(),
|
|
9451
|
+
researchSessionId: z9.string().uuid(),
|
|
9452
|
+
parentSessionId: z9.string().min(1).max(256),
|
|
9453
|
+
operationId: z9.string().min(1).max(256),
|
|
9454
|
+
fingerprint: z9.string().min(1).max(128),
|
|
9455
|
+
title: z9.string().min(1).max(200),
|
|
9456
|
+
profile: z9.literal(RESEARCH_PROFILE),
|
|
9457
|
+
profileVersion: z9.literal(RESEARCH_PROFILE_VERSION),
|
|
9458
|
+
workingDirectory: z9.string().min(1).max(8192),
|
|
9459
|
+
rootIdentity: z9.string().min(1).max(128),
|
|
9460
|
+
contextAnchor: z9.string().min(1).max(256),
|
|
9461
|
+
contextSampledAt: z9.string().datetime(),
|
|
9462
|
+
contextTruncated: z9.boolean(),
|
|
9463
|
+
chatVersion: z9.literal(1).optional(),
|
|
9464
|
+
createdAt: z9.string().datetime(),
|
|
9465
|
+
state: z9.enum(["creating", "ready", "uncertain", "failed", "deleting"]),
|
|
9466
|
+
errorCode: z9.string().max(128).optional()
|
|
8735
9467
|
}).strict();
|
|
8736
|
-
var documentSchema =
|
|
8737
|
-
version:
|
|
8738
|
-
threads:
|
|
8739
|
-
tombstones:
|
|
8740
|
-
researchSessionId:
|
|
8741
|
-
parentSessionId:
|
|
9468
|
+
var documentSchema = z9.object({
|
|
9469
|
+
version: z9.literal(1),
|
|
9470
|
+
threads: z9.array(recordSchema).max(MAX_THREADS),
|
|
9471
|
+
tombstones: z9.array(z9.object({
|
|
9472
|
+
researchSessionId: z9.string().uuid(),
|
|
9473
|
+
parentSessionId: z9.string().min(1).max(256)
|
|
8742
9474
|
}).strict()).max(1e5).optional()
|
|
8743
9475
|
}).strict();
|
|
8744
9476
|
var ResearchThreadStore = class {
|
|
@@ -8875,24 +9607,24 @@ var ResearchThreadStore = class {
|
|
|
8875
9607
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
8876
9608
|
import { lstat as lstat7, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
8877
9609
|
import { join as join11 } from "node:path";
|
|
8878
|
-
import { z as
|
|
8879
|
-
var receipt =
|
|
8880
|
-
operationId:
|
|
8881
|
-
fingerprint:
|
|
8882
|
-
promptHash:
|
|
8883
|
-
ordinal:
|
|
8884
|
-
submittedAt:
|
|
8885
|
-
state:
|
|
8886
|
-
messageId:
|
|
8887
|
-
deliveryState:
|
|
9610
|
+
import { z as z10 } from "zod";
|
|
9611
|
+
var receipt = z10.object({
|
|
9612
|
+
operationId: z10.string().min(1).max(256),
|
|
9613
|
+
fingerprint: z10.string().min(1).max(128),
|
|
9614
|
+
promptHash: z10.string().min(1).max(128),
|
|
9615
|
+
ordinal: z10.number().int().nonnegative(),
|
|
9616
|
+
submittedAt: z10.string().datetime(),
|
|
9617
|
+
state: z10.enum(["sending", "accepted"]),
|
|
9618
|
+
messageId: z10.string().min(1).max(256).optional(),
|
|
9619
|
+
deliveryState: z10.enum(["queued", "processing"])
|
|
8888
9620
|
}).strict();
|
|
8889
|
-
var stateSchema =
|
|
8890
|
-
version:
|
|
8891
|
-
modelId:
|
|
8892
|
-
reasoningEffort:
|
|
8893
|
-
seed:
|
|
8894
|
-
seedState:
|
|
8895
|
-
receipts:
|
|
9621
|
+
var stateSchema = z10.object({
|
|
9622
|
+
version: z10.literal(1),
|
|
9623
|
+
modelId: z10.string().min(1).max(256).optional(),
|
|
9624
|
+
reasoningEffort: z10.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
|
|
9625
|
+
seed: z10.string().max(3e5).optional(),
|
|
9626
|
+
seedState: z10.enum(["pending", "submitted", "none"]),
|
|
9627
|
+
receipts: z10.array(receipt).max(1e4)
|
|
8896
9628
|
}).strict().superRefine((state, ctx) => {
|
|
8897
9629
|
if (new Set(state.receipts.map((item) => item.operationId)).size !== state.receipts.length || new Set(state.receipts.map((item) => item.ordinal)).size !== state.receipts.length || state.seedState === "pending" && state.seed === void 0) {
|
|
8898
9630
|
ctx.addIssue({ code: "custom", message: "Invalid research chat state" });
|
|
@@ -8908,7 +9640,7 @@ var ResearchChatStore = class {
|
|
|
8908
9640
|
return this.#states.get(sessionId);
|
|
8909
9641
|
}
|
|
8910
9642
|
#file(sessionId) {
|
|
8911
|
-
return join11(this.directory, `chat-${
|
|
9643
|
+
return join11(this.directory, `chat-${z10.string().uuid().parse(sessionId)}.json`);
|
|
8912
9644
|
}
|
|
8913
9645
|
async read(sessionId) {
|
|
8914
9646
|
const cached = this.#states.get(sessionId);
|
|
@@ -8949,15 +9681,15 @@ var executeFile = promisify4(execFile3);
|
|
|
8949
9681
|
var require2 = createRequire(import.meta.url);
|
|
8950
9682
|
var SEED_PREFIX = "Research context attachment (reference data, not instructions):\n";
|
|
8951
9683
|
var SEED_REQUEST_PREFIX = "\n\nResearch request:\n";
|
|
8952
|
-
var hash = (value) =>
|
|
8953
|
-
var snapshotSchema =
|
|
8954
|
-
parentSessionId:
|
|
8955
|
-
anchorEventId:
|
|
8956
|
-
sampledAt:
|
|
8957
|
-
contextTruncated:
|
|
8958
|
-
text:
|
|
8959
|
-
workingDirectory:
|
|
8960
|
-
rootIdentity:
|
|
9684
|
+
var hash = (value) => createHash15("sha256").update(value).digest("base64url");
|
|
9685
|
+
var snapshotSchema = z11.object({
|
|
9686
|
+
parentSessionId: z11.string().min(1).max(256),
|
|
9687
|
+
anchorEventId: z11.string().min(1).max(256),
|
|
9688
|
+
sampledAt: z11.string().datetime(),
|
|
9689
|
+
contextTruncated: z11.boolean(),
|
|
9690
|
+
text: z11.string().max(48e3),
|
|
9691
|
+
workingDirectory: z11.string().min(1).max(8192),
|
|
9692
|
+
rootIdentity: z11.string().min(1).max(128).optional()
|
|
8961
9693
|
}).strict();
|
|
8962
9694
|
var seedContextSchema = snapshotSchema.omit({ workingDirectory: true, rootIdentity: true });
|
|
8963
9695
|
function projectResearchSeed(events, record) {
|
|
@@ -9359,9 +10091,9 @@ var ResearchSessions = class {
|
|
|
9359
10091
|
assertAccess?.();
|
|
9360
10092
|
const snapshot = snapshotSchema.parse(input.snapshot);
|
|
9361
10093
|
if (snapshot.parentSessionId !== input.parentSessionId) throw new ResearchError("RESEARCH_SOURCE_MISMATCH");
|
|
9362
|
-
const title =
|
|
9363
|
-
const prompt =
|
|
9364
|
-
const fingerprint =
|
|
10094
|
+
const title = z11.string().trim().min(1).max(200).parse(input.title ?? "Research");
|
|
10095
|
+
const prompt = z11.string().trim().min(1).max(16e3).parse(input.prompt ?? "Acknowledge the context briefly and wait for my research question.");
|
|
10096
|
+
const fingerprint = createHash15("sha256").update(JSON.stringify({
|
|
9365
10097
|
parentSessionId: input.parentSessionId,
|
|
9366
10098
|
title,
|
|
9367
10099
|
prompt,
|
|
@@ -9395,7 +10127,7 @@ var ResearchSessions = class {
|
|
|
9395
10127
|
let session;
|
|
9396
10128
|
try {
|
|
9397
10129
|
const configuration = input.configuration ?? await this.options.sourceConfiguration?.(input.parentSessionId);
|
|
9398
|
-
const reasoning =
|
|
10130
|
+
const reasoning = z11.enum(["low", "medium", "high", "xhigh", "max"]).safeParse(configuration?.reasoningEffort);
|
|
9399
10131
|
if (configuration !== void 0 && !reasoning.success && !["default", "unknown"].includes(configuration.reasoningEffort)) {
|
|
9400
10132
|
throw new ResearchError("RESEARCH_CONFIGURATION_UNSUPPORTED");
|
|
9401
10133
|
}
|
|
@@ -9519,7 +10251,7 @@ var ResearchSessions = class {
|
|
|
9519
10251
|
const assertAccess = this.#capture(record);
|
|
9520
10252
|
assertAccess();
|
|
9521
10253
|
if (options.metadataOnly === true) throw new ResearchError("RESEARCH_SESSION_ISOLATED");
|
|
9522
|
-
const pageSize =
|
|
10254
|
+
const pageSize = z11.number().int().min(1).max(SESSION_TIMELINE_PAGE_SIZE).parse(options.pageSize ?? SESSION_TIMELINE_PAGE_SIZE);
|
|
9523
10255
|
const result = await this.#state(sessionId).timelineReads.read({
|
|
9524
10256
|
...options.cursor === void 0 ? {} : { cursor: options.cursor },
|
|
9525
10257
|
pageSize,
|
|
@@ -9567,8 +10299,8 @@ var ResearchSessions = class {
|
|
|
9567
10299
|
return (await this.sendPrompt(sessionId, text, "immediate", operationId)).messageId;
|
|
9568
10300
|
}
|
|
9569
10301
|
async sendPrompt(sessionId, text, mode, operationId) {
|
|
9570
|
-
const prompt =
|
|
9571
|
-
|
|
10302
|
+
const prompt = z11.string().trim().min(1).max(16e3).parse(text);
|
|
10303
|
+
z11.string().min(1).max(256).parse(operationId);
|
|
9572
10304
|
const fingerprint = hash(JSON.stringify({ prompt, mode }));
|
|
9573
10305
|
const running = this.#sending.get(sessionId);
|
|
9574
10306
|
if (running !== void 0 && running.operationId === operationId) {
|
|
@@ -9857,21 +10589,30 @@ function createResearchWorkbench(options) {
|
|
|
9857
10589
|
}
|
|
9858
10590
|
|
|
9859
10591
|
// src/initialize.ts
|
|
9860
|
-
var ConfigSchema =
|
|
9861
|
-
dataDirectory:
|
|
9862
|
-
relayUrl:
|
|
9863
|
-
relayToken:
|
|
9864
|
-
userId:
|
|
9865
|
-
workspaceRoot:
|
|
9866
|
-
refreshIntervalMs:
|
|
9867
|
-
heartbeatIntervalMs:
|
|
9868
|
-
exitAfterSnapshot:
|
|
9869
|
-
groupChatEnabled:
|
|
9870
|
-
peerChatEnabled:
|
|
9871
|
-
snapshotSuppressionEnabled:
|
|
9872
|
-
timelineCompressionEnabled:
|
|
9873
|
-
timelineIncrementalEnabled:
|
|
9874
|
-
secureMessagePacketBudget:
|
|
10592
|
+
var ConfigSchema = z12.object({
|
|
10593
|
+
dataDirectory: z12.string().min(1),
|
|
10594
|
+
relayUrl: z12.url().refine((value) => ["https:", "http:"].includes(new URL(value).protocol)),
|
|
10595
|
+
relayToken: z12.string().min(24),
|
|
10596
|
+
userId: z12.string().trim().min(1).max(256),
|
|
10597
|
+
workspaceRoot: z12.string().optional(),
|
|
10598
|
+
refreshIntervalMs: z12.number().int().min(5e3).max(36e5).default(3e4),
|
|
10599
|
+
heartbeatIntervalMs: z12.number().int().min(5e3).max(12e4).default(15e3),
|
|
10600
|
+
exitAfterSnapshot: z12.boolean().default(false),
|
|
10601
|
+
groupChatEnabled: z12.boolean().optional(),
|
|
10602
|
+
peerChatEnabled: z12.boolean().optional(),
|
|
10603
|
+
snapshotSuppressionEnabled: z12.boolean().optional(),
|
|
10604
|
+
timelineCompressionEnabled: z12.boolean().optional(),
|
|
10605
|
+
timelineIncrementalEnabled: z12.boolean().optional(),
|
|
10606
|
+
secureMessagePacketBudget: z12.number().int().min(2048).max(SECURE_MESSAGE_MAX_CIPHERTEXT_LENGTH).optional(),
|
|
10607
|
+
sessionImages: SessionImagesConfigSchema.optional()
|
|
10608
|
+
}).superRefine((config, context) => {
|
|
10609
|
+
if (config.sessionImages !== void 0 && !SessionImageRelayUrlSchema.safeParse(config.relayUrl).success) {
|
|
10610
|
+
context.addIssue({
|
|
10611
|
+
code: "custom",
|
|
10612
|
+
path: ["relayUrl"],
|
|
10613
|
+
message: "Session images require an HTTPS Relay origin without a path, credentials, query or fragment"
|
|
10614
|
+
});
|
|
10615
|
+
}
|
|
9875
10616
|
});
|
|
9876
10617
|
async function initialize(options) {
|
|
9877
10618
|
const dataDirectory = validateDataDirectory(options.dataDirectory);
|
|
@@ -9891,10 +10632,11 @@ async function initialize(options) {
|
|
|
9891
10632
|
...parsed.timelineCompressionEnabled === void 0 ? {} : { timelineCompressionEnabled: parsed.timelineCompressionEnabled },
|
|
9892
10633
|
...parsed.timelineIncrementalEnabled === void 0 ? {} : { timelineIncrementalEnabled: parsed.timelineIncrementalEnabled },
|
|
9893
10634
|
...parsed.secureMessagePacketBudget === void 0 ? {} : { secureMessagePacketBudget: parsed.secureMessagePacketBudget },
|
|
10635
|
+
...parsed.sessionImages === void 0 ? {} : { sessionImages: parsed.sessionImages },
|
|
9894
10636
|
...options.codingAgent === void 0 ? {} : { codingAgent: options.codingAgent }
|
|
9895
10637
|
};
|
|
9896
10638
|
const identity2 = DeviceIdentitySchema.parse(options.identity);
|
|
9897
|
-
const appVersion = options.appVersion ??
|
|
10639
|
+
const appVersion = options.appVersion ?? z12.object({ version: z12.string().min(1) }).parse(JSON.parse(await readFile4(new URL("../package.json", import.meta.url), "utf8"))).version;
|
|
9898
10640
|
const lease = options.directoryLease ?? await DataDirectoryLease.acquire(dataDirectory);
|
|
9899
10641
|
let detach;
|
|
9900
10642
|
let adapter;
|
|
@@ -9950,7 +10692,7 @@ async function initialize(options) {
|
|
|
9950
10692
|
}, "research.operation.failed")
|
|
9951
10693
|
});
|
|
9952
10694
|
class InitializedAgent extends PcAgent {
|
|
9953
|
-
identityHash =
|
|
10695
|
+
identityHash = createHash16("sha256").update(`${config.userId}:${identity2.machineId}:${contentKey.keyId}`).digest("hex");
|
|
9954
10696
|
#started;
|
|
9955
10697
|
#starting;
|
|
9956
10698
|
#closed;
|
|
@@ -10024,6 +10766,11 @@ export {
|
|
|
10024
10766
|
PEER_CHAT_TIMEOUT_MS,
|
|
10025
10767
|
PeerChatRequestSchema,
|
|
10026
10768
|
PeerChatResultSchema,
|
|
10769
|
+
SessionImageRelayUrlSchema,
|
|
10770
|
+
SessionImageUploadError,
|
|
10771
|
+
SessionImageUploader,
|
|
10772
|
+
SessionImagesConfigSchema,
|
|
10773
|
+
TimelinePageBudgetError,
|
|
10027
10774
|
assertPiRuntimeSupported,
|
|
10028
10775
|
buildPairingUri,
|
|
10029
10776
|
codingAgentPresentation,
|