@agentknight/sdk 0.9.38 → 0.9.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{acp-adapter-DZ3EE36H.js → acp-adapter-BWVM6WIF.js} +3 -3
- package/dist/chunks/{chunk-SREMK7S4.js → chunk-45FBONW3.js} +1 -1
- package/dist/chunks/{chunk-6GVMVE5N.js → chunk-BM6T2DLH.js} +1 -1
- package/dist/chunks/{chunk-GNQC2IS2.js → chunk-E3XCZWRK.js} +339 -235
- package/dist/chunks/{chunk-6YF3VZLR.js → chunk-KPRPUIXR.js} +1 -1
- package/dist/chunks/{chunk-34DUD2Y4.js → chunk-N4MN54CD.js} +1 -1
- package/dist/chunks/{chunk-UIM3KQRC.js → chunk-QGPX4UDW.js} +3 -3
- package/dist/chunks/{chunk-4FC2WG64.js → chunk-UE4QYZCG.js} +1 -1
- package/dist/chunks/{chunk-YSJRQ35E.js → chunk-VCNYBWYE.js} +2 -2
- package/dist/chunks/{copilot-adapter-USX4UEWH.js → copilot-adapter-N5B7BNWO.js} +6 -6
- package/dist/chunks/{managed-agent-adapter-EMBDKITT.js → managed-agent-adapter-T3FVUJHA.js} +5 -5
- package/dist/chunks/{pi-adapter-KT5M7Z6R.js → pi-adapter-PHEFFJVF.js} +3 -3
- package/dist/index.d.ts +50 -2
- package/dist/index.js +940 -284
- package/dist/runtime.js +2 -2
- package/package.json +3 -3
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-UE4QYZCG.js";
|
|
8
8
|
import {
|
|
9
9
|
AgentSelectionSchema,
|
|
10
10
|
assertSelectionSnapshotFits,
|
|
11
11
|
supportsAgentSelection
|
|
12
|
-
} from "./chunks/chunk-
|
|
12
|
+
} from "./chunks/chunk-KPRPUIXR.js";
|
|
13
13
|
import {
|
|
14
14
|
ConversationNormalizer,
|
|
15
15
|
LiveTimelineProjection,
|
|
@@ -31,16 +31,16 @@ import {
|
|
|
31
31
|
resolveGitProjectIdentity,
|
|
32
32
|
sessionRestoreFailure,
|
|
33
33
|
validateOperationResponse
|
|
34
|
-
} from "./chunks/chunk-
|
|
34
|
+
} from "./chunks/chunk-QGPX4UDW.js";
|
|
35
35
|
import {
|
|
36
36
|
GROUP_CHAT_MAX_TURNS,
|
|
37
37
|
GroupChatError
|
|
38
|
-
} from "./chunks/chunk-
|
|
38
|
+
} from "./chunks/chunk-VCNYBWYE.js";
|
|
39
39
|
import {
|
|
40
40
|
assistantPreview,
|
|
41
41
|
isContentfulAssistant,
|
|
42
42
|
userPreview
|
|
43
|
-
} from "./chunks/chunk-
|
|
43
|
+
} from "./chunks/chunk-N4MN54CD.js";
|
|
44
44
|
import {
|
|
45
45
|
AgentModelListingError,
|
|
46
46
|
AgentSessionRenameError,
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
UnsupportedAgentOperationError,
|
|
50
50
|
captureResearchAccess,
|
|
51
51
|
captureWorkbenchAccess
|
|
52
|
-
} from "./chunks/chunk-
|
|
52
|
+
} from "./chunks/chunk-BM6T2DLH.js";
|
|
53
53
|
import {
|
|
54
54
|
A2aRelayRequestSchema,
|
|
55
55
|
A2aRelayResponseSchema,
|
|
@@ -107,8 +107,11 @@ import {
|
|
|
107
107
|
SESSION_FILE_MAX_IMAGE_EDGE,
|
|
108
108
|
SESSION_FILE_MAX_IMAGE_PIXELS,
|
|
109
109
|
SESSION_FILE_MAX_ORIGINAL_BYTES,
|
|
110
|
+
SESSION_FILE_MAX_VIDEO_PIXELS,
|
|
110
111
|
SESSION_FILE_READ_OPERATION,
|
|
112
|
+
SESSION_FILE_READ_V2_VERSION,
|
|
111
113
|
SESSION_FILE_READ_VERSION,
|
|
114
|
+
SESSION_FILE_V2_MAX_ORIGINAL_BYTES,
|
|
112
115
|
SESSION_GIT_DIFF_GZIP_MIN_BYTES,
|
|
113
116
|
SESSION_GIT_DIFF_GZIP_VERSION,
|
|
114
117
|
SESSION_GIT_DIFF_MAX_FILES,
|
|
@@ -116,6 +119,9 @@ import {
|
|
|
116
119
|
SESSION_GIT_DIFF_MAX_RESPONSE_BYTES,
|
|
117
120
|
SESSION_GIT_DIFF_OPERATION,
|
|
118
121
|
SESSION_GIT_DIFF_VERSION,
|
|
122
|
+
SESSION_IMAGE_API_PATH,
|
|
123
|
+
SESSION_IMAGE_MAX_BYTES,
|
|
124
|
+
SESSION_IMAGE_MAX_PIXELS,
|
|
119
125
|
SESSION_LIST_UPDATES_CAPABILITY,
|
|
120
126
|
SESSION_OPTIONS_PARTIAL_CAPABILITY,
|
|
121
127
|
SESSION_PROJECT_IDENTITY_OPERATION,
|
|
@@ -144,9 +150,14 @@ import {
|
|
|
144
150
|
SessionDirectoryListResultDataSchema,
|
|
145
151
|
SessionFileReadDataSchema,
|
|
146
152
|
SessionFileReadResultDataSchema,
|
|
153
|
+
SessionFileReadV2ResultDataSchema,
|
|
147
154
|
SessionGitDiffDataSchema,
|
|
148
155
|
SessionGitDiffGzipResultDataSchema,
|
|
149
156
|
SessionGitDiffResultDataSchema,
|
|
157
|
+
SessionImageCommitResultSchema,
|
|
158
|
+
SessionImageScopeSchema,
|
|
159
|
+
SessionImageUploadRequestSchema,
|
|
160
|
+
SessionImageUploadResultSchema,
|
|
150
161
|
SessionListRequestSchema,
|
|
151
162
|
SessionListResultSchema,
|
|
152
163
|
SessionOptionsRequestSchema,
|
|
@@ -164,6 +175,8 @@ import {
|
|
|
164
175
|
SessionTimelinePageSchema,
|
|
165
176
|
SessionWatchRequestSchema,
|
|
166
177
|
SessionsSnapshotSchema,
|
|
178
|
+
TIMELINE_IMAGE_BLOB_OPERATION,
|
|
179
|
+
TIMELINE_IMAGE_BLOB_VERSION,
|
|
167
180
|
TIMELINE_IMAGE_CHUNK_BYTES,
|
|
168
181
|
TIMELINE_IMAGE_MAX_BYTES,
|
|
169
182
|
TIMELINE_IMAGE_OPERATION,
|
|
@@ -172,6 +185,8 @@ import {
|
|
|
172
185
|
TIMELINE_SYNC_OPERATION,
|
|
173
186
|
TIMELINE_SYNC_VERSION,
|
|
174
187
|
TRANSPORT_V2,
|
|
188
|
+
TimelineImageBlobRequestSchema,
|
|
189
|
+
TimelineImageBlobResultSchema,
|
|
175
190
|
TimelineImageChunkSchema,
|
|
176
191
|
TimelineImageRequestSchema,
|
|
177
192
|
TimelineSyncRequestSchema,
|
|
@@ -208,12 +223,12 @@ import {
|
|
|
208
223
|
sessionCommunicationError,
|
|
209
224
|
sessionCommunicationOperationCapability,
|
|
210
225
|
workbenchCapability
|
|
211
|
-
} from "./chunks/chunk-
|
|
226
|
+
} from "./chunks/chunk-E3XCZWRK.js";
|
|
212
227
|
|
|
213
228
|
// src/initialize.ts
|
|
214
|
-
import { createHash as
|
|
229
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
215
230
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
216
|
-
import { z as
|
|
231
|
+
import { z as z12 } from "zod";
|
|
217
232
|
|
|
218
233
|
// src/coding-agent-factory.ts
|
|
219
234
|
import { resolve } from "node:path";
|
|
@@ -286,7 +301,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
286
301
|
switch (engine.kind) {
|
|
287
302
|
case "managed": {
|
|
288
303
|
if (owner === void 0) throw new Error("Managed coding agents require the PC owner identity");
|
|
289
|
-
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-
|
|
304
|
+
const { ManagedCodingAgentAdapter } = await import("./chunks/managed-agent-adapter-T3FVUJHA.js");
|
|
290
305
|
return new ManagedCodingAgentAdapter({
|
|
291
306
|
dataDirectory: config.dataDirectory,
|
|
292
307
|
owner,
|
|
@@ -325,7 +340,7 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
325
340
|
});
|
|
326
341
|
}
|
|
327
342
|
case "copilot": {
|
|
328
|
-
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-
|
|
343
|
+
const { GithubCopilotAdapter } = await import("./chunks/copilot-adapter-N5B7BNWO.js");
|
|
329
344
|
if (owner !== void 0) {
|
|
330
345
|
return new GithubCopilotAdapter(void 0, void 0, void 0, {
|
|
331
346
|
dataDirectory: config.dataDirectory,
|
|
@@ -345,13 +360,13 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
345
360
|
}
|
|
346
361
|
case "pi": {
|
|
347
362
|
assertPiRuntimeSupported();
|
|
348
|
-
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-
|
|
363
|
+
const { PiCodingAgentAdapter } = await import("./chunks/pi-adapter-PHEFFJVF.js");
|
|
349
364
|
return new PiCodingAgentAdapter(
|
|
350
365
|
engine.agentDirectory === void 0 ? {} : { agentDirectory: resolve(engine.agentDirectory) }
|
|
351
366
|
);
|
|
352
367
|
}
|
|
353
368
|
case "acp": {
|
|
354
|
-
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-
|
|
369
|
+
const { AcpCodingAgentAdapter } = await import("./chunks/acp-adapter-BWVM6WIF.js");
|
|
355
370
|
return new AcpCodingAgentAdapter({
|
|
356
371
|
command: engine.command,
|
|
357
372
|
args: engine.args,
|
|
@@ -367,6 +382,18 @@ async function createCodingAgentAdapter(config, onDiagnostic, owner) {
|
|
|
367
382
|
}
|
|
368
383
|
}
|
|
369
384
|
|
|
385
|
+
// src/config.ts
|
|
386
|
+
import { z } from "zod";
|
|
387
|
+
var SessionImagesConfigSchema = z.object({
|
|
388
|
+
allowUnencryptedStorage: z.literal(true),
|
|
389
|
+
storageAccount: z.string().regex(/^[a-z0-9]{3,24}$/u),
|
|
390
|
+
storageContainer: z.string().min(3).max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/u)
|
|
391
|
+
}).strict();
|
|
392
|
+
var SessionImageRelayUrlSchema = z.url().refine((value) => {
|
|
393
|
+
const url = new URL(value);
|
|
394
|
+
return /^https:\/\/[^/?#\\@\s\x00-\x1f\x7f]+\/?$/iu.test(value) && url.protocol === "https:" && url.username === "" && url.password === "" && url.pathname === "/" && url.search === "" && url.hash === "";
|
|
395
|
+
}, "Session images require an HTTPS Relay origin without a path, credentials, query or fragment").transform((value) => new URL(value).origin);
|
|
396
|
+
|
|
370
397
|
// src/content-encryption.ts
|
|
371
398
|
import {
|
|
372
399
|
createCipheriv,
|
|
@@ -383,22 +410,22 @@ import {
|
|
|
383
410
|
} from "node:fs";
|
|
384
411
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
385
412
|
import { join as join2 } from "node:path";
|
|
386
|
-
import { z } from "zod";
|
|
413
|
+
import { z as z2 } from "zod";
|
|
387
414
|
var KEY_BYTES = 32;
|
|
388
415
|
var NONCE_BYTES = 12;
|
|
389
416
|
var AUTH_TAG_BYTES = 16;
|
|
390
417
|
var DEFAULT_REPLAY_WINDOW_MS = 10 * 60 * 1e3;
|
|
391
418
|
var MAX_REPLAY_ENTRIES = 1e4;
|
|
392
419
|
var CONTENT_KEY_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
|
|
393
|
-
var ContentKeyFileSchema =
|
|
394
|
-
schemaVersion:
|
|
395
|
-
key:
|
|
420
|
+
var ContentKeyFileSchema = z2.object({
|
|
421
|
+
schemaVersion: z2.literal(1),
|
|
422
|
+
key: z2.string().regex(CONTENT_KEY_PATTERN)
|
|
396
423
|
}).strict();
|
|
397
|
-
var ReplayJournalEntrySchema =
|
|
398
|
-
schemaVersion:
|
|
399
|
-
keyId:
|
|
400
|
-
messageId:
|
|
401
|
-
sentAt:
|
|
424
|
+
var ReplayJournalEntrySchema = z2.object({
|
|
425
|
+
schemaVersion: z2.literal(1),
|
|
426
|
+
keyId: z2.string().regex(/^[A-Za-z0-9_-]{22}$/u),
|
|
427
|
+
messageId: z2.string().trim().min(1).max(256),
|
|
428
|
+
sentAt: z2.number().int().nonnegative()
|
|
402
429
|
}).strict();
|
|
403
430
|
var REPLAY_JOURNAL_COMPACT_EVERY = 256;
|
|
404
431
|
var REPLAY_JOURNAL_MAX_BYTES = 1024 * 1024;
|
|
@@ -709,13 +736,13 @@ var ContentCipher = class {
|
|
|
709
736
|
};
|
|
710
737
|
|
|
711
738
|
// src/device-identity.ts
|
|
712
|
-
import { z as
|
|
713
|
-
var DeviceIdentitySchema =
|
|
714
|
-
schemaVersion:
|
|
715
|
-
machineId:
|
|
716
|
-
stableDeviceId:
|
|
717
|
-
machineName:
|
|
718
|
-
createdAt:
|
|
739
|
+
import { z as z3 } from "zod";
|
|
740
|
+
var DeviceIdentitySchema = z3.object({
|
|
741
|
+
schemaVersion: z3.literal(2),
|
|
742
|
+
machineId: z3.uuid(),
|
|
743
|
+
stableDeviceId: z3.uuid(),
|
|
744
|
+
machineName: z3.string().trim().min(1).max(120),
|
|
745
|
+
createdAt: z3.iso.datetime({ offset: true })
|
|
719
746
|
});
|
|
720
747
|
|
|
721
748
|
// src/pairing.ts
|
|
@@ -812,7 +839,7 @@ import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypt
|
|
|
812
839
|
import { lstat as lstat3, realpath as realpath3, stat as stat4 } from "node:fs/promises";
|
|
813
840
|
import { isAbsolute as isAbsolute4, join as join5 } from "node:path";
|
|
814
841
|
import { gunzipSync } from "node:zlib";
|
|
815
|
-
import { z as
|
|
842
|
+
import { z as z5 } from "zod";
|
|
816
843
|
|
|
817
844
|
// src/session-directory.ts
|
|
818
845
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -967,6 +994,152 @@ import { basename, extname, isAbsolute as isAbsolute3, join as join4 } from "nod
|
|
|
967
994
|
import { promisify } from "node:util";
|
|
968
995
|
import { gzip } from "node:zlib";
|
|
969
996
|
import sharp from "sharp";
|
|
997
|
+
|
|
998
|
+
// src/session-video.ts
|
|
999
|
+
function requireVideo(valid) {
|
|
1000
|
+
if (!valid) throw new Error("Unsupported video container");
|
|
1001
|
+
}
|
|
1002
|
+
function one(elements, id) {
|
|
1003
|
+
const matches = elements.filter((element) => element.id === id);
|
|
1004
|
+
const match = matches[0];
|
|
1005
|
+
requireVideo(matches.length === 1 && match !== void 0);
|
|
1006
|
+
return match;
|
|
1007
|
+
}
|
|
1008
|
+
function validateSessionVideo(bytes, mediaType) {
|
|
1009
|
+
requireVideo(bytes.length > 0 && bytes.length <= SESSION_FILE_V2_MAX_ORIGINAL_BYTES);
|
|
1010
|
+
if (mediaType === "video/mp4") validateMp4(bytes);
|
|
1011
|
+
else validateWebm(bytes);
|
|
1012
|
+
}
|
|
1013
|
+
function validateMp4(bytes) {
|
|
1014
|
+
const boxes = (start, end) => {
|
|
1015
|
+
const result = [];
|
|
1016
|
+
while (start < end) {
|
|
1017
|
+
requireVideo(end - start >= 8);
|
|
1018
|
+
let size = bytes.readUInt32BE(start);
|
|
1019
|
+
const id = bytes.toString("latin1", start + 4, start + 8);
|
|
1020
|
+
let header = 8;
|
|
1021
|
+
if (size === 1) {
|
|
1022
|
+
requireVideo(end - start >= 16 && bytes.readUInt32BE(start + 8) === 0);
|
|
1023
|
+
size = bytes.readUInt32BE(start + 12);
|
|
1024
|
+
header = 16;
|
|
1025
|
+
}
|
|
1026
|
+
if (size === 0) {
|
|
1027
|
+
requireVideo(id === "mdat");
|
|
1028
|
+
size = end - start;
|
|
1029
|
+
}
|
|
1030
|
+
requireVideo(size >= header && size <= end - start);
|
|
1031
|
+
result.push({ id, start: start + header, end: start + size });
|
|
1032
|
+
start += size;
|
|
1033
|
+
}
|
|
1034
|
+
return result;
|
|
1035
|
+
};
|
|
1036
|
+
const children = (element) => boxes(element.start, element.end);
|
|
1037
|
+
const top = boxes(0, bytes.length);
|
|
1038
|
+
requireVideo(top[0]?.id === "ftyp" && top.every((box) => ["ftyp", "moov", "mdat", "free", "skip", "wide"].includes(box.id)));
|
|
1039
|
+
const ftyp = one(top, "ftyp");
|
|
1040
|
+
requireVideo(ftyp.end - ftyp.start >= 8 && (ftyp.end - ftyp.start) % 4 === 0);
|
|
1041
|
+
const brands = [bytes.toString("latin1", ftyp.start, ftyp.start + 4)];
|
|
1042
|
+
for (let offset = ftyp.start + 8; offset < ftyp.end; offset += 4) brands.push(bytes.toString("latin1", offset, offset + 4));
|
|
1043
|
+
requireVideo(brands.some((brand) => ["isom", "iso2", "mp41", "mp42", "avc1"].includes(brand)));
|
|
1044
|
+
requireVideo(top.some((box) => box.id === "mdat" && box.end > box.start));
|
|
1045
|
+
const movie = children(one(top, "moov"));
|
|
1046
|
+
requireVideo(!movie.some((box) => ["rmra", "mvex"].includes(box.id)));
|
|
1047
|
+
let videoTracks = 0;
|
|
1048
|
+
for (const track of movie.filter((box) => box.id === "trak")) {
|
|
1049
|
+
const media = children(one(children(track), "mdia"));
|
|
1050
|
+
const handler = one(media, "hdlr");
|
|
1051
|
+
requireVideo(handler.end - handler.start >= 12);
|
|
1052
|
+
const type = bytes.toString("latin1", handler.start + 8, handler.start + 12);
|
|
1053
|
+
requireVideo(type === "vide" || type === "soun");
|
|
1054
|
+
const info = children(one(media, "minf"));
|
|
1055
|
+
const dref = one(children(one(info, "dinf")), "dref");
|
|
1056
|
+
requireVideo(dref.end - dref.start >= 8 && bytes.readUInt32BE(dref.start) === 0 && bytes.readUInt32BE(dref.start + 4) === 1);
|
|
1057
|
+
const references = boxes(dref.start + 8, dref.end);
|
|
1058
|
+
const reference = one(references, "url ");
|
|
1059
|
+
requireVideo(references.length === 1 && reference.end - reference.start === 4 && bytes.readUInt32BE(reference.start) === 1);
|
|
1060
|
+
const descriptions = one(children(one(info, "stbl")), "stsd");
|
|
1061
|
+
requireVideo(descriptions.end - descriptions.start >= 8 && bytes.readUInt32BE(descriptions.start) === 0 && bytes.readUInt32BE(descriptions.start + 4) === 1);
|
|
1062
|
+
const entries = boxes(descriptions.start + 8, descriptions.end);
|
|
1063
|
+
const entry = entries[0];
|
|
1064
|
+
requireVideo(entries.length === 1 && entry !== void 0 && entry.end - entry.start >= 8 && bytes.readUInt16BE(entry.start + 6) === 1);
|
|
1065
|
+
if (type === "vide") {
|
|
1066
|
+
requireVideo(["avc1", "avc3"].includes(entry.id) && entry.end - entry.start >= 78);
|
|
1067
|
+
const width = bytes.readUInt16BE(entry.start + 24);
|
|
1068
|
+
const height = bytes.readUInt16BE(entry.start + 26);
|
|
1069
|
+
requireVideo(width > 0 && height > 0 && width * height <= SESSION_FILE_MAX_VIDEO_PIXELS);
|
|
1070
|
+
const config = one(boxes(entry.start + 78, entry.end), "avcC");
|
|
1071
|
+
requireVideo(config.end - config.start >= 7 && bytes[config.start] === 1);
|
|
1072
|
+
videoTracks++;
|
|
1073
|
+
} else requireVideo(entry.id === "mp4a" && entry.end - entry.start >= 28);
|
|
1074
|
+
}
|
|
1075
|
+
requireVideo(videoTracks === 1);
|
|
1076
|
+
}
|
|
1077
|
+
function validateWebm(bytes) {
|
|
1078
|
+
const vint = (offset, end, id) => {
|
|
1079
|
+
requireVideo(offset < end);
|
|
1080
|
+
const first = bytes[offset];
|
|
1081
|
+
requireVideo(first !== void 0 && first !== 0);
|
|
1082
|
+
let length = 1;
|
|
1083
|
+
while ((first & 128 >> length - 1) === 0) length++;
|
|
1084
|
+
requireVideo(length <= (id ? 4 : 8) && length <= end - offset);
|
|
1085
|
+
let value = id ? first : first & 255 >> length;
|
|
1086
|
+
let unknown = !id && value === 255 >> length;
|
|
1087
|
+
for (let index = 1; index < length; index++) {
|
|
1088
|
+
const byte = bytes[offset + index];
|
|
1089
|
+
requireVideo(byte !== void 0);
|
|
1090
|
+
value = value * 256 + byte;
|
|
1091
|
+
unknown = unknown && byte === 255;
|
|
1092
|
+
}
|
|
1093
|
+
requireVideo(unknown || Number.isSafeInteger(value));
|
|
1094
|
+
return { value, next: offset + length, unknown };
|
|
1095
|
+
};
|
|
1096
|
+
const elements = (start, end) => {
|
|
1097
|
+
const result = [];
|
|
1098
|
+
while (start < end) {
|
|
1099
|
+
const id = vint(start, end, true);
|
|
1100
|
+
const size = vint(id.next, end, false);
|
|
1101
|
+
const tag = id.value.toString(16);
|
|
1102
|
+
requireVideo(!size.unknown || tag === "18538067");
|
|
1103
|
+
const length = size.unknown ? end - size.next : size.value;
|
|
1104
|
+
requireVideo(length <= end - size.next);
|
|
1105
|
+
result.push({ id: tag, start: size.next, end: size.next + length });
|
|
1106
|
+
start = size.next + length;
|
|
1107
|
+
}
|
|
1108
|
+
return result;
|
|
1109
|
+
};
|
|
1110
|
+
const text = (element) => bytes.toString("latin1", element.start, element.end);
|
|
1111
|
+
const uint = (element) => {
|
|
1112
|
+
requireVideo(element.end - element.start >= 1 && element.end - element.start <= 4);
|
|
1113
|
+
return bytes.readUIntBE(element.start, element.end - element.start);
|
|
1114
|
+
};
|
|
1115
|
+
const top = elements(0, bytes.length);
|
|
1116
|
+
requireVideo(top.length === 2 && top[0]?.id === "1a45dfa3");
|
|
1117
|
+
const header = one(top, "1a45dfa3");
|
|
1118
|
+
requireVideo(text(one(elements(header.start, header.end), "4282")) === "webm");
|
|
1119
|
+
const segment = one(top, "18538067");
|
|
1120
|
+
const contents = elements(segment.start, segment.end);
|
|
1121
|
+
requireVideo(contents.some((element) => element.id === "1f43b675" && element.end > element.start));
|
|
1122
|
+
const tracks = one(contents, "1654ae6b");
|
|
1123
|
+
let videoTracks = 0;
|
|
1124
|
+
for (const track of elements(tracks.start, tracks.end).filter((element) => element.id === "ae")) {
|
|
1125
|
+
const fields = elements(track.start, track.end);
|
|
1126
|
+
const type = uint(one(fields, "83"));
|
|
1127
|
+
const codec = text(one(fields, "86"));
|
|
1128
|
+
requireVideo(!fields.some((field2) => field2.id === "6d80" || field2.id === "6fab"));
|
|
1129
|
+
if (type === 1) {
|
|
1130
|
+
requireVideo(codec === "V_VP8" || codec === "V_VP9");
|
|
1131
|
+
const video = one(fields, "e0");
|
|
1132
|
+
const dimensions = elements(video.start, video.end);
|
|
1133
|
+
const width = uint(one(dimensions, "b0"));
|
|
1134
|
+
const height = uint(one(dimensions, "ba"));
|
|
1135
|
+
requireVideo(width > 0 && height > 0 && width * height <= SESSION_FILE_MAX_VIDEO_PIXELS);
|
|
1136
|
+
videoTracks++;
|
|
1137
|
+
} else requireVideo(type === 2 && (codec === "A_VORBIS" || codec === "A_OPUS"));
|
|
1138
|
+
}
|
|
1139
|
+
requireVideo(videoTracks === 1);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// src/session-file.ts
|
|
970
1143
|
var SESSION_FILE_CONCURRENCY = 4;
|
|
971
1144
|
var SESSION_FILE_TIMEOUT_MS = 5e3;
|
|
972
1145
|
var compress = promisify(gzip);
|
|
@@ -1204,10 +1377,12 @@ var SessionFileReader = class {
|
|
|
1204
1377
|
}
|
|
1205
1378
|
metadata;
|
|
1206
1379
|
#active = 0;
|
|
1207
|
-
async read(sessionId, path, rootRevision) {
|
|
1380
|
+
async read(sessionId, path, rootRevision, version = SESSION_FILE_READ_VERSION) {
|
|
1381
|
+
if (version !== SESSION_FILE_READ_VERSION && version !== SESSION_FILE_READ_V2_VERSION) fail2("UNSUPPORTED_OPERATION_VERSION");
|
|
1208
1382
|
if (!SessionFileReadDataSchema.safeParse({ path, rootRevision }).success) fail2("INVALID_FILE_PATH");
|
|
1209
|
-
|
|
1210
|
-
|
|
1383
|
+
const schema = version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema;
|
|
1384
|
+
return schema.parse(await this.#run(
|
|
1385
|
+
(checkDeadline) => this.#read(sessionId, path, rootRevision, checkDeadline, false, version)
|
|
1211
1386
|
));
|
|
1212
1387
|
}
|
|
1213
1388
|
async readResource(sessionId, path, rootRevision) {
|
|
@@ -1241,7 +1416,7 @@ var SessionFileReader = class {
|
|
|
1241
1416
|
clearTimeout(timer);
|
|
1242
1417
|
}
|
|
1243
1418
|
}
|
|
1244
|
-
async #read(sessionId, path, rootRevision, checkDeadline, resource) {
|
|
1419
|
+
async #read(sessionId, path, rootRevision, checkDeadline, resource, version = SESSION_FILE_READ_VERSION) {
|
|
1245
1420
|
const metadata = await this.metadata(sessionId);
|
|
1246
1421
|
checkDeadline();
|
|
1247
1422
|
if (metadata === void 0) fail2("SESSION_NOT_FOUND");
|
|
@@ -1283,11 +1458,12 @@ var SessionFileReader = class {
|
|
|
1283
1458
|
return file;
|
|
1284
1459
|
};
|
|
1285
1460
|
const before = await inspectPath();
|
|
1286
|
-
const maxOriginalBytes = resource ? SESSION_RESOURCE_MAX_ORIGINAL_BYTES : SESSION_FILE_MAX_ORIGINAL_BYTES;
|
|
1461
|
+
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
1462
|
if (before.size > BigInt(maxOriginalBytes)) fail2("FILE_TOO_LARGE");
|
|
1288
1463
|
const extension = extname(path).toLowerCase();
|
|
1289
1464
|
const image = [".png", ".jpg", ".jpeg", ".webp"].includes(extension);
|
|
1290
|
-
const
|
|
1465
|
+
const video = !resource && version === SESSION_FILE_READ_V2_VERSION && [".mp4", ".webm"].includes(extension);
|
|
1466
|
+
const mediaType = resource ? resourceMediaType(extension) : image ? "image/webp" : video ? extension === ".mp4" ? "video/mp4" : "video/webm" : textMediaType(path);
|
|
1291
1467
|
const handle = await open(target, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
|
|
1292
1468
|
try {
|
|
1293
1469
|
const opened = await handle.stat({ bigint: true });
|
|
@@ -1313,6 +1489,13 @@ var SessionFileReader = class {
|
|
|
1313
1489
|
await validateRawImage(bytes, extension, checkDeadline);
|
|
1314
1490
|
canonical = bytes;
|
|
1315
1491
|
} else canonical = await imagePreview(bytes, extension, checkDeadline);
|
|
1492
|
+
} else if (mediaType === "video/mp4" || mediaType === "video/webm") {
|
|
1493
|
+
try {
|
|
1494
|
+
validateSessionVideo(bytes, mediaType);
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
throw new SessionFileReadError("FILE_UNSUPPORTED", { cause: error });
|
|
1497
|
+
}
|
|
1498
|
+
canonical = bytes;
|
|
1316
1499
|
} else {
|
|
1317
1500
|
let text;
|
|
1318
1501
|
try {
|
|
@@ -1345,7 +1528,7 @@ var SessionFileReader = class {
|
|
|
1345
1528
|
throw new SessionFileReadError("FILE_CHANGED", { cause: error });
|
|
1346
1529
|
}
|
|
1347
1530
|
checkDeadline();
|
|
1348
|
-
return (resource ? SessionResourceReadResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1531
|
+
return (resource ? SessionResourceReadResultDataSchema : version === SESSION_FILE_READ_V2_VERSION ? SessionFileReadV2ResultDataSchema : SessionFileReadResultDataSchema).parse({
|
|
1349
1532
|
path,
|
|
1350
1533
|
rootRevision,
|
|
1351
1534
|
mediaType,
|
|
@@ -1784,20 +1967,20 @@ function sensitiveSegments(segments) {
|
|
|
1784
1967
|
}
|
|
1785
1968
|
return false;
|
|
1786
1969
|
}
|
|
1787
|
-
var pathSchema =
|
|
1788
|
-
var listSchema =
|
|
1970
|
+
var pathSchema = z5.string().max(4096).default("");
|
|
1971
|
+
var listSchema = z5.object({
|
|
1789
1972
|
path: pathSchema,
|
|
1790
|
-
cursor:
|
|
1973
|
+
cursor: z5.string().uuid().optional()
|
|
1791
1974
|
}).strict();
|
|
1792
|
-
var readSchema =
|
|
1793
|
-
path:
|
|
1794
|
-
startLine:
|
|
1795
|
-
lineCount:
|
|
1975
|
+
var readSchema = z5.object({
|
|
1976
|
+
path: z5.string().min(1).max(4096),
|
|
1977
|
+
startLine: z5.number().int().min(1).max(500001).default(1),
|
|
1978
|
+
lineCount: z5.number().int().min(1).max(200).default(200)
|
|
1796
1979
|
}).strict();
|
|
1797
|
-
var searchSchema =
|
|
1980
|
+
var searchSchema = z5.object({
|
|
1798
1981
|
path: pathSchema,
|
|
1799
|
-
query:
|
|
1800
|
-
cursor:
|
|
1982
|
+
query: z5.string().min(1).max(256),
|
|
1983
|
+
cursor: z5.string().uuid().optional()
|
|
1801
1984
|
}).strict();
|
|
1802
1985
|
var ResearchTools = class {
|
|
1803
1986
|
constructor(parentSessionId, root, sourceMetadata, privateDirectories = [], onError, captureSourceAccess) {
|
|
@@ -2057,7 +2240,7 @@ var ResearchTools = class {
|
|
|
2057
2240
|
const definition = (name, description, schema, run) => ({
|
|
2058
2241
|
name,
|
|
2059
2242
|
description,
|
|
2060
|
-
parameters:
|
|
2243
|
+
parameters: z5.toJSONSchema(schema),
|
|
2061
2244
|
// Only these host-owned read tools bypass prompts; their path policy remains mandatory.
|
|
2062
2245
|
skipPermission: true,
|
|
2063
2246
|
defer: "never",
|
|
@@ -2070,7 +2253,7 @@ var ResearchTools = class {
|
|
|
2070
2253
|
return { textResultForLlm: JSON.stringify(result), resultType: "success" };
|
|
2071
2254
|
} catch (error) {
|
|
2072
2255
|
const knownCode = workbenchErrorDetails(error).code;
|
|
2073
|
-
const code = (error instanceof ResearchError || error instanceof SessionFileReadError) && knownCode !== "UNCLASSIFIED" ? knownCode : error instanceof
|
|
2256
|
+
const code = (error instanceof ResearchError || error instanceof SessionFileReadError) && knownCode !== "UNCLASSIFIED" ? knownCode : error instanceof z5.ZodError ? "RESEARCH_INVALID_ARGUMENT" : "RESEARCH_TOOL_FAILED";
|
|
2074
2257
|
this.onError?.(new ResearchError(code, "The research tool failed", { cause: error }), { stage: name });
|
|
2075
2258
|
return { textResultForLlm: JSON.stringify({ error: code }), resultType: "failure" };
|
|
2076
2259
|
}
|
|
@@ -2225,7 +2408,7 @@ import { io } from "socket.io-client";
|
|
|
2225
2408
|
|
|
2226
2409
|
// src/service/requests/session-communication.ts
|
|
2227
2410
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2228
|
-
import { createHash as
|
|
2411
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
2229
2412
|
import { join as join7 } from "node:path";
|
|
2230
2413
|
import { performance as performance2 } from "node:perf_hooks";
|
|
2231
2414
|
|
|
@@ -3097,6 +3280,430 @@ var SessionGitDiffReader = class {
|
|
|
3097
3280
|
}
|
|
3098
3281
|
};
|
|
3099
3282
|
|
|
3283
|
+
// src/timeline-image-cache.ts
|
|
3284
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
3285
|
+
var TimelineAssetError = class extends Error {
|
|
3286
|
+
constructor(code, message, options) {
|
|
3287
|
+
super(message, options);
|
|
3288
|
+
this.code = code;
|
|
3289
|
+
}
|
|
3290
|
+
code;
|
|
3291
|
+
};
|
|
3292
|
+
var TimelineImageCache = class {
|
|
3293
|
+
constructor(maxBytes = 32 * 1024 * 1024) {
|
|
3294
|
+
this.maxBytes = maxBytes;
|
|
3295
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < TIMELINE_IMAGE_MAX_BYTES) {
|
|
3296
|
+
throw new Error("Image cache must hold at least one maximum-size image");
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
maxBytes;
|
|
3300
|
+
#assets = /* @__PURE__ */ new Map();
|
|
3301
|
+
#bytes = 0;
|
|
3302
|
+
get bytes() {
|
|
3303
|
+
return this.#bytes;
|
|
3304
|
+
}
|
|
3305
|
+
referenceItem(sessionId, item) {
|
|
3306
|
+
if (!("attachments" in item) || item.attachments === void 0) return item;
|
|
3307
|
+
return { ...item, attachments: item.attachments.map((attachment) => this.#reference(sessionId, attachment)) };
|
|
3308
|
+
}
|
|
3309
|
+
read(sessionId, request) {
|
|
3310
|
+
const key = JSON.stringify([sessionId, request.sha256]);
|
|
3311
|
+
const asset = this.#assets.get(key);
|
|
3312
|
+
if (asset === void 0) throw new TimelineAssetError("IMAGE_ABSENT", "The image is no longer cached on this PC");
|
|
3313
|
+
if (request.offset >= asset.bytes.length) {
|
|
3314
|
+
throw new TimelineAssetError("IMAGE_OFFSET_INVALID", "The image chunk offset exceeds its length");
|
|
3315
|
+
}
|
|
3316
|
+
this.#assets.delete(key);
|
|
3317
|
+
this.#assets.set(key, asset);
|
|
3318
|
+
const end = Math.min(request.offset + TIMELINE_IMAGE_CHUNK_BYTES, asset.bytes.length);
|
|
3319
|
+
return TimelineImageChunkSchema.parse({
|
|
3320
|
+
sha256: request.sha256,
|
|
3321
|
+
offset: request.offset,
|
|
3322
|
+
totalBytes: asset.bytes.length,
|
|
3323
|
+
mimeType: asset.mimeType,
|
|
3324
|
+
data: asset.bytes.subarray(request.offset, end).toString("base64"),
|
|
3325
|
+
eof: end === asset.bytes.length
|
|
3326
|
+
});
|
|
3327
|
+
}
|
|
3328
|
+
readAsset(sessionId, sha256) {
|
|
3329
|
+
const key = JSON.stringify([sessionId, sha256]);
|
|
3330
|
+
const asset = this.#assets.get(key);
|
|
3331
|
+
if (asset === void 0) throw new TimelineAssetError("IMAGE_ABSENT", "The image is no longer cached on this PC");
|
|
3332
|
+
this.#assets.delete(key);
|
|
3333
|
+
this.#assets.set(key, asset);
|
|
3334
|
+
return { bytes: Buffer.from(asset.bytes), mimeType: asset.mimeType };
|
|
3335
|
+
}
|
|
3336
|
+
clearSession(sessionId) {
|
|
3337
|
+
const prefix = JSON.stringify([sessionId]).slice(0, -1) + ",";
|
|
3338
|
+
for (const [key, asset] of this.#assets) {
|
|
3339
|
+
if (key.startsWith(prefix)) {
|
|
3340
|
+
this.#assets.delete(key);
|
|
3341
|
+
this.#bytes -= asset.bytes.length;
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
clear() {
|
|
3346
|
+
this.#assets.clear();
|
|
3347
|
+
this.#bytes = 0;
|
|
3348
|
+
}
|
|
3349
|
+
#reference(sessionId, attachment) {
|
|
3350
|
+
if (attachment.kind !== "image" || attachment.data === void 0) return attachment;
|
|
3351
|
+
const parsed = ConversationAttachmentSchema.parse(attachment);
|
|
3352
|
+
const bytes = Buffer.from(attachment.data, "base64");
|
|
3353
|
+
if (bytes.length !== parsed.byteLength || createHash6("sha256").update(bytes).digest("hex") !== parsed.sha256 || !hasSignature(bytes, parsed.mimeType)) {
|
|
3354
|
+
throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its metadata");
|
|
3355
|
+
}
|
|
3356
|
+
const key = JSON.stringify([sessionId, parsed.sha256]);
|
|
3357
|
+
const previous = this.#assets.get(key);
|
|
3358
|
+
if (previous !== void 0) this.#bytes -= previous.bytes.length;
|
|
3359
|
+
this.#assets.delete(key);
|
|
3360
|
+
this.#assets.set(key, { bytes, mimeType: parsed.mimeType ?? "" });
|
|
3361
|
+
this.#bytes += bytes.length;
|
|
3362
|
+
while (this.#bytes > this.maxBytes) {
|
|
3363
|
+
const oldest = this.#assets.entries().next().value;
|
|
3364
|
+
if (oldest === void 0) break;
|
|
3365
|
+
this.#assets.delete(oldest[0]);
|
|
3366
|
+
this.#bytes -= oldest[1].bytes.length;
|
|
3367
|
+
}
|
|
3368
|
+
const { data: _data, ...metadata } = parsed;
|
|
3369
|
+
return { ...metadata, imageRef: "sha256" };
|
|
3370
|
+
}
|
|
3371
|
+
};
|
|
3372
|
+
function hasSignature(bytes, mimeType) {
|
|
3373
|
+
switch (mimeType) {
|
|
3374
|
+
case "image/png":
|
|
3375
|
+
return bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]));
|
|
3376
|
+
case "image/jpeg":
|
|
3377
|
+
return bytes.subarray(0, 3).equals(Buffer.from([255, 216, 255]));
|
|
3378
|
+
case "image/gif":
|
|
3379
|
+
return ["GIF87a", "GIF89a"].includes(bytes.subarray(0, 6).toString("ascii"));
|
|
3380
|
+
case "image/webp":
|
|
3381
|
+
return bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP";
|
|
3382
|
+
default:
|
|
3383
|
+
return false;
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
|
|
3387
|
+
// src/session-image-uploader.ts
|
|
3388
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
3389
|
+
import sharp2 from "sharp";
|
|
3390
|
+
import { z as z6 } from "zod";
|
|
3391
|
+
var MAX_ACTIVE_UPLOADS = 3;
|
|
3392
|
+
var MAX_JSON_BYTES = 16 * 1024;
|
|
3393
|
+
var REQUEST_TIMEOUT_MS = 2e4;
|
|
3394
|
+
var UPLOAD_TIMEOUT_MS = 7e4;
|
|
3395
|
+
var MAX_GRANT_LIFETIME_MS = 10 * 6e4 + 3e4;
|
|
3396
|
+
var UploaderConfigSchema = z6.object({
|
|
3397
|
+
relayUrl: SessionImageRelayUrlSchema,
|
|
3398
|
+
relayToken: z6.string().min(24).max(4096).regex(/^[\x21-\x7e]+$/u),
|
|
3399
|
+
userId: IdSchema.refine((value) => /^[\x20-\x7e]+$/u.test(value)),
|
|
3400
|
+
machineId: IdSchema,
|
|
3401
|
+
storageAccount: SessionImagesConfigSchema.shape.storageAccount,
|
|
3402
|
+
storageContainer: SessionImagesConfigSchema.shape.storageContainer
|
|
3403
|
+
});
|
|
3404
|
+
function safeCause(error) {
|
|
3405
|
+
if (error instanceof SessionImageUploadError) return error;
|
|
3406
|
+
if (error instanceof z6.ZodError) return new Error("Schema validation failed.");
|
|
3407
|
+
const result = new Error("Image operation failed.");
|
|
3408
|
+
if (error instanceof Error) {
|
|
3409
|
+
if (["TypeError", "SyntaxError", "AbortError", "TimeoutError"].includes(error.name)) result.name = error.name;
|
|
3410
|
+
const code = "code" in error ? error.code : void 0;
|
|
3411
|
+
if (typeof code === "string" && ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND", "EAI_AGAIN", "UND_ERR_CONNECT_TIMEOUT"].includes(code)) {
|
|
3412
|
+
result.cause = { code };
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
return result;
|
|
3416
|
+
}
|
|
3417
|
+
var SessionImageUploadError = class extends Error {
|
|
3418
|
+
constructor(code, stage, status, cause) {
|
|
3419
|
+
super(`${code} (${stage})`, cause === void 0 ? void 0 : { cause: safeCause(cause) });
|
|
3420
|
+
this.code = code;
|
|
3421
|
+
this.stage = stage;
|
|
3422
|
+
this.status = status;
|
|
3423
|
+
this.name = "SessionImageUploadError";
|
|
3424
|
+
}
|
|
3425
|
+
code;
|
|
3426
|
+
stage;
|
|
3427
|
+
status;
|
|
3428
|
+
};
|
|
3429
|
+
function digest(bytes) {
|
|
3430
|
+
return createHash7("sha256").update(bytes).digest("hex");
|
|
3431
|
+
}
|
|
3432
|
+
function assert(condition, code, stage) {
|
|
3433
|
+
if (!condition) throw new SessionImageUploadError(code, stage);
|
|
3434
|
+
}
|
|
3435
|
+
function parse(schema, input, stage) {
|
|
3436
|
+
const result = schema.safeParse(input);
|
|
3437
|
+
if (!result.success) throw new SessionImageUploadError(
|
|
3438
|
+
stage === "config" ? "IMAGE_CONFIG_INVALID" : stage === "source" ? "IMAGE_CONTENT_INVALID" : "IMAGE_RESPONSE_INVALID",
|
|
3439
|
+
stage,
|
|
3440
|
+
void 0,
|
|
3441
|
+
result.error
|
|
3442
|
+
);
|
|
3443
|
+
return result.data;
|
|
3444
|
+
}
|
|
3445
|
+
function abortable(operation, signal, stage) {
|
|
3446
|
+
return new Promise((resolve5, reject) => {
|
|
3447
|
+
const abort = () => reject(new SessionImageUploadError("IMAGE_ABORTED", stage));
|
|
3448
|
+
if (signal.aborted) abort();
|
|
3449
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
3450
|
+
void operation.then((value) => {
|
|
3451
|
+
signal.removeEventListener("abort", abort);
|
|
3452
|
+
resolve5(value);
|
|
3453
|
+
}, (error) => {
|
|
3454
|
+
signal.removeEventListener("abort", abort);
|
|
3455
|
+
reject(error);
|
|
3456
|
+
});
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
var SessionImageUploader = class {
|
|
3460
|
+
#config;
|
|
3461
|
+
#fetch;
|
|
3462
|
+
#active = /* @__PURE__ */ new Set();
|
|
3463
|
+
#closed = false;
|
|
3464
|
+
constructor(config, dependencies = {}) {
|
|
3465
|
+
this.#config = parse(UploaderConfigSchema, config, "config");
|
|
3466
|
+
this.#fetch = dependencies.fetch ?? globalThis.fetch;
|
|
3467
|
+
}
|
|
3468
|
+
close() {
|
|
3469
|
+
this.#closed = true;
|
|
3470
|
+
for (const controller of this.#active) controller.abort();
|
|
3471
|
+
}
|
|
3472
|
+
async upload(sessionId, source, signal, assertCurrent = () => void 0) {
|
|
3473
|
+
assert(!this.#closed, "IMAGE_UPLOADER_CLOSED", "source");
|
|
3474
|
+
assert(!signal?.aborted, "IMAGE_ABORTED", "source");
|
|
3475
|
+
const revalidate = (stage) => {
|
|
3476
|
+
try {
|
|
3477
|
+
assertCurrent();
|
|
3478
|
+
} catch (error) {
|
|
3479
|
+
throw new SessionImageUploadError("IMAGE_ACCESS_REVOKED", stage, void 0, error);
|
|
3480
|
+
}
|
|
3481
|
+
};
|
|
3482
|
+
revalidate("source");
|
|
3483
|
+
assert(this.#active.size < MAX_ACTIVE_UPLOADS, "IMAGE_CAPACITY_EXCEEDED", "source");
|
|
3484
|
+
assert(
|
|
3485
|
+
Buffer.isBuffer(source) && source.length > 0 && source.length <= SESSION_IMAGE_MAX_BYTES,
|
|
3486
|
+
"IMAGE_CONTENT_INVALID",
|
|
3487
|
+
"source"
|
|
3488
|
+
);
|
|
3489
|
+
const scope = parse(SessionImageScopeSchema, { machineId: this.#config.machineId, sessionId }, "source");
|
|
3490
|
+
const controller = new AbortController();
|
|
3491
|
+
this.#active.add(controller);
|
|
3492
|
+
const abort = () => controller.abort();
|
|
3493
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
3494
|
+
let timedOut = false;
|
|
3495
|
+
const timer = setTimeout(() => {
|
|
3496
|
+
timedOut = true;
|
|
3497
|
+
controller.abort();
|
|
3498
|
+
}, UPLOAD_TIMEOUT_MS);
|
|
3499
|
+
timer.unref();
|
|
3500
|
+
try {
|
|
3501
|
+
const bytes = Buffer.from(source);
|
|
3502
|
+
const dimensions = await this.#dimensions(bytes);
|
|
3503
|
+
const hash2 = digest(bytes);
|
|
3504
|
+
const request = parse(SessionImageUploadRequestSchema, { ...scope, sizeBytes: bytes.length }, "source");
|
|
3505
|
+
revalidate("reserve");
|
|
3506
|
+
const upload = await this.#json(
|
|
3507
|
+
`${SESSION_IMAGE_API_PATH}/uploads`,
|
|
3508
|
+
request,
|
|
3509
|
+
201,
|
|
3510
|
+
SessionImageUploadResultSchema,
|
|
3511
|
+
"reserve",
|
|
3512
|
+
controller.signal
|
|
3513
|
+
);
|
|
3514
|
+
const target = this.#target(upload, scope.sessionId);
|
|
3515
|
+
let ambiguous;
|
|
3516
|
+
revalidate("put");
|
|
3517
|
+
try {
|
|
3518
|
+
await this.#request(target, {
|
|
3519
|
+
method: "PUT",
|
|
3520
|
+
headers: upload.mode === "direct" ? upload.headers : { ...this.#headers(), ...upload.headers },
|
|
3521
|
+
body: new Uint8Array(bytes)
|
|
3522
|
+
}, "put", controller.signal, async (response) => {
|
|
3523
|
+
if (response.status !== 201) throw new SessionImageUploadError("IMAGE_HTTP_ERROR", "put", response.status);
|
|
3524
|
+
});
|
|
3525
|
+
} catch (error) {
|
|
3526
|
+
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;
|
|
3527
|
+
ambiguous = error;
|
|
3528
|
+
}
|
|
3529
|
+
revalidate("complete");
|
|
3530
|
+
try {
|
|
3531
|
+
const committed = await this.#json(
|
|
3532
|
+
`${SESSION_IMAGE_API_PATH}/${upload.imageId}/complete`,
|
|
3533
|
+
scope,
|
|
3534
|
+
200,
|
|
3535
|
+
SessionImageCommitResultSchema,
|
|
3536
|
+
"complete",
|
|
3537
|
+
controller.signal
|
|
3538
|
+
);
|
|
3539
|
+
assert(
|
|
3540
|
+
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(),
|
|
3541
|
+
"IMAGE_COMMIT_MISMATCH",
|
|
3542
|
+
"complete"
|
|
3543
|
+
);
|
|
3544
|
+
revalidate("complete");
|
|
3545
|
+
return committed;
|
|
3546
|
+
} catch (error) {
|
|
3547
|
+
if (ambiguous === void 0 || error instanceof SessionImageUploadError && error.code === "IMAGE_ACCESS_REVOKED") {
|
|
3548
|
+
throw error;
|
|
3549
|
+
}
|
|
3550
|
+
const failure = new SessionImageUploadError("IMAGE_COMPLETION_FAILED", "complete", void 0, error);
|
|
3551
|
+
failure.cause = new AggregateError([ambiguous, safeCause(error)], "PUT outcome and completion failed.");
|
|
3552
|
+
throw failure;
|
|
3553
|
+
}
|
|
3554
|
+
} catch (error) {
|
|
3555
|
+
if (controller.signal.aborted) {
|
|
3556
|
+
throw new SessionImageUploadError(
|
|
3557
|
+
timedOut ? "IMAGE_TIMEOUT" : "IMAGE_ABORTED",
|
|
3558
|
+
error instanceof SessionImageUploadError ? error.stage : "source",
|
|
3559
|
+
void 0,
|
|
3560
|
+
error
|
|
3561
|
+
);
|
|
3562
|
+
}
|
|
3563
|
+
if (error instanceof SessionImageUploadError) throw error;
|
|
3564
|
+
throw new SessionImageUploadError("IMAGE_CONTENT_INVALID", "source", void 0, error);
|
|
3565
|
+
} finally {
|
|
3566
|
+
clearTimeout(timer);
|
|
3567
|
+
signal?.removeEventListener("abort", abort);
|
|
3568
|
+
this.#active.delete(controller);
|
|
3569
|
+
}
|
|
3570
|
+
}
|
|
3571
|
+
async #dimensions(bytes) {
|
|
3572
|
+
assert(
|
|
3573
|
+
bytes.length >= 20 && bytes.toString("ascii", 0, 4) === "RIFF" && bytes.toString("ascii", 8, 12) === "WEBP" && bytes.readUInt32LE(4) + 8 === bytes.length,
|
|
3574
|
+
"IMAGE_CONTENT_INVALID",
|
|
3575
|
+
"source"
|
|
3576
|
+
);
|
|
3577
|
+
const decoder = sharp2(bytes, { limitInputPixels: SESSION_IMAGE_MAX_PIXELS, failOn: "warning" });
|
|
3578
|
+
try {
|
|
3579
|
+
const metadata = await decoder.metadata();
|
|
3580
|
+
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");
|
|
3581
|
+
return { width: metadata.width, height: metadata.height };
|
|
3582
|
+
} finally {
|
|
3583
|
+
decoder.destroy();
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
#headers() {
|
|
3587
|
+
return { authorization: `Bearer ${this.#config.relayToken}`, "x-agentknight-user-id": this.#config.userId };
|
|
3588
|
+
}
|
|
3589
|
+
#target(upload, sessionId) {
|
|
3590
|
+
const now = Date.now();
|
|
3591
|
+
assert(Number.isSafeInteger(upload.uploadExpiresAt) && upload.uploadExpiresAt > now && upload.uploadExpiresAt <= now + MAX_GRANT_LIFETIME_MS, "IMAGE_UPLOAD_EXPIRED", "reserve");
|
|
3592
|
+
assert(
|
|
3593
|
+
upload.headers["content-type"] === "image/webp" && (upload.headers["if-none-match"] === void 0 || upload.headers["if-none-match"] === "*"),
|
|
3594
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3595
|
+
"reserve"
|
|
3596
|
+
);
|
|
3597
|
+
if (upload.mode === "proxy") {
|
|
3598
|
+
const path2 = `${SESSION_IMAGE_API_PATH}/${upload.imageId}/content`;
|
|
3599
|
+
const url2 = new URL(upload.uploadUrl, this.#config.relayUrl);
|
|
3600
|
+
assert(
|
|
3601
|
+
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,
|
|
3602
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3603
|
+
"reserve"
|
|
3604
|
+
);
|
|
3605
|
+
return url2.href;
|
|
3606
|
+
}
|
|
3607
|
+
const { storageAccount, storageContainer, userId, machineId } = this.#config;
|
|
3608
|
+
const host = `${storageAccount}.blob.core.windows.net`;
|
|
3609
|
+
const path = `/${storageContainer}/uploads/${digest(userId)}/${digest(machineId)}/${upload.imageId}.webp`;
|
|
3610
|
+
const url = new URL(upload.uploadUrl);
|
|
3611
|
+
const query = url.searchParams;
|
|
3612
|
+
const allowed = /* @__PURE__ */ new Set(["sv", "spr", "st", "se", "sr", "sp", "sig", "skoid", "sktid", "skt", "ske", "sks", "skv"]);
|
|
3613
|
+
assert(
|
|
3614
|
+
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",
|
|
3615
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3616
|
+
"reserve"
|
|
3617
|
+
);
|
|
3618
|
+
const expiresAt = Date.parse(query.get("se") ?? "");
|
|
3619
|
+
const startsAt = Date.parse(query.get("st") ?? "");
|
|
3620
|
+
const keyStartsAt = Date.parse(query.get("skt") ?? "");
|
|
3621
|
+
const keyExpiresAt = Date.parse(query.get("ske") ?? "");
|
|
3622
|
+
assert(
|
|
3623
|
+
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,
|
|
3624
|
+
"IMAGE_UPLOAD_TARGET_INVALID",
|
|
3625
|
+
"reserve"
|
|
3626
|
+
);
|
|
3627
|
+
return url.href;
|
|
3628
|
+
}
|
|
3629
|
+
async #json(path, body, status, schema, stage, signal) {
|
|
3630
|
+
return this.#request(`${this.#config.relayUrl}${path}`, {
|
|
3631
|
+
method: "POST",
|
|
3632
|
+
headers: { ...this.#headers(), "content-type": "application/json" },
|
|
3633
|
+
body: JSON.stringify(body)
|
|
3634
|
+
}, stage, signal, async (response, requestSignal) => {
|
|
3635
|
+
if (response.status !== status) throw new SessionImageUploadError("IMAGE_HTTP_ERROR", stage, response.status);
|
|
3636
|
+
const contentLength = response.headers.get("content-length");
|
|
3637
|
+
assert(
|
|
3638
|
+
contentLength === null || /^\d+$/u.test(contentLength) && Number(contentLength) <= MAX_JSON_BYTES,
|
|
3639
|
+
"IMAGE_RESPONSE_TOO_LARGE",
|
|
3640
|
+
stage
|
|
3641
|
+
);
|
|
3642
|
+
assert(response.body !== null, "IMAGE_RESPONSE_INVALID", stage);
|
|
3643
|
+
const reader = response.body.getReader();
|
|
3644
|
+
const chunks = [];
|
|
3645
|
+
let size = 0;
|
|
3646
|
+
try {
|
|
3647
|
+
while (true) {
|
|
3648
|
+
const next = await abortable(reader.read(), requestSignal, stage);
|
|
3649
|
+
requestSignal.throwIfAborted();
|
|
3650
|
+
if (next.done) break;
|
|
3651
|
+
size += next.value.byteLength;
|
|
3652
|
+
assert(size <= MAX_JSON_BYTES, "IMAGE_RESPONSE_TOO_LARGE", stage);
|
|
3653
|
+
chunks.push(next.value);
|
|
3654
|
+
}
|
|
3655
|
+
let value;
|
|
3656
|
+
try {
|
|
3657
|
+
value = JSON.parse(Buffer.concat(chunks, size).toString("utf8"));
|
|
3658
|
+
} catch (error) {
|
|
3659
|
+
throw new SessionImageUploadError("IMAGE_RESPONSE_INVALID", stage, void 0, error);
|
|
3660
|
+
}
|
|
3661
|
+
return parse(schema, value, stage);
|
|
3662
|
+
} finally {
|
|
3663
|
+
reader.releaseLock();
|
|
3664
|
+
}
|
|
3665
|
+
});
|
|
3666
|
+
}
|
|
3667
|
+
async #request(url, init, stage, signal, consume) {
|
|
3668
|
+
assert(!signal.aborted, "IMAGE_ABORTED", stage);
|
|
3669
|
+
const controller = new AbortController();
|
|
3670
|
+
const abort = () => controller.abort();
|
|
3671
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
3672
|
+
let timedOut = false;
|
|
3673
|
+
const timer = setTimeout(() => {
|
|
3674
|
+
timedOut = true;
|
|
3675
|
+
controller.abort();
|
|
3676
|
+
}, REQUEST_TIMEOUT_MS);
|
|
3677
|
+
timer.unref();
|
|
3678
|
+
try {
|
|
3679
|
+
const response = await this.#fetch(url, {
|
|
3680
|
+
...init,
|
|
3681
|
+
signal: controller.signal,
|
|
3682
|
+
redirect: "error",
|
|
3683
|
+
credentials: "omit",
|
|
3684
|
+
cache: "no-store",
|
|
3685
|
+
referrerPolicy: "no-referrer"
|
|
3686
|
+
});
|
|
3687
|
+
try {
|
|
3688
|
+
controller.signal.throwIfAborted();
|
|
3689
|
+
assert(!response.redirected, "IMAGE_RESPONSE_INVALID", stage);
|
|
3690
|
+
return await consume(response, controller.signal);
|
|
3691
|
+
} finally {
|
|
3692
|
+
await response.body?.cancel();
|
|
3693
|
+
}
|
|
3694
|
+
} catch (error) {
|
|
3695
|
+
if (controller.signal.aborted) {
|
|
3696
|
+
throw new SessionImageUploadError(timedOut ? "IMAGE_TIMEOUT" : "IMAGE_ABORTED", stage, void 0, error);
|
|
3697
|
+
}
|
|
3698
|
+
if (error instanceof SessionImageUploadError) throw error;
|
|
3699
|
+
throw new SessionImageUploadError("IMAGE_NETWORK_ERROR", stage, void 0, error);
|
|
3700
|
+
} finally {
|
|
3701
|
+
clearTimeout(timer);
|
|
3702
|
+
signal.removeEventListener("abort", abort);
|
|
3703
|
+
}
|
|
3704
|
+
}
|
|
3705
|
+
};
|
|
3706
|
+
|
|
3100
3707
|
// src/service/requests/request-errors.ts
|
|
3101
3708
|
function operationErrorCode(agentId, operation, error) {
|
|
3102
3709
|
if (isSessionRestoreFailure(error)) return "SESSION_RESTORE_FAILED";
|
|
@@ -3113,7 +3720,7 @@ function unsupportedOperation(error) {
|
|
|
3113
3720
|
// src/service/requests/communication-operations.ts
|
|
3114
3721
|
var compress2 = promisify2(gzip3);
|
|
3115
3722
|
function createCommunicationOperations(options) {
|
|
3116
|
-
const { adapter, publications } = options;
|
|
3723
|
+
const { adapter, publications, imageBlob } = options;
|
|
3117
3724
|
const directoryLister = new SessionDirectoryLister(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
|
|
3118
3725
|
const fileReader = new SessionFileReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId));
|
|
3119
3726
|
const gitDiffReader = new SessionGitDiffReader(async (sessionId) => adapter.getSessionDirectoryMetadata?.(sessionId), options.gitDiffDirectory);
|
|
@@ -3122,6 +3729,24 @@ function createCommunicationOperations(options) {
|
|
|
3122
3729
|
const renameSession = adapter.renameSession?.bind(adapter);
|
|
3123
3730
|
return [
|
|
3124
3731
|
...options.workbenchOperations,
|
|
3732
|
+
...imageBlob === void 0 ? [] : [
|
|
3733
|
+
sessionCommunicationOperation({
|
|
3734
|
+
operation: TIMELINE_IMAGE_BLOB_OPERATION,
|
|
3735
|
+
version: TIMELINE_IMAGE_BLOB_VERSION,
|
|
3736
|
+
schema: TimelineImageBlobRequestSchema,
|
|
3737
|
+
handle: async ({ sha256 }, request) => {
|
|
3738
|
+
const assertCurrent = options.captureRequestAccess();
|
|
3739
|
+
assertCurrent();
|
|
3740
|
+
const result = await imageBlob.publish(request.sessionId, sha256, assertCurrent);
|
|
3741
|
+
assertCurrent();
|
|
3742
|
+
return SessionCommunicationDataSchema.parse(result);
|
|
3743
|
+
},
|
|
3744
|
+
onError: (error) => ({
|
|
3745
|
+
code: error instanceof TimelineAssetError || error instanceof SessionImageUploadError || error instanceof SessionNotSharedError || error instanceof AgentSessionRenameError ? error.code : "IMAGE_BLOB_FAILED",
|
|
3746
|
+
message: "The cloud image is unavailable"
|
|
3747
|
+
})
|
|
3748
|
+
})
|
|
3749
|
+
],
|
|
3125
3750
|
...renameSession === void 0 ? [] : [
|
|
3126
3751
|
sessionCommunicationOperation({
|
|
3127
3752
|
operation: SESSION_RENAME_OPERATION,
|
|
@@ -3198,16 +3823,16 @@ function createCommunicationOperations(options) {
|
|
|
3198
3823
|
}),
|
|
3199
3824
|
onError: (error) => unsupportedOperation(error) ?? (error instanceof SessionDirectoryError ? { code: error.code, message: error.message } : { code: "DIRECTORY_UNAVAILABLE", message: "The directory is unavailable" })
|
|
3200
3825
|
}),
|
|
3201
|
-
sessionCommunicationOperation({
|
|
3826
|
+
...[SESSION_FILE_READ_VERSION, SESSION_FILE_READ_V2_VERSION].map((version) => sessionCommunicationOperation({
|
|
3202
3827
|
operation: SESSION_FILE_READ_OPERATION,
|
|
3203
|
-
version
|
|
3828
|
+
version,
|
|
3204
3829
|
schema: SessionFileReadDataSchema,
|
|
3205
|
-
handle: ({ path, rootRevision }, request) => fileReader.read(request.sessionId, path, rootRevision),
|
|
3830
|
+
handle: ({ path, rootRevision }, request) => fileReader.read(request.sessionId, path, rootRevision, version),
|
|
3206
3831
|
onError: (error) => unsupportedOperation(error) ?? {
|
|
3207
3832
|
code: error instanceof SessionFileReadError ? error.code : "FILE_UNAVAILABLE",
|
|
3208
3833
|
message: "The file preview is unavailable"
|
|
3209
3834
|
}
|
|
3210
|
-
}),
|
|
3835
|
+
})),
|
|
3211
3836
|
sessionCommunicationOperation({
|
|
3212
3837
|
operation: SESSION_RESOURCE_READ_OPERATION,
|
|
3213
3838
|
version: SESSION_RESOURCE_READ_VERSION,
|
|
@@ -3269,6 +3894,7 @@ var SessionCommunicationHandler = class {
|
|
|
3269
3894
|
return assertCurrent;
|
|
3270
3895
|
},
|
|
3271
3896
|
incrementalOperations: options.incrementalOperations.filter((operation) => config.timelineIncrementalEnabled !== false || operation.operation === TIMELINE_IMAGE_OPERATION),
|
|
3897
|
+
...options.imageBlob === void 0 ? {} : { imageBlob: options.imageBlob },
|
|
3272
3898
|
workbenchOperations: this.#operations === void 0 ? [] : createWorkbenchOperations({
|
|
3273
3899
|
...workbench === void 0 ? {} : { runtime: workbench },
|
|
3274
3900
|
...researchWorkbench === void 0 ? {} : { research: researchWorkbench },
|
|
@@ -3437,7 +4063,7 @@ var SessionCommunicationHandler = class {
|
|
|
3437
4063
|
}
|
|
3438
4064
|
const clientId = scope.clientId;
|
|
3439
4065
|
const owner = [config.userId, machineId, clientId, request.sessionId];
|
|
3440
|
-
const key = (parts) =>
|
|
4066
|
+
const key = (parts) => createHash8("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 24);
|
|
3441
4067
|
const knownSource = this.#researchParents.get(request.sessionId) ?? adapter.sessionPublicationSource?.(request.sessionId) ?? request.sessionId;
|
|
3442
4068
|
const arrivalPublication = adapter.isSessionVisible?.(knownSource) === true ? publications.capture(knownSource) : void 0;
|
|
3443
4069
|
let researchParent = this.#researchParents.get(request.sessionId);
|
|
@@ -3470,7 +4096,7 @@ var SessionCommunicationHandler = class {
|
|
|
3470
4096
|
}
|
|
3471
4097
|
if (researchParent !== void 0) {
|
|
3472
4098
|
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);
|
|
4099
|
+
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
4100
|
if (!unifiedSync && !["research.timeline", "research.send", "research.abort", "research.delete", "operation.get", "insights.read"].includes(request.operation)) {
|
|
3475
4101
|
reject("research-isolated");
|
|
3476
4102
|
emit("session.communication", sessionCommunicationError(request, {
|
|
@@ -3617,7 +4243,7 @@ var SessionCommunicationHandler = class {
|
|
|
3617
4243
|
};
|
|
3618
4244
|
|
|
3619
4245
|
// src/desktop-workspaces.ts
|
|
3620
|
-
import { createHash as
|
|
4246
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
3621
4247
|
import { readdir, realpath as realpath5, stat as stat6 } from "node:fs/promises";
|
|
3622
4248
|
import { homedir as homedir2 } from "node:os";
|
|
3623
4249
|
import { basename as basename2, join as join8, normalize } from "node:path";
|
|
@@ -3633,7 +4259,7 @@ function desktopRootCandidates(environment2 = process.env) {
|
|
|
3633
4259
|
}
|
|
3634
4260
|
function workspaceId(path) {
|
|
3635
4261
|
const normalizedPath = workspacePathKey(path);
|
|
3636
|
-
return
|
|
4262
|
+
return createHash9("sha256").update(normalizedPath).digest("base64url").slice(0, 32);
|
|
3637
4263
|
}
|
|
3638
4264
|
function workspacePathKey(path) {
|
|
3639
4265
|
const normalizedPath = normalize(path);
|
|
@@ -4395,9 +5021,9 @@ var SessionCommandsHandler = class {
|
|
|
4395
5021
|
};
|
|
4396
5022
|
|
|
4397
5023
|
// src/service/requests/session-prompt.ts
|
|
4398
|
-
import { createHash as
|
|
5024
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
4399
5025
|
function promptOperationFingerprint(request) {
|
|
4400
|
-
return
|
|
5026
|
+
return 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
5027
|
name,
|
|
4402
5028
|
mimeType,
|
|
4403
5029
|
sha256
|
|
@@ -4643,8 +5269,8 @@ import { randomUUID as randomUUID5 } from "node:crypto";
|
|
|
4643
5269
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4644
5270
|
import { Role, TaskState } from "@a2a-js/sdk";
|
|
4645
5271
|
import { ClientFactory, DefaultAgentCardResolver, JsonRpcTransportFactory } from "@a2a-js/sdk/client";
|
|
4646
|
-
import { z as
|
|
4647
|
-
var TextSchema =
|
|
5272
|
+
import { z as z7 } from "zod";
|
|
5273
|
+
var TextSchema = z7.string().max(16e3).refine((text) => text.trim().length > 0);
|
|
4648
5274
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
4649
5275
|
var MAX_RESPONSE_BYTES = 262144;
|
|
4650
5276
|
function relayEndpoint(relayUrl, target) {
|
|
@@ -4803,7 +5429,7 @@ var A2aPeerClient = class {
|
|
|
4803
5429
|
};
|
|
4804
5430
|
|
|
4805
5431
|
// src/a2a-server.ts
|
|
4806
|
-
import { createHash as
|
|
5432
|
+
import { createHash as createHash11, randomUUID as randomUUID6 } from "node:crypto";
|
|
4807
5433
|
import {
|
|
4808
5434
|
AgentCard,
|
|
4809
5435
|
Role as Role2,
|
|
@@ -4829,7 +5455,7 @@ import {
|
|
|
4829
5455
|
ServerCallContext,
|
|
4830
5456
|
validateVersion
|
|
4831
5457
|
} from "@a2a-js/sdk/server";
|
|
4832
|
-
import { z as
|
|
5458
|
+
import { z as z8 } from "zod";
|
|
4833
5459
|
var MAX_ACTIVE = 4;
|
|
4834
5460
|
var MAX_TASKS = 128;
|
|
4835
5461
|
var MAX_RECEIPTS = 256;
|
|
@@ -4838,52 +5464,52 @@ var TURN_TIMEOUT_MS = 12e4;
|
|
|
4838
5464
|
var SHORT_REQUEST_TIMEOUT_MS = 9e3;
|
|
4839
5465
|
var MAX_TEXT2 = 16e3;
|
|
4840
5466
|
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:
|
|
5467
|
+
var idSchema = z8.string().min(1).max(256).regex(/^[^\s\u0000-\u001f\u007f]+$/u);
|
|
5468
|
+
var optionalIdSchema = z8.union([z8.literal(""), idSchema]).optional();
|
|
5469
|
+
var historySchema = z8.number().int().min(0).max(128).optional();
|
|
5470
|
+
var emptyMetadataSchema = z8.object({}).strict().optional();
|
|
5471
|
+
var tenantSchema = z8.literal("").optional();
|
|
5472
|
+
var rpcSchema = z8.object({
|
|
5473
|
+
jsonrpc: z8.literal("2.0"),
|
|
5474
|
+
id: z8.union([idSchema, z8.number().int().safe(), z8.null()]),
|
|
5475
|
+
method: z8.string().min(1).max(128),
|
|
5476
|
+
params: z8.unknown()
|
|
4851
5477
|
}).strict();
|
|
4852
|
-
var sendSchema =
|
|
5478
|
+
var sendSchema = z8.object({
|
|
4853
5479
|
tenant: tenantSchema,
|
|
4854
|
-
message:
|
|
5480
|
+
message: z8.object({
|
|
4855
5481
|
messageId: idSchema,
|
|
4856
5482
|
contextId: optionalIdSchema,
|
|
4857
5483
|
taskId: optionalIdSchema,
|
|
4858
|
-
role:
|
|
4859
|
-
parts:
|
|
4860
|
-
text:
|
|
4861
|
-
mediaType:
|
|
4862
|
-
filename:
|
|
5484
|
+
role: z8.union([z8.literal("ROLE_USER"), z8.literal(Role2.ROLE_USER)]),
|
|
5485
|
+
parts: z8.array(z8.object({
|
|
5486
|
+
text: z8.string().max(MAX_TEXT2),
|
|
5487
|
+
mediaType: z8.union([z8.literal(""), z8.literal("text/plain")]).optional(),
|
|
5488
|
+
filename: z8.literal("").optional(),
|
|
4863
5489
|
metadata: emptyMetadataSchema
|
|
4864
5490
|
}).strict()).min(1).max(32),
|
|
4865
5491
|
metadata: emptyMetadataSchema,
|
|
4866
|
-
extensions:
|
|
4867
|
-
referenceTaskIds:
|
|
5492
|
+
extensions: z8.array(z8.never()).optional(),
|
|
5493
|
+
referenceTaskIds: z8.array(z8.never()).optional()
|
|
4868
5494
|
}).strict().refine((message) => {
|
|
4869
5495
|
const text = message.parts.map((part) => part.text).join("\n");
|
|
4870
5496
|
return text.trim().length > 0 && text.length <= MAX_TEXT2;
|
|
4871
5497
|
}, "A message must contain nonempty plain text of at most 16000 characters."),
|
|
4872
|
-
configuration:
|
|
4873
|
-
acceptedOutputModes:
|
|
4874
|
-
returnImmediately:
|
|
5498
|
+
configuration: z8.object({
|
|
5499
|
+
acceptedOutputModes: z8.array(z8.literal("text/plain")).max(1).optional(),
|
|
5500
|
+
returnImmediately: z8.boolean().optional(),
|
|
4875
5501
|
historyLength: historySchema
|
|
4876
5502
|
}).strict().optional(),
|
|
4877
5503
|
metadata: emptyMetadataSchema
|
|
4878
5504
|
}).strict();
|
|
4879
|
-
var getSchema =
|
|
4880
|
-
var cancelSchema =
|
|
4881
|
-
var listSchema2 =
|
|
5505
|
+
var getSchema = z8.object({ tenant: tenantSchema, id: idSchema, historyLength: historySchema }).strict();
|
|
5506
|
+
var cancelSchema = z8.object({ tenant: tenantSchema, id: idSchema, metadata: emptyMetadataSchema }).strict();
|
|
5507
|
+
var listSchema2 = z8.object({
|
|
4882
5508
|
tenant: tenantSchema,
|
|
4883
5509
|
contextId: optionalIdSchema,
|
|
4884
|
-
status:
|
|
4885
|
-
|
|
4886
|
-
|
|
5510
|
+
status: z8.union([
|
|
5511
|
+
z8.number().int().min(0).max(8),
|
|
5512
|
+
z8.enum([
|
|
4887
5513
|
"TASK_STATE_UNSPECIFIED",
|
|
4888
5514
|
"TASK_STATE_SUBMITTED",
|
|
4889
5515
|
"TASK_STATE_WORKING",
|
|
@@ -4895,11 +5521,11 @@ var listSchema2 = z6.object({
|
|
|
4895
5521
|
"TASK_STATE_AUTH_REQUIRED"
|
|
4896
5522
|
])
|
|
4897
5523
|
]).optional(),
|
|
4898
|
-
pageSize:
|
|
4899
|
-
pageToken:
|
|
5524
|
+
pageSize: z8.number().int().min(1).max(100).optional(),
|
|
5525
|
+
pageToken: z8.string().regex(/^(?:|0|[1-9]\d{0,3})$/u).optional(),
|
|
4900
5526
|
historyLength: historySchema,
|
|
4901
|
-
statusTimestampAfter:
|
|
4902
|
-
includeArtifacts:
|
|
5527
|
+
statusTimestampAfter: z8.iso.datetime({ offset: true }).optional(),
|
|
5528
|
+
includeArtifacts: z8.boolean().optional()
|
|
4903
5529
|
}).strict();
|
|
4904
5530
|
function deferred() {
|
|
4905
5531
|
let resolve5 = () => {
|
|
@@ -5105,7 +5731,7 @@ var A2aPeerServer = class {
|
|
|
5105
5731
|
if (message === void 0) throw new RequestMalformedError("A message is required.");
|
|
5106
5732
|
const scope = scopedOwner(context);
|
|
5107
5733
|
const key = JSON.stringify([scope, message.messageId]);
|
|
5108
|
-
const fingerprint =
|
|
5734
|
+
const fingerprint = createHash11("sha256").update(JSON.stringify(SendMessageRequest.toJSON(params))).digest("hex");
|
|
5109
5735
|
const previous = this.#receipts.get(key);
|
|
5110
5736
|
if (previous !== void 0) {
|
|
5111
5737
|
if (previous.fingerprint !== fingerprint) throw new RequestMalformedError("messageId was already used with different content.");
|
|
@@ -5980,7 +6606,7 @@ var SessionInventory = class {
|
|
|
5980
6606
|
};
|
|
5981
6607
|
|
|
5982
6608
|
// src/snapshot-audience.ts
|
|
5983
|
-
import { createHash as
|
|
6609
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
5984
6610
|
var SnapshotAudience = class {
|
|
5985
6611
|
constructor(send) {
|
|
5986
6612
|
this.send = send;
|
|
@@ -6009,9 +6635,9 @@ var SnapshotAudience = class {
|
|
|
6009
6635
|
async publish(snapshot, recipient) {
|
|
6010
6636
|
const generation = this.#generation;
|
|
6011
6637
|
const { sequence: _sequence, generatedAt: _generatedAt, ...content } = snapshot;
|
|
6012
|
-
const
|
|
6638
|
+
const digest2 = createHash12("sha256").update(JSON.stringify(content)).digest("base64url");
|
|
6013
6639
|
if (recipient !== void 0) {
|
|
6014
|
-
await this.#direct(snapshot, recipient,
|
|
6640
|
+
await this.#direct(snapshot, recipient, digest2, generation);
|
|
6015
6641
|
return;
|
|
6016
6642
|
}
|
|
6017
6643
|
if (!this.#negotiated || this.#registrationBaseline) {
|
|
@@ -6023,10 +6649,10 @@ var SnapshotAudience = class {
|
|
|
6023
6649
|
}
|
|
6024
6650
|
const pending = [];
|
|
6025
6651
|
for (const [clientId, baseline] of this.#audiences) {
|
|
6026
|
-
if (baseline.digest ===
|
|
6652
|
+
if (baseline.digest === digest2) {
|
|
6027
6653
|
this.#metrics.unchangedRecipients += 1;
|
|
6028
6654
|
} else {
|
|
6029
|
-
pending.push(this.#direct(snapshot, clientId,
|
|
6655
|
+
pending.push(this.#direct(snapshot, clientId, digest2, generation));
|
|
6030
6656
|
}
|
|
6031
6657
|
}
|
|
6032
6658
|
if (this.#audiences.size === 0) this.#metrics.withoutRecipients += 1;
|
|
@@ -6039,12 +6665,12 @@ var SnapshotAudience = class {
|
|
|
6039
6665
|
this.#metrics = this.#emptyMetrics();
|
|
6040
6666
|
return metrics;
|
|
6041
6667
|
}
|
|
6042
|
-
async #direct(snapshot, recipient,
|
|
6668
|
+
async #direct(snapshot, recipient, digest2, generation) {
|
|
6043
6669
|
const baseline = this.#audiences.get(recipient);
|
|
6044
6670
|
this.#metrics.directedAttempts += 1;
|
|
6045
6671
|
await this.send(snapshot, recipient);
|
|
6046
6672
|
if (generation === this.#generation && baseline !== void 0 && this.#audiences.get(recipient) === baseline) {
|
|
6047
|
-
baseline.digest =
|
|
6673
|
+
baseline.digest = digest2;
|
|
6048
6674
|
}
|
|
6049
6675
|
}
|
|
6050
6676
|
#emptyMetrics() {
|
|
@@ -6141,106 +6767,8 @@ var ConversationAudience = class {
|
|
|
6141
6767
|
};
|
|
6142
6768
|
|
|
6143
6769
|
// src/timeline-incremental-transport.ts
|
|
6144
|
-
import { createHash as
|
|
6770
|
+
import { createHash as createHash13, randomUUID as randomUUID8 } from "node:crypto";
|
|
6145
6771
|
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
6772
|
function journalDiagnostic(state) {
|
|
6245
6773
|
return {
|
|
6246
6774
|
currentSequence: state.sequence,
|
|
@@ -6373,7 +6901,7 @@ var TimelineIncrementalTransport = class {
|
|
|
6373
6901
|
}
|
|
6374
6902
|
#observeColdStep(state, upsert) {
|
|
6375
6903
|
const cold = state.coldSteps;
|
|
6376
|
-
const key =
|
|
6904
|
+
const key = createHash13("sha256").update(JSON.stringify([upsert.activityId, upsert.step.id])).digest("hex");
|
|
6377
6905
|
const revision = cold.revisions.get(key);
|
|
6378
6906
|
if (revision !== void 0 && upsert.revision < revision) return false;
|
|
6379
6907
|
cold.revisions.delete(key);
|
|
@@ -6383,7 +6911,7 @@ var TimelineIncrementalTransport = class {
|
|
|
6383
6911
|
if (oldest === void 0) break;
|
|
6384
6912
|
cold.revisions.delete(oldest);
|
|
6385
6913
|
}
|
|
6386
|
-
const fingerprint =
|
|
6914
|
+
const fingerprint = createHash13("sha256").update(JSON.stringify(upsert)).digest("hex");
|
|
6387
6915
|
const observed = cold.fingerprints.has(fingerprint);
|
|
6388
6916
|
cold.fingerprints.delete(fingerprint);
|
|
6389
6917
|
cold.fingerprints.add(fingerprint);
|
|
@@ -6664,6 +7192,104 @@ function canAppend(previous, item) {
|
|
|
6664
7192
|
return isDeepStrictEqual(oldMetadata, newMetadata);
|
|
6665
7193
|
}
|
|
6666
7194
|
|
|
7195
|
+
// src/timeline-image-blob.ts
|
|
7196
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
7197
|
+
import sharp3 from "sharp";
|
|
7198
|
+
var TimelineImageBlobPublisher = class {
|
|
7199
|
+
constructor(images, uploader) {
|
|
7200
|
+
this.images = images;
|
|
7201
|
+
this.uploader = uploader;
|
|
7202
|
+
}
|
|
7203
|
+
images;
|
|
7204
|
+
uploader;
|
|
7205
|
+
#references = /* @__PURE__ */ new Map();
|
|
7206
|
+
#flights = /* @__PURE__ */ new Map();
|
|
7207
|
+
#closed = false;
|
|
7208
|
+
async publish(sessionId, sha256, assertCurrent) {
|
|
7209
|
+
TimelineImageBlobRequestSchema.parse({ sha256 });
|
|
7210
|
+
assertCurrent();
|
|
7211
|
+
if (this.#closed) throw new TimelineAssetError("IMAGE_BLOB_CLOSED", "The image publisher is closed");
|
|
7212
|
+
const key = JSON.stringify([sessionId, sha256]);
|
|
7213
|
+
const cached = this.#references.get(key);
|
|
7214
|
+
if (cached !== void 0) {
|
|
7215
|
+
this.#references.delete(key);
|
|
7216
|
+
if (cached.image.expiresAt > Date.now() + 3e4) {
|
|
7217
|
+
this.#references.set(key, cached);
|
|
7218
|
+
return cached;
|
|
7219
|
+
}
|
|
7220
|
+
}
|
|
7221
|
+
let flight = this.#flights.get(key);
|
|
7222
|
+
if (flight === void 0) {
|
|
7223
|
+
if (this.#flights.size >= 3) throw new TimelineAssetError("IMAGE_BLOB_BUSY", "Too many image uploads");
|
|
7224
|
+
const controller = new AbortController();
|
|
7225
|
+
const check = () => {
|
|
7226
|
+
controller.signal.throwIfAborted();
|
|
7227
|
+
assertCurrent();
|
|
7228
|
+
};
|
|
7229
|
+
const promise = this.#publish(sessionId, sha256, controller.signal, check).then((result2) => {
|
|
7230
|
+
check();
|
|
7231
|
+
this.#references.set(key, result2);
|
|
7232
|
+
while (this.#references.size > 256) {
|
|
7233
|
+
const oldest = this.#references.keys().next().value;
|
|
7234
|
+
if (oldest === void 0) break;
|
|
7235
|
+
this.#references.delete(oldest);
|
|
7236
|
+
}
|
|
7237
|
+
return result2;
|
|
7238
|
+
}).finally(() => {
|
|
7239
|
+
this.#flights.delete(key);
|
|
7240
|
+
});
|
|
7241
|
+
flight = { sessionId, controller, promise };
|
|
7242
|
+
this.#flights.set(key, flight);
|
|
7243
|
+
}
|
|
7244
|
+
const result = await flight.promise;
|
|
7245
|
+
assertCurrent();
|
|
7246
|
+
return result;
|
|
7247
|
+
}
|
|
7248
|
+
clearSession(sessionId) {
|
|
7249
|
+
for (const [key, reference] of this.#references) {
|
|
7250
|
+
if (reference.image.sessionId === sessionId) this.#references.delete(key);
|
|
7251
|
+
}
|
|
7252
|
+
for (const flight of this.#flights.values()) {
|
|
7253
|
+
if (flight.sessionId === sessionId) flight.controller.abort();
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
reset() {
|
|
7257
|
+
this.#references.clear();
|
|
7258
|
+
for (const flight of this.#flights.values()) flight.controller.abort();
|
|
7259
|
+
}
|
|
7260
|
+
async close() {
|
|
7261
|
+
this.#closed = true;
|
|
7262
|
+
this.reset();
|
|
7263
|
+
await Promise.allSettled([...this.#flights.values()].map((flight) => flight.promise));
|
|
7264
|
+
}
|
|
7265
|
+
async #publish(sessionId, sha256, signal, check) {
|
|
7266
|
+
const asset = this.images.readAsset(sessionId, sha256);
|
|
7267
|
+
const extension = { "image/png": ".png", "image/jpeg": ".jpg", "image/webp": ".webp" }[asset.mimeType];
|
|
7268
|
+
if (extension === void 0) throw new TimelineAssetError("IMAGE_BLOB_UNSUPPORTED", "This image requires encrypted transfer");
|
|
7269
|
+
if (createHash14("sha256").update(asset.bytes).digest("hex") !== sha256) {
|
|
7270
|
+
throw new TimelineAssetError("IMAGE_INVALID", "Image content does not match its reference");
|
|
7271
|
+
}
|
|
7272
|
+
let bytes;
|
|
7273
|
+
try {
|
|
7274
|
+
await validateRawImage(asset.bytes, extension, check);
|
|
7275
|
+
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();
|
|
7276
|
+
} catch (error) {
|
|
7277
|
+
check();
|
|
7278
|
+
if (error instanceof SessionFileReadError) {
|
|
7279
|
+
throw new TimelineAssetError("IMAGE_BLOB_UNSUPPORTED", "The image cannot be safely stored as WebP", { cause: error });
|
|
7280
|
+
}
|
|
7281
|
+
throw new TimelineAssetError("IMAGE_BLOB_ENCODING_FAILED", "Unable to encode the image for storage", { cause: error });
|
|
7282
|
+
}
|
|
7283
|
+
check();
|
|
7284
|
+
if (bytes.length > SESSION_IMAGE_MAX_BYTES) {
|
|
7285
|
+
throw new TimelineAssetError("IMAGE_BLOB_TOO_LARGE", "The lossless WebP exceeds the image storage limit");
|
|
7286
|
+
}
|
|
7287
|
+
const image = await this.uploader.upload(sessionId, bytes, signal, check);
|
|
7288
|
+
check();
|
|
7289
|
+
return TimelineImageBlobResultSchema.parse({ sha256, image });
|
|
7290
|
+
}
|
|
7291
|
+
};
|
|
7292
|
+
|
|
6667
7293
|
// src/secure-traffic-metrics.ts
|
|
6668
7294
|
var SecureTrafficMetrics = class {
|
|
6669
7295
|
#samples = /* @__PURE__ */ new Map();
|
|
@@ -7208,6 +7834,14 @@ var PcAgent = class {
|
|
|
7208
7834
|
this.#timelineIncremental = new TimelineIncrementalTransport({
|
|
7209
7835
|
readHead: (sessionId) => this.adapter.readSessionTimelinePage(sessionId, { pageSize: 100 })
|
|
7210
7836
|
});
|
|
7837
|
+
const imageSettings = config.sessionImages === void 0 ? void 0 : SessionImagesConfigSchema.parse(config.sessionImages);
|
|
7838
|
+
this.#imageBlob = imageSettings === void 0 ? void 0 : new TimelineImageBlobPublisher(this.#timelineIncremental.images, new SessionImageUploader({
|
|
7839
|
+
relayUrl: config.relayUrl,
|
|
7840
|
+
relayToken: config.relayToken,
|
|
7841
|
+
userId: config.userId,
|
|
7842
|
+
machineId: identity2.machineId,
|
|
7843
|
+
...imageSettings
|
|
7844
|
+
}));
|
|
7211
7845
|
const emit = (event, payload) => this.#emitSecure(event, payload);
|
|
7212
7846
|
const handlerOptions = { adapter, config, machineId: identity2.machineId, logger, emit };
|
|
7213
7847
|
this.#communication = new SessionCommunicationHandler({
|
|
@@ -7218,6 +7852,7 @@ var PcAgent = class {
|
|
|
7218
7852
|
audience: this.#conversationAudience,
|
|
7219
7853
|
metrics: this.#operationMetrics,
|
|
7220
7854
|
incrementalOperations: this.#timelineIncremental.operations,
|
|
7855
|
+
...this.#imageBlob === void 0 ? {} : { imageBlob: this.#imageBlob },
|
|
7221
7856
|
captureResearchAccess: (sessionId) => captureResearchAccess(adapter, sessionId),
|
|
7222
7857
|
captureWorkbenchAccess: (sessionId) => captureWorkbenchAccess(adapter, sessionId),
|
|
7223
7858
|
...research === void 0 ? {} : { research },
|
|
@@ -7244,6 +7879,7 @@ var PcAgent = class {
|
|
|
7244
7879
|
supportsAgentControls: (clientId) => this.#supportsAgentControls(clientId),
|
|
7245
7880
|
refreshSessions: (clientId) => this.#inventoryPublisher.request(clientId, true),
|
|
7246
7881
|
removeSession: (sessionId) => {
|
|
7882
|
+
this.#imageBlob?.clearSession(sessionId);
|
|
7247
7883
|
this.#timelineIncremental.remove(sessionId);
|
|
7248
7884
|
this.#conversationAudience.removeSession(sessionId);
|
|
7249
7885
|
this.#publication.revoke(sessionId);
|
|
@@ -7424,6 +8060,7 @@ var PcAgent = class {
|
|
|
7424
8060
|
},
|
|
7425
8061
|
onTimelineInvalidated: (sessionId, invalidation) => {
|
|
7426
8062
|
if (this.adapter.isSessionVisible?.(sessionId) === false) return;
|
|
8063
|
+
this.#imageBlob?.clearSession(sessionId);
|
|
7427
8064
|
this.#publication.capture(sessionId);
|
|
7428
8065
|
const envelope = SessionTimelineInvalidatedSchema.parse({
|
|
7429
8066
|
userId: this.config.userId,
|
|
@@ -7573,6 +8210,7 @@ var PcAgent = class {
|
|
|
7573
8210
|
#readMetrics = { readStarts: 0, readJoins: 0, untrackedReads: 0, peakInFlightPerSession: 0 };
|
|
7574
8211
|
#conversationAudience = new ConversationAudience();
|
|
7575
8212
|
#timelineIncremental;
|
|
8213
|
+
#imageBlob;
|
|
7576
8214
|
#pendingAcks = /* @__PURE__ */ new Set();
|
|
7577
8215
|
#connectionGeneration = 0;
|
|
7578
8216
|
#heartbeatTimer;
|
|
@@ -7833,6 +8471,7 @@ var PcAgent = class {
|
|
|
7833
8471
|
return this.#stopped;
|
|
7834
8472
|
}
|
|
7835
8473
|
this.#stopping = true;
|
|
8474
|
+
const imagesStopped = this.#imageBlob?.close();
|
|
7836
8475
|
const peerStopped = this.#peerChat.close();
|
|
7837
8476
|
const a2aStopped = this.#a2aServer.close();
|
|
7838
8477
|
this.#machineRegistered = false;
|
|
@@ -7859,6 +8498,7 @@ var PcAgent = class {
|
|
|
7859
8498
|
this.adapter.stop(),
|
|
7860
8499
|
peerStopped,
|
|
7861
8500
|
a2aStopped,
|
|
8501
|
+
...imagesStopped === void 0 ? [] : [imagesStopped],
|
|
7862
8502
|
...this.research === void 0 ? [] : [this.research.stop()]
|
|
7863
8503
|
]);
|
|
7864
8504
|
const operationsStopped = await Promise.allSettled(
|
|
@@ -7915,6 +8555,7 @@ var PcAgent = class {
|
|
|
7915
8555
|
this.#peerTransportNegotiated = false;
|
|
7916
8556
|
this.#a2aNegotiated = false;
|
|
7917
8557
|
this.#peerChat.reset(new Error("Relay disconnected; peer conversation will not be replayed."));
|
|
8558
|
+
this.#imageBlob?.reset();
|
|
7918
8559
|
this.#a2aServer.reset(new Error("Relay disconnected; A2A execution will not be replayed."));
|
|
7919
8560
|
this.#conversationAudience.reset();
|
|
7920
8561
|
this.#cancelSnapshotSync();
|
|
@@ -8206,6 +8847,7 @@ var PcAgent = class {
|
|
|
8206
8847
|
if (this.adapter.isSessionVisible === void 0) return;
|
|
8207
8848
|
let removed = 0;
|
|
8208
8849
|
for (const id of this.#publication.revokeHidden()) {
|
|
8850
|
+
this.#imageBlob?.clearSession(id);
|
|
8209
8851
|
this.#timelineIncremental.remove(id);
|
|
8210
8852
|
this.#conversationAudience.removeSession(id);
|
|
8211
8853
|
removed += 1;
|
|
@@ -8699,46 +9341,46 @@ function contextItem(item) {
|
|
|
8699
9341
|
|
|
8700
9342
|
// src/research-sessions.ts
|
|
8701
9343
|
import { execFile as execFile3 } from "node:child_process";
|
|
8702
|
-
import { createHash as
|
|
9344
|
+
import { createHash as createHash15, randomUUID as randomUUID12 } from "node:crypto";
|
|
8703
9345
|
import { mkdir as mkdir5, lstat as lstat8, realpath as realpath8 } from "node:fs/promises";
|
|
8704
9346
|
import { createRequire } from "node:module";
|
|
8705
9347
|
import { join as join12, resolve as resolve4 } from "node:path";
|
|
8706
9348
|
import { promisify as promisify4 } from "node:util";
|
|
8707
|
-
import { z as
|
|
9349
|
+
import { z as z11 } from "zod";
|
|
8708
9350
|
|
|
8709
9351
|
// src/research-thread-store.ts
|
|
8710
9352
|
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
8711
9353
|
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
9354
|
import { join as join10, resolve as resolve3 } from "node:path";
|
|
8713
|
-
import { z as
|
|
9355
|
+
import { z as z9 } from "zod";
|
|
8714
9356
|
var RESEARCH_PROFILE = "research-readonly";
|
|
8715
9357
|
var RESEARCH_PROFILE_VERSION = 1;
|
|
8716
9358
|
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:
|
|
9359
|
+
var recordSchema = z9.object({
|
|
9360
|
+
threadId: z9.string().uuid(),
|
|
9361
|
+
researchSessionId: z9.string().uuid(),
|
|
9362
|
+
parentSessionId: z9.string().min(1).max(256),
|
|
9363
|
+
operationId: z9.string().min(1).max(256),
|
|
9364
|
+
fingerprint: z9.string().min(1).max(128),
|
|
9365
|
+
title: z9.string().min(1).max(200),
|
|
9366
|
+
profile: z9.literal(RESEARCH_PROFILE),
|
|
9367
|
+
profileVersion: z9.literal(RESEARCH_PROFILE_VERSION),
|
|
9368
|
+
workingDirectory: z9.string().min(1).max(8192),
|
|
9369
|
+
rootIdentity: z9.string().min(1).max(128),
|
|
9370
|
+
contextAnchor: z9.string().min(1).max(256),
|
|
9371
|
+
contextSampledAt: z9.string().datetime(),
|
|
9372
|
+
contextTruncated: z9.boolean(),
|
|
9373
|
+
chatVersion: z9.literal(1).optional(),
|
|
9374
|
+
createdAt: z9.string().datetime(),
|
|
9375
|
+
state: z9.enum(["creating", "ready", "uncertain", "failed", "deleting"]),
|
|
9376
|
+
errorCode: z9.string().max(128).optional()
|
|
8735
9377
|
}).strict();
|
|
8736
|
-
var documentSchema =
|
|
8737
|
-
version:
|
|
8738
|
-
threads:
|
|
8739
|
-
tombstones:
|
|
8740
|
-
researchSessionId:
|
|
8741
|
-
parentSessionId:
|
|
9378
|
+
var documentSchema = z9.object({
|
|
9379
|
+
version: z9.literal(1),
|
|
9380
|
+
threads: z9.array(recordSchema).max(MAX_THREADS),
|
|
9381
|
+
tombstones: z9.array(z9.object({
|
|
9382
|
+
researchSessionId: z9.string().uuid(),
|
|
9383
|
+
parentSessionId: z9.string().min(1).max(256)
|
|
8742
9384
|
}).strict()).max(1e5).optional()
|
|
8743
9385
|
}).strict();
|
|
8744
9386
|
var ResearchThreadStore = class {
|
|
@@ -8875,24 +9517,24 @@ var ResearchThreadStore = class {
|
|
|
8875
9517
|
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
8876
9518
|
import { lstat as lstat7, readFile as readFile3, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
8877
9519
|
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:
|
|
9520
|
+
import { z as z10 } from "zod";
|
|
9521
|
+
var receipt = z10.object({
|
|
9522
|
+
operationId: z10.string().min(1).max(256),
|
|
9523
|
+
fingerprint: z10.string().min(1).max(128),
|
|
9524
|
+
promptHash: z10.string().min(1).max(128),
|
|
9525
|
+
ordinal: z10.number().int().nonnegative(),
|
|
9526
|
+
submittedAt: z10.string().datetime(),
|
|
9527
|
+
state: z10.enum(["sending", "accepted"]),
|
|
9528
|
+
messageId: z10.string().min(1).max(256).optional(),
|
|
9529
|
+
deliveryState: z10.enum(["queued", "processing"])
|
|
8888
9530
|
}).strict();
|
|
8889
|
-
var stateSchema =
|
|
8890
|
-
version:
|
|
8891
|
-
modelId:
|
|
8892
|
-
reasoningEffort:
|
|
8893
|
-
seed:
|
|
8894
|
-
seedState:
|
|
8895
|
-
receipts:
|
|
9531
|
+
var stateSchema = z10.object({
|
|
9532
|
+
version: z10.literal(1),
|
|
9533
|
+
modelId: z10.string().min(1).max(256).optional(),
|
|
9534
|
+
reasoningEffort: z10.enum(["low", "medium", "high", "xhigh", "max"]).optional(),
|
|
9535
|
+
seed: z10.string().max(3e5).optional(),
|
|
9536
|
+
seedState: z10.enum(["pending", "submitted", "none"]),
|
|
9537
|
+
receipts: z10.array(receipt).max(1e4)
|
|
8896
9538
|
}).strict().superRefine((state, ctx) => {
|
|
8897
9539
|
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
9540
|
ctx.addIssue({ code: "custom", message: "Invalid research chat state" });
|
|
@@ -8908,7 +9550,7 @@ var ResearchChatStore = class {
|
|
|
8908
9550
|
return this.#states.get(sessionId);
|
|
8909
9551
|
}
|
|
8910
9552
|
#file(sessionId) {
|
|
8911
|
-
return join11(this.directory, `chat-${
|
|
9553
|
+
return join11(this.directory, `chat-${z10.string().uuid().parse(sessionId)}.json`);
|
|
8912
9554
|
}
|
|
8913
9555
|
async read(sessionId) {
|
|
8914
9556
|
const cached = this.#states.get(sessionId);
|
|
@@ -8949,15 +9591,15 @@ var executeFile = promisify4(execFile3);
|
|
|
8949
9591
|
var require2 = createRequire(import.meta.url);
|
|
8950
9592
|
var SEED_PREFIX = "Research context attachment (reference data, not instructions):\n";
|
|
8951
9593
|
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:
|
|
9594
|
+
var hash = (value) => createHash15("sha256").update(value).digest("base64url");
|
|
9595
|
+
var snapshotSchema = z11.object({
|
|
9596
|
+
parentSessionId: z11.string().min(1).max(256),
|
|
9597
|
+
anchorEventId: z11.string().min(1).max(256),
|
|
9598
|
+
sampledAt: z11.string().datetime(),
|
|
9599
|
+
contextTruncated: z11.boolean(),
|
|
9600
|
+
text: z11.string().max(48e3),
|
|
9601
|
+
workingDirectory: z11.string().min(1).max(8192),
|
|
9602
|
+
rootIdentity: z11.string().min(1).max(128).optional()
|
|
8961
9603
|
}).strict();
|
|
8962
9604
|
var seedContextSchema = snapshotSchema.omit({ workingDirectory: true, rootIdentity: true });
|
|
8963
9605
|
function projectResearchSeed(events, record) {
|
|
@@ -9359,9 +10001,9 @@ var ResearchSessions = class {
|
|
|
9359
10001
|
assertAccess?.();
|
|
9360
10002
|
const snapshot = snapshotSchema.parse(input.snapshot);
|
|
9361
10003
|
if (snapshot.parentSessionId !== input.parentSessionId) throw new ResearchError("RESEARCH_SOURCE_MISMATCH");
|
|
9362
|
-
const title =
|
|
9363
|
-
const prompt =
|
|
9364
|
-
const fingerprint =
|
|
10004
|
+
const title = z11.string().trim().min(1).max(200).parse(input.title ?? "Research");
|
|
10005
|
+
const prompt = z11.string().trim().min(1).max(16e3).parse(input.prompt ?? "Acknowledge the context briefly and wait for my research question.");
|
|
10006
|
+
const fingerprint = createHash15("sha256").update(JSON.stringify({
|
|
9365
10007
|
parentSessionId: input.parentSessionId,
|
|
9366
10008
|
title,
|
|
9367
10009
|
prompt,
|
|
@@ -9395,7 +10037,7 @@ var ResearchSessions = class {
|
|
|
9395
10037
|
let session;
|
|
9396
10038
|
try {
|
|
9397
10039
|
const configuration = input.configuration ?? await this.options.sourceConfiguration?.(input.parentSessionId);
|
|
9398
|
-
const reasoning =
|
|
10040
|
+
const reasoning = z11.enum(["low", "medium", "high", "xhigh", "max"]).safeParse(configuration?.reasoningEffort);
|
|
9399
10041
|
if (configuration !== void 0 && !reasoning.success && !["default", "unknown"].includes(configuration.reasoningEffort)) {
|
|
9400
10042
|
throw new ResearchError("RESEARCH_CONFIGURATION_UNSUPPORTED");
|
|
9401
10043
|
}
|
|
@@ -9519,7 +10161,7 @@ var ResearchSessions = class {
|
|
|
9519
10161
|
const assertAccess = this.#capture(record);
|
|
9520
10162
|
assertAccess();
|
|
9521
10163
|
if (options.metadataOnly === true) throw new ResearchError("RESEARCH_SESSION_ISOLATED");
|
|
9522
|
-
const pageSize =
|
|
10164
|
+
const pageSize = z11.number().int().min(1).max(SESSION_TIMELINE_PAGE_SIZE).parse(options.pageSize ?? SESSION_TIMELINE_PAGE_SIZE);
|
|
9523
10165
|
const result = await this.#state(sessionId).timelineReads.read({
|
|
9524
10166
|
...options.cursor === void 0 ? {} : { cursor: options.cursor },
|
|
9525
10167
|
pageSize,
|
|
@@ -9567,8 +10209,8 @@ var ResearchSessions = class {
|
|
|
9567
10209
|
return (await this.sendPrompt(sessionId, text, "immediate", operationId)).messageId;
|
|
9568
10210
|
}
|
|
9569
10211
|
async sendPrompt(sessionId, text, mode, operationId) {
|
|
9570
|
-
const prompt =
|
|
9571
|
-
|
|
10212
|
+
const prompt = z11.string().trim().min(1).max(16e3).parse(text);
|
|
10213
|
+
z11.string().min(1).max(256).parse(operationId);
|
|
9572
10214
|
const fingerprint = hash(JSON.stringify({ prompt, mode }));
|
|
9573
10215
|
const running = this.#sending.get(sessionId);
|
|
9574
10216
|
if (running !== void 0 && running.operationId === operationId) {
|
|
@@ -9857,21 +10499,30 @@ function createResearchWorkbench(options) {
|
|
|
9857
10499
|
}
|
|
9858
10500
|
|
|
9859
10501
|
// 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:
|
|
10502
|
+
var ConfigSchema = z12.object({
|
|
10503
|
+
dataDirectory: z12.string().min(1),
|
|
10504
|
+
relayUrl: z12.url().refine((value) => ["https:", "http:"].includes(new URL(value).protocol)),
|
|
10505
|
+
relayToken: z12.string().min(24),
|
|
10506
|
+
userId: z12.string().trim().min(1).max(256),
|
|
10507
|
+
workspaceRoot: z12.string().optional(),
|
|
10508
|
+
refreshIntervalMs: z12.number().int().min(5e3).max(36e5).default(3e4),
|
|
10509
|
+
heartbeatIntervalMs: z12.number().int().min(5e3).max(12e4).default(15e3),
|
|
10510
|
+
exitAfterSnapshot: z12.boolean().default(false),
|
|
10511
|
+
groupChatEnabled: z12.boolean().optional(),
|
|
10512
|
+
peerChatEnabled: z12.boolean().optional(),
|
|
10513
|
+
snapshotSuppressionEnabled: z12.boolean().optional(),
|
|
10514
|
+
timelineCompressionEnabled: z12.boolean().optional(),
|
|
10515
|
+
timelineIncrementalEnabled: z12.boolean().optional(),
|
|
10516
|
+
secureMessagePacketBudget: z12.number().int().min(2048).max(SECURE_MESSAGE_MAX_CIPHERTEXT_LENGTH).optional(),
|
|
10517
|
+
sessionImages: SessionImagesConfigSchema.optional()
|
|
10518
|
+
}).superRefine((config, context) => {
|
|
10519
|
+
if (config.sessionImages !== void 0 && !SessionImageRelayUrlSchema.safeParse(config.relayUrl).success) {
|
|
10520
|
+
context.addIssue({
|
|
10521
|
+
code: "custom",
|
|
10522
|
+
path: ["relayUrl"],
|
|
10523
|
+
message: "Session images require an HTTPS Relay origin without a path, credentials, query or fragment"
|
|
10524
|
+
});
|
|
10525
|
+
}
|
|
9875
10526
|
});
|
|
9876
10527
|
async function initialize(options) {
|
|
9877
10528
|
const dataDirectory = validateDataDirectory(options.dataDirectory);
|
|
@@ -9891,10 +10542,11 @@ async function initialize(options) {
|
|
|
9891
10542
|
...parsed.timelineCompressionEnabled === void 0 ? {} : { timelineCompressionEnabled: parsed.timelineCompressionEnabled },
|
|
9892
10543
|
...parsed.timelineIncrementalEnabled === void 0 ? {} : { timelineIncrementalEnabled: parsed.timelineIncrementalEnabled },
|
|
9893
10544
|
...parsed.secureMessagePacketBudget === void 0 ? {} : { secureMessagePacketBudget: parsed.secureMessagePacketBudget },
|
|
10545
|
+
...parsed.sessionImages === void 0 ? {} : { sessionImages: parsed.sessionImages },
|
|
9894
10546
|
...options.codingAgent === void 0 ? {} : { codingAgent: options.codingAgent }
|
|
9895
10547
|
};
|
|
9896
10548
|
const identity2 = DeviceIdentitySchema.parse(options.identity);
|
|
9897
|
-
const appVersion = options.appVersion ??
|
|
10549
|
+
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
10550
|
const lease = options.directoryLease ?? await DataDirectoryLease.acquire(dataDirectory);
|
|
9899
10551
|
let detach;
|
|
9900
10552
|
let adapter;
|
|
@@ -9950,7 +10602,7 @@ async function initialize(options) {
|
|
|
9950
10602
|
}, "research.operation.failed")
|
|
9951
10603
|
});
|
|
9952
10604
|
class InitializedAgent extends PcAgent {
|
|
9953
|
-
identityHash =
|
|
10605
|
+
identityHash = createHash16("sha256").update(`${config.userId}:${identity2.machineId}:${contentKey.keyId}`).digest("hex");
|
|
9954
10606
|
#started;
|
|
9955
10607
|
#starting;
|
|
9956
10608
|
#closed;
|
|
@@ -10024,6 +10676,10 @@ export {
|
|
|
10024
10676
|
PEER_CHAT_TIMEOUT_MS,
|
|
10025
10677
|
PeerChatRequestSchema,
|
|
10026
10678
|
PeerChatResultSchema,
|
|
10679
|
+
SessionImageRelayUrlSchema,
|
|
10680
|
+
SessionImageUploadError,
|
|
10681
|
+
SessionImageUploader,
|
|
10682
|
+
SessionImagesConfigSchema,
|
|
10027
10683
|
assertPiRuntimeSupported,
|
|
10028
10684
|
buildPairingUri,
|
|
10029
10685
|
codingAgentPresentation,
|