@gatherthread/dsh-host 0.1.0-alpha.8 → 0.1.0-beta.2
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/README.md +35 -21
- package/bundle/manifest.json +5 -5
- package/client/client.js +160 -45
- package/dist/bundle/native-plugin.js +2493 -312
- package/dist/bundle/plugin.js +1541 -109
- package/package.json +2 -2
|
@@ -729,9 +729,9 @@ function floatSafeRemainder(val, step) {
|
|
|
729
729
|
return ratio - roundedRatio;
|
|
730
730
|
}
|
|
731
731
|
var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
|
|
732
|
-
function defineLazy(
|
|
732
|
+
function defineLazy(object3, key, getter) {
|
|
733
733
|
let value = void 0;
|
|
734
|
-
Object.defineProperty(
|
|
734
|
+
Object.defineProperty(object3, key, {
|
|
735
735
|
get() {
|
|
736
736
|
if (value === EVALUATING) {
|
|
737
737
|
return void 0;
|
|
@@ -743,7 +743,7 @@ function defineLazy(object2, key, getter) {
|
|
|
743
743
|
return value;
|
|
744
744
|
},
|
|
745
745
|
set(v) {
|
|
746
|
-
Object.defineProperty(
|
|
746
|
+
Object.defineProperty(object3, key, {
|
|
747
747
|
value: v
|
|
748
748
|
// configurable: true,
|
|
749
749
|
});
|
|
@@ -823,10 +823,10 @@ function mergeDefs(...defs) {
|
|
|
823
823
|
function cloneDef(schema) {
|
|
824
824
|
return mergeDefs(schema._zod.def);
|
|
825
825
|
}
|
|
826
|
-
function getElementAtPath(obj,
|
|
827
|
-
if (!
|
|
826
|
+
function getElementAtPath(obj, path11) {
|
|
827
|
+
if (!path11)
|
|
828
828
|
return obj;
|
|
829
|
-
return
|
|
829
|
+
return path11.reduce((acc, key) => acc?.[key], obj);
|
|
830
830
|
}
|
|
831
831
|
function promiseAllObject(promisesObj) {
|
|
832
832
|
const keys = Object.keys(promisesObj);
|
|
@@ -1166,11 +1166,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
1166
1166
|
}
|
|
1167
1167
|
return false;
|
|
1168
1168
|
}
|
|
1169
|
-
function prefixIssues(
|
|
1169
|
+
function prefixIssues(path11, issues) {
|
|
1170
1170
|
return issues.map((iss) => {
|
|
1171
1171
|
var _a3;
|
|
1172
1172
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
1173
|
-
iss.path.unshift(
|
|
1173
|
+
iss.path.unshift(path11);
|
|
1174
1174
|
return iss;
|
|
1175
1175
|
});
|
|
1176
1176
|
}
|
|
@@ -1277,16 +1277,16 @@ function cleanEnum(obj) {
|
|
|
1277
1277
|
}
|
|
1278
1278
|
function base64ToUint8Array(base643) {
|
|
1279
1279
|
const binaryString = atob(base643);
|
|
1280
|
-
const
|
|
1280
|
+
const bytes2 = new Uint8Array(binaryString.length);
|
|
1281
1281
|
for (let i = 0; i < binaryString.length; i++) {
|
|
1282
|
-
|
|
1282
|
+
bytes2[i] = binaryString.charCodeAt(i);
|
|
1283
1283
|
}
|
|
1284
|
-
return
|
|
1284
|
+
return bytes2;
|
|
1285
1285
|
}
|
|
1286
|
-
function uint8ArrayToBase64(
|
|
1286
|
+
function uint8ArrayToBase64(bytes2) {
|
|
1287
1287
|
let binaryString = "";
|
|
1288
|
-
for (let i = 0; i <
|
|
1289
|
-
binaryString += String.fromCharCode(
|
|
1288
|
+
for (let i = 0; i < bytes2.length; i++) {
|
|
1289
|
+
binaryString += String.fromCharCode(bytes2[i]);
|
|
1290
1290
|
}
|
|
1291
1291
|
return btoa(binaryString);
|
|
1292
1292
|
}
|
|
@@ -1295,22 +1295,22 @@ function base64urlToUint8Array(base64url3) {
|
|
|
1295
1295
|
const padding = "=".repeat((4 - base643.length % 4) % 4);
|
|
1296
1296
|
return base64ToUint8Array(base643 + padding);
|
|
1297
1297
|
}
|
|
1298
|
-
function uint8ArrayToBase64url(
|
|
1299
|
-
return uint8ArrayToBase64(
|
|
1298
|
+
function uint8ArrayToBase64url(bytes2) {
|
|
1299
|
+
return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1300
1300
|
}
|
|
1301
1301
|
function hexToUint8Array(hex3) {
|
|
1302
1302
|
const cleanHex = hex3.replace(/^0x/, "");
|
|
1303
1303
|
if (cleanHex.length % 2 !== 0) {
|
|
1304
1304
|
throw new Error("Invalid hex string length");
|
|
1305
1305
|
}
|
|
1306
|
-
const
|
|
1306
|
+
const bytes2 = new Uint8Array(cleanHex.length / 2);
|
|
1307
1307
|
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1308
|
-
|
|
1308
|
+
bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1309
1309
|
}
|
|
1310
|
-
return
|
|
1310
|
+
return bytes2;
|
|
1311
1311
|
}
|
|
1312
|
-
function uint8ArrayToHex(
|
|
1313
|
-
return Array.from(
|
|
1312
|
+
function uint8ArrayToHex(bytes2) {
|
|
1313
|
+
return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1314
1314
|
}
|
|
1315
1315
|
var Class = class {
|
|
1316
1316
|
constructor(..._args) {
|
|
@@ -1620,16 +1620,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1620
1620
|
}
|
|
1621
1621
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
1622
1622
|
const fieldErrors = { _errors: [] };
|
|
1623
|
-
const processError = (error63,
|
|
1623
|
+
const processError = (error63, path11 = []) => {
|
|
1624
1624
|
for (const issue2 of error63.issues) {
|
|
1625
1625
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1626
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1626
|
+
issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
|
|
1627
1627
|
} else if (issue2.code === "invalid_key") {
|
|
1628
|
-
processError({ issues: issue2.issues }, [...
|
|
1628
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
1629
1629
|
} else if (issue2.code === "invalid_element") {
|
|
1630
|
-
processError({ issues: issue2.issues }, [...
|
|
1630
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
1631
1631
|
} else {
|
|
1632
|
-
const fullpath = [...
|
|
1632
|
+
const fullpath = [...path11, ...issue2.path];
|
|
1633
1633
|
if (fullpath.length === 0) {
|
|
1634
1634
|
fieldErrors._errors.push(mapper(issue2));
|
|
1635
1635
|
} else {
|
|
@@ -1668,17 +1668,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1668
1668
|
}
|
|
1669
1669
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
1670
1670
|
const result = { errors: [] };
|
|
1671
|
-
const processError = (error63,
|
|
1671
|
+
const processError = (error63, path11 = []) => {
|
|
1672
1672
|
var _a3;
|
|
1673
1673
|
for (const issue2 of error63.issues) {
|
|
1674
1674
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
1675
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
1675
|
+
issue2.errors.map((issues) => processError({ issues }, [...path11, ...issue2.path]));
|
|
1676
1676
|
} else if (issue2.code === "invalid_key") {
|
|
1677
|
-
processError({ issues: issue2.issues }, [...
|
|
1677
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
1678
1678
|
} else if (issue2.code === "invalid_element") {
|
|
1679
|
-
processError({ issues: issue2.issues }, [...
|
|
1679
|
+
processError({ issues: issue2.issues }, [...path11, ...issue2.path]);
|
|
1680
1680
|
} else {
|
|
1681
|
-
const fullpath = [...
|
|
1681
|
+
const fullpath = [...path11, ...issue2.path];
|
|
1682
1682
|
if (fullpath.length === 0) {
|
|
1683
1683
|
result.errors.push(mapper(issue2));
|
|
1684
1684
|
continue;
|
|
@@ -1717,8 +1717,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
1717
1717
|
}
|
|
1718
1718
|
function toDotPath(_path) {
|
|
1719
1719
|
const segs = [];
|
|
1720
|
-
const
|
|
1721
|
-
for (const seg of
|
|
1720
|
+
const path11 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
1721
|
+
for (const seg of path11) {
|
|
1722
1722
|
if (typeof seg === "number")
|
|
1723
1723
|
segs.push(`[${seg}]`);
|
|
1724
1724
|
else if (typeof seg === "symbol")
|
|
@@ -15810,8 +15810,8 @@ function foldObjects(members2) {
|
|
|
15810
15810
|
}
|
|
15811
15811
|
const properties = {};
|
|
15812
15812
|
const required2 = /* @__PURE__ */ new Set();
|
|
15813
|
-
for (const
|
|
15814
|
-
for (const key in
|
|
15813
|
+
for (const object3 of objects) {
|
|
15814
|
+
for (const key in object3.properties) {
|
|
15815
15815
|
if (Object.prototype.hasOwnProperty.call(properties, key))
|
|
15816
15816
|
continue;
|
|
15817
15817
|
const parts = [];
|
|
@@ -15825,18 +15825,18 @@ function foldObjects(members2) {
|
|
|
15825
15825
|
const merged = parts.length === 1 ? parts[0] : foldObjects(parts) ?? { allOf: parts };
|
|
15826
15826
|
assignProp(properties, key, merged);
|
|
15827
15827
|
}
|
|
15828
|
-
for (const key of
|
|
15828
|
+
for (const key of object3.required ?? [])
|
|
15829
15829
|
required2.add(key);
|
|
15830
15830
|
}
|
|
15831
15831
|
const folded = { type: "object", properties };
|
|
15832
15832
|
if (required2.size)
|
|
15833
15833
|
folded.required = [...required2];
|
|
15834
|
-
if (objects.every((
|
|
15834
|
+
if (objects.every((object3) => object3.additionalProperties === false)) {
|
|
15835
15835
|
folded.additionalProperties = false;
|
|
15836
15836
|
} else {
|
|
15837
15837
|
const constraints = [];
|
|
15838
|
-
for (const
|
|
15839
|
-
const constraint = undeclaredConstraint(
|
|
15838
|
+
for (const object3 of objects) {
|
|
15839
|
+
const constraint = undeclaredConstraint(object3);
|
|
15840
15840
|
if (constraint && !constraints.some((seen) => JSON.stringify(seen) === JSON.stringify(constraint)))
|
|
15841
15841
|
constraints.push(constraint);
|
|
15842
15842
|
}
|
|
@@ -18820,13 +18820,13 @@ function resolveRef(ref, ctx) {
|
|
|
18820
18820
|
if (!ref.startsWith("#")) {
|
|
18821
18821
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
18822
18822
|
}
|
|
18823
|
-
const
|
|
18824
|
-
if (
|
|
18823
|
+
const path11 = ref.slice(1).split("/").filter(Boolean);
|
|
18824
|
+
if (path11.length === 0) {
|
|
18825
18825
|
return ctx.rootSchema;
|
|
18826
18826
|
}
|
|
18827
18827
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
18828
|
-
if (
|
|
18829
|
-
const key =
|
|
18828
|
+
if (path11[0] === defsKey) {
|
|
18829
|
+
const key = path11[1] === void 0 ? void 0 : decodeJSONPointerSegment(path11[1]);
|
|
18830
18830
|
if (!key || !ctx.defs[key]) {
|
|
18831
18831
|
throw new Error(`Reference not found: ${ref}`);
|
|
18832
18832
|
}
|
|
@@ -19675,6 +19675,28 @@ function date4(params) {
|
|
|
19675
19675
|
return _coercedDate(ZodDate, params);
|
|
19676
19676
|
}
|
|
19677
19677
|
|
|
19678
|
+
// ../protocol/dist/src/live-output.js
|
|
19679
|
+
var utf8 = (maximum) => external_exports.string().refine((value) => new TextEncoder().encode(value).length <= maximum, "Live output is too large");
|
|
19680
|
+
var LiveOutputPayloadSchema = external_exports.object({
|
|
19681
|
+
content: utf8(24 * 1024),
|
|
19682
|
+
phase: external_exports.literal("stream"),
|
|
19683
|
+
stream_id: external_exports.string().uuid(),
|
|
19684
|
+
stream_mode: external_exports.literal("snapshot"),
|
|
19685
|
+
stream_kind: external_exports.enum(["answer", "reasoning", "tool", "plan"]),
|
|
19686
|
+
stream_revision: external_exports.number().int().min(1).max(1200),
|
|
19687
|
+
stream_state: external_exports.enum(["running", "completed", "failed"]),
|
|
19688
|
+
title: utf8(256).optional(),
|
|
19689
|
+
plan: external_exports.array(external_exports.object({ step: utf8(512), status: external_exports.enum(["pending", "in_progress", "completed"]) }).strict()).max(64).optional(),
|
|
19690
|
+
status: external_exports.enum(["thinking", "running"]).optional(),
|
|
19691
|
+
occurred_at: external_exports.string().datetime().optional(),
|
|
19692
|
+
capture_fidelity: external_exports.literal("harness_transcript").optional(),
|
|
19693
|
+
source_harness: external_exports.string().min(1).max(80).optional()
|
|
19694
|
+
}).strict().superRefine((value, context) => {
|
|
19695
|
+
if (value.stream_kind === "plan" !== (value.plan !== void 0)) {
|
|
19696
|
+
context.addIssue({ code: "custom", message: "Only plan snapshots carry plan steps" });
|
|
19697
|
+
}
|
|
19698
|
+
});
|
|
19699
|
+
|
|
19678
19700
|
// ../protocol/dist/src/code-sync.js
|
|
19679
19701
|
var CODE_SYNC_MAX_FILES = 1e3;
|
|
19680
19702
|
var CODE_SYNC_MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
@@ -19685,16 +19707,16 @@ var Commit = external_exports.string().regex(/^[a-f0-9]{40}$/);
|
|
|
19685
19707
|
var BranchId = external_exports.string().regex(/^branch-[a-f0-9]{24}$/);
|
|
19686
19708
|
var pathEncoder = new TextEncoder();
|
|
19687
19709
|
var hfsIgnoredCharacters = /[\u200c-\u200f\u202a-\u202e\u206a-\u206f\ufeff]/gu;
|
|
19688
|
-
function portablePathKey(
|
|
19689
|
-
return
|
|
19710
|
+
function portablePathKey(path11) {
|
|
19711
|
+
return path11.normalize("NFC").replace(hfsIgnoredCharacters, "").toLowerCase();
|
|
19690
19712
|
}
|
|
19691
19713
|
function containsCodeSyncSecret(text) {
|
|
19692
|
-
return /-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----|\b(?:gh[pousr]|github_pat|glpat|sk|xox[baprs])[-_][A-Za-z0-9_-]{16,}|\b(?:gt[abidp]|acp(?:i|d)?)_[A-Za-z0-9_-]{20,}|\bAKIA[A-Z0-9]{16}\b/u.test(text);
|
|
19714
|
+
return /-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----|\b(?:gh[pousr]|github_pat|glpat|sk|xox[baprs])[-_][A-Za-z0-9_-]{16,}|\b(?:(?:gt[abidp]|gteg?)|acp(?:i|d)?)_[A-Za-z0-9_-]{20,}|\bAKIA[A-Z0-9]{16}\b/u.test(text);
|
|
19693
19715
|
}
|
|
19694
|
-
function isCodeSyncPathAllowed(
|
|
19695
|
-
if (!
|
|
19716
|
+
function isCodeSyncPathAllowed(path11) {
|
|
19717
|
+
if (!path11 || path11.length > 500 || path11.startsWith("/") || /[\\\u0000-\u001f\u007f\ud800-\udfff:<>"|?*]/u.test(path11))
|
|
19696
19718
|
return false;
|
|
19697
|
-
const components =
|
|
19719
|
+
const components = path11.split("/");
|
|
19698
19720
|
return components.every((part) => {
|
|
19699
19721
|
const lower = portablePathKey(part);
|
|
19700
19722
|
if (!part || pathEncoder.encode(part).byteLength > 255 || part === "." || part === ".." || part.endsWith(".") || part.endsWith(" "))
|
|
@@ -19714,34 +19736,42 @@ function isCodeSyncPathAllowed(path10) {
|
|
|
19714
19736
|
return !/\.(?:pem|p12|pfx|key)$/iu.test(lower) && !/^(?:id_rsa|id_ed25519)(?:\.|$)/iu.test(lower);
|
|
19715
19737
|
});
|
|
19716
19738
|
}
|
|
19717
|
-
var
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19724
|
-
|
|
19725
|
-
|
|
19726
|
-
|
|
19727
|
-
|
|
19728
|
-
|
|
19729
|
-
|
|
19730
|
-
|
|
19731
|
-
|
|
19732
|
-
|
|
19733
|
-
|
|
19734
|
-
|
|
19735
|
-
|
|
19736
|
-
|
|
19737
|
-
|
|
19738
|
-
|
|
19739
|
-
|
|
19740
|
-
|
|
19739
|
+
var CLOUD_CODE_SYNC_LIMITS = { maxFiles: CODE_SYNC_MAX_FILES, maxFileBytes: CODE_SYNC_MAX_FILE_BYTES, maxSnapshotBytes: CODE_SYNC_MAX_SNAPSHOT_BYTES };
|
|
19740
|
+
var GITHUB_CODE_SYNC_LIMITS = { maxFiles: 1e4, maxFileBytes: 20 * 1024 * 1024, maxSnapshotBytes: 128 * 1024 * 1024 };
|
|
19741
|
+
function createCodeFileSchema(limits) {
|
|
19742
|
+
return external_exports.object({
|
|
19743
|
+
path: external_exports.string().refine(isCodeSyncPathAllowed, "Unsafe or private code path"),
|
|
19744
|
+
content_base64: external_exports.string().max(4 * Math.ceil(limits.maxFileBytes / 3)).regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/),
|
|
19745
|
+
executable: external_exports.boolean()
|
|
19746
|
+
}).strict();
|
|
19747
|
+
}
|
|
19748
|
+
var CodeFileSchema = createCodeFileSchema(CLOUD_CODE_SYNC_LIMITS);
|
|
19749
|
+
function createCodeFilesSchema(limits) {
|
|
19750
|
+
return external_exports.array(createCodeFileSchema(limits)).max(limits.maxFiles).superRefine((files, context) => {
|
|
19751
|
+
let size = 0;
|
|
19752
|
+
const names = /* @__PURE__ */ new Set();
|
|
19753
|
+
for (const [index, file2] of files.entries()) {
|
|
19754
|
+
const bytes2 = file2.content_base64.length * 3 / 4 - (file2.content_base64.endsWith("==") ? 2 : file2.content_base64.endsWith("=") ? 1 : 0);
|
|
19755
|
+
size += bytes2;
|
|
19756
|
+
const normalized = portablePathKey(file2.path);
|
|
19757
|
+
if (bytes2 > limits.maxFileBytes || names.has(normalized))
|
|
19758
|
+
context.addIssue({ code: "custom", path: [index], message: "File limit or duplicate portable path" });
|
|
19759
|
+
names.add(normalized);
|
|
19760
|
+
}
|
|
19761
|
+
if (size > limits.maxSnapshotBytes)
|
|
19762
|
+
context.addIssue({ code: "custom", message: "Code snapshot exceeds provider limit" });
|
|
19763
|
+
for (const name2 of names) {
|
|
19764
|
+
const parts = name2.split("/");
|
|
19741
19765
|
parts.pop();
|
|
19766
|
+
while (parts.length) {
|
|
19767
|
+
if (names.has(parts.join("/")))
|
|
19768
|
+
context.addIssue({ code: "custom", message: "File and directory paths overlap" });
|
|
19769
|
+
parts.pop();
|
|
19770
|
+
}
|
|
19742
19771
|
}
|
|
19743
|
-
}
|
|
19744
|
-
}
|
|
19772
|
+
});
|
|
19773
|
+
}
|
|
19774
|
+
var CodeFilesSchema = createCodeFilesSchema(CLOUD_CODE_SYNC_LIMITS);
|
|
19745
19775
|
var CodeEnableInputSchema = external_exports.object({ idempotency_key: Key }).strict();
|
|
19746
19776
|
var CodeDisableInputSchema = external_exports.object({ idempotency_key: Key }).strict();
|
|
19747
19777
|
var CodeCheckpointInputSchema = external_exports.object({
|
|
@@ -19803,6 +19833,159 @@ var CodeSnapshotResultSchema = external_exports.object({ snapshot: external_expo
|
|
|
19803
19833
|
}).strict() }).strict();
|
|
19804
19834
|
var codeSyncRequestKinds = ["code_sync_status", "code_upload", "code_download", "code_recover", "code_auto_upload_enable", "code_auto_upload_disable"];
|
|
19805
19835
|
|
|
19836
|
+
// ../protocol/dist/src/github-sync.js
|
|
19837
|
+
var GitHubRepositorySchema = external_exports.string().max(140).regex(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?\/[A-Za-z0-9_.-]{1,100}$/).refine((value) => {
|
|
19838
|
+
const repository = value.split("/")[1];
|
|
19839
|
+
return repository !== "." && repository !== ".." && !repository.toLowerCase().endsWith(".git");
|
|
19840
|
+
}, "Use owner/repository without a .git suffix");
|
|
19841
|
+
function isGitHubBranchAllowed(value) {
|
|
19842
|
+
return value.length > 0 && value.length <= 200 && /^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(value) && !value.includes("..") && value.split("/").every((part) => !!part && !part.startsWith(".") && !part.endsWith(".") && !part.toLowerCase().endsWith(".lock")) && value !== "HEAD";
|
|
19843
|
+
}
|
|
19844
|
+
var GitHubBranchSchema = external_exports.string().refine(isGitHubBranchAllowed, "Unsafe GitHub branch name");
|
|
19845
|
+
var GitHubBaseBranchSchema = GitHubBranchSchema.refine((value) => !value.startsWith("gatherthread/"), "The shared base cannot use the GatherThread personal branch namespace");
|
|
19846
|
+
var GitHubPersonalBranchSchema = external_exports.string().regex(/^gatherthread\/[a-f0-9]{24}\/[a-f0-9]{24}$/);
|
|
19847
|
+
var GitHubConnectionSchema = external_exports.object({
|
|
19848
|
+
repository: GitHubRepositorySchema,
|
|
19849
|
+
base_branch: GitHubBaseBranchSchema,
|
|
19850
|
+
enabled: external_exports.boolean(),
|
|
19851
|
+
revision: external_exports.string().uuid()
|
|
19852
|
+
}).strict();
|
|
19853
|
+
var GitHubProjectStatusSchema = external_exports.object({
|
|
19854
|
+
connection: GitHubConnectionSchema.nullable(),
|
|
19855
|
+
branch: GitHubPersonalBranchSchema,
|
|
19856
|
+
can_configure: external_exports.boolean(),
|
|
19857
|
+
// GatherThread membership only. GitHub permissions must be checked locally.
|
|
19858
|
+
can_write: external_exports.boolean()
|
|
19859
|
+
}).strict();
|
|
19860
|
+
var GitHubConnectionInputSchema = external_exports.object({
|
|
19861
|
+
repository: GitHubRepositorySchema,
|
|
19862
|
+
base_branch: GitHubBaseBranchSchema,
|
|
19863
|
+
enabled: external_exports.boolean(),
|
|
19864
|
+
expected_revision: external_exports.string().uuid().nullable()
|
|
19865
|
+
}).strict();
|
|
19866
|
+
var githubCodeSyncRequestKinds = [
|
|
19867
|
+
"github_auth_connect",
|
|
19868
|
+
"github_code_sync_status",
|
|
19869
|
+
"github_code_upload",
|
|
19870
|
+
"github_code_download",
|
|
19871
|
+
"github_code_recover",
|
|
19872
|
+
"github_code_auto_upload_enable",
|
|
19873
|
+
"github_code_auto_upload_disable",
|
|
19874
|
+
"github_code_update"
|
|
19875
|
+
];
|
|
19876
|
+
|
|
19877
|
+
// ../protocol/dist/src/registration.js
|
|
19878
|
+
var RegistrationEmailSchema = external_exports.string().max(254).trim().toLowerCase().regex(/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/).refine((value) => value.split("@")[0].length <= 64 && value.split("@")[1].split(".").every((part) => part.length <= 63));
|
|
19879
|
+
var Label = external_exports.string().regex(/^[^\u0000-\u001f\u007f-\u009f]*$/u).trim().min(1).max(120);
|
|
19880
|
+
var SendRegistrationInputSchema = external_exports.object({
|
|
19881
|
+
email: RegistrationEmailSchema,
|
|
19882
|
+
challenge_token: external_exports.string().min(1).max(2048),
|
|
19883
|
+
idempotency_key: external_exports.string().uuid(),
|
|
19884
|
+
locale: external_exports.enum(["en", "zh-CN"])
|
|
19885
|
+
}).strict();
|
|
19886
|
+
var AccountPasswordSchema = external_exports.string().min(8).max(128).refine((value) => new TextEncoder().encode(value).byteLength <= 512);
|
|
19887
|
+
var EmailLoginInputSchema = external_exports.object({
|
|
19888
|
+
email: RegistrationEmailSchema,
|
|
19889
|
+
password: AccountPasswordSchema,
|
|
19890
|
+
device_name: Label,
|
|
19891
|
+
remember_device: external_exports.boolean().default(false)
|
|
19892
|
+
}).strict();
|
|
19893
|
+
var LauncherProjectId = external_exports.string().min(1).max(128).refine((value) => /^[A-Za-z0-9]/u.test(value) && !/[^A-Za-z0-9._:-]/u.test(value));
|
|
19894
|
+
var CreateCodexLauncherAuthorizationInputSchema = external_exports.object({
|
|
19895
|
+
project_id: LauncherProjectId,
|
|
19896
|
+
email: RegistrationEmailSchema
|
|
19897
|
+
}).strict();
|
|
19898
|
+
var ClaimCodexLauncherAuthorizationInputSchema = external_exports.object({
|
|
19899
|
+
authorization_id: external_exports.string().uuid().toLowerCase(),
|
|
19900
|
+
project_id: LauncherProjectId,
|
|
19901
|
+
email: RegistrationEmailSchema,
|
|
19902
|
+
password: AccountPasswordSchema,
|
|
19903
|
+
device_name: Label
|
|
19904
|
+
}).strict();
|
|
19905
|
+
var VerifyRegistrationInputSchema = external_exports.object({
|
|
19906
|
+
registration_id: external_exports.string().uuid(),
|
|
19907
|
+
code: external_exports.string().regex(/^\d{8}$/),
|
|
19908
|
+
display_name: Label,
|
|
19909
|
+
device_name: Label,
|
|
19910
|
+
remember_device: external_exports.boolean().default(false),
|
|
19911
|
+
privacy_acknowledged: external_exports.literal(true),
|
|
19912
|
+
password: AccountPasswordSchema
|
|
19913
|
+
}).strict();
|
|
19914
|
+
var RegistrationStatusSchema = external_exports.object({
|
|
19915
|
+
enabled: external_exports.boolean(),
|
|
19916
|
+
site_key: external_exports.string().nullable(),
|
|
19917
|
+
challenge_binding: external_exports.string().nullable()
|
|
19918
|
+
}).strict();
|
|
19919
|
+
var RegistrationSentSchema = external_exports.object({
|
|
19920
|
+
registration_id: external_exports.string().uuid(),
|
|
19921
|
+
expires_in_seconds: external_exports.literal(600),
|
|
19922
|
+
resend_after_seconds: external_exports.literal(60)
|
|
19923
|
+
}).strict();
|
|
19924
|
+
var PasswordResetSentSchema = external_exports.object({
|
|
19925
|
+
reset_id: external_exports.string().uuid(),
|
|
19926
|
+
expires_in_seconds: external_exports.literal(600),
|
|
19927
|
+
resend_after_seconds: external_exports.literal(60)
|
|
19928
|
+
}).strict();
|
|
19929
|
+
var VerifyPasswordResetInputSchema = external_exports.object({
|
|
19930
|
+
reset_id: external_exports.string().uuid(),
|
|
19931
|
+
email: RegistrationEmailSchema,
|
|
19932
|
+
code: external_exports.string().regex(/^\d{8}$/),
|
|
19933
|
+
password: AccountPasswordSchema,
|
|
19934
|
+
password_confirmation: AccountPasswordSchema,
|
|
19935
|
+
locale: external_exports.enum(["en", "zh-CN"])
|
|
19936
|
+
}).strict().refine((value) => value.password === value.password_confirmation);
|
|
19937
|
+
var EmailAccountSessionSchema = external_exports.object({
|
|
19938
|
+
actor: external_exports.object({ user_id: external_exports.string(), display_name: external_exports.string(), device_id: external_exports.string(), can_create_projects: external_exports.literal(true) }).strict(),
|
|
19939
|
+
expires_at: external_exports.string().datetime()
|
|
19940
|
+
}).strict();
|
|
19941
|
+
|
|
19942
|
+
// ../protocol/dist/src/cloud-api.js
|
|
19943
|
+
var CloudApiProviderIdSchema = external_exports.enum(["openai", "anthropic", "deepseek", "siliconflow-cn", "siliconflow-global", "openrouter", "qwen", "glm", "doubao"]);
|
|
19944
|
+
var CloudApiModelSchema = external_exports.string().trim().min(1).max(160).regex(/^[^\u0000-\u0020\u007f-\u009f]+$/u);
|
|
19945
|
+
var Label2 = external_exports.string().trim().min(1).max(80).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u);
|
|
19946
|
+
var CloudApiPutInputSchema = external_exports.object({
|
|
19947
|
+
expected_revision: external_exports.string().uuid().nullable(),
|
|
19948
|
+
provider_id: CloudApiProviderIdSchema,
|
|
19949
|
+
label: Label2,
|
|
19950
|
+
model: CloudApiModelSchema,
|
|
19951
|
+
enabled: external_exports.boolean(),
|
|
19952
|
+
api_key: external_exports.string().min(8).max(4096).regex(/^[\x21-\x7e]+$/u).optional()
|
|
19953
|
+
}).strict();
|
|
19954
|
+
var CloudApiDeleteInputSchema = external_exports.object({ expected_revision: external_exports.string().uuid() }).strict();
|
|
19955
|
+
var CloudApiConfigurationSchema = external_exports.object({
|
|
19956
|
+
id: external_exports.string().uuid(),
|
|
19957
|
+
revision: external_exports.string().uuid(),
|
|
19958
|
+
provider_id: CloudApiProviderIdSchema,
|
|
19959
|
+
profile_id: external_exports.string().regex(/^api-[0-9a-f-]{36}$/u),
|
|
19960
|
+
label: Label2,
|
|
19961
|
+
model: CloudApiModelSchema,
|
|
19962
|
+
enabled: external_exports.boolean(),
|
|
19963
|
+
has_key: external_exports.literal(true),
|
|
19964
|
+
updated_at: external_exports.string().datetime()
|
|
19965
|
+
}).strict();
|
|
19966
|
+
var CloudApiProviderSchema = external_exports.object({
|
|
19967
|
+
id: CloudApiProviderIdSchema,
|
|
19968
|
+
label: external_exports.string().max(80),
|
|
19969
|
+
transport: external_exports.enum(["openai-chat", "anthropic-messages"]),
|
|
19970
|
+
base_url: external_exports.string().url().max(200),
|
|
19971
|
+
docs_url: external_exports.string().url().max(300),
|
|
19972
|
+
default_model: CloudApiModelSchema.optional()
|
|
19973
|
+
}).strict();
|
|
19974
|
+
var CloudApiStatusSchema = external_exports.object({
|
|
19975
|
+
enabled: external_exports.boolean(),
|
|
19976
|
+
providers: external_exports.array(CloudApiProviderSchema).max(16),
|
|
19977
|
+
configurations: external_exports.array(CloudApiConfigurationSchema).max(8)
|
|
19978
|
+
}).strict();
|
|
19979
|
+
var CloudApiSavedSchema = external_exports.object({ configuration: CloudApiConfigurationSchema }).strict();
|
|
19980
|
+
var PersonalCloudRevisionSchema = external_exports.string().uuid().optional();
|
|
19981
|
+
function hasValidPersonalCloudRevision(value) {
|
|
19982
|
+
return value.profile_id.startsWith("api-") ? /^api-[0-9a-f-]{36}$/u.test(value.profile_id) && value.api_config_revision !== void 0 : value.api_config_revision === void 0;
|
|
19983
|
+
}
|
|
19984
|
+
var HostedAgentSelectionSchema = external_exports.object({
|
|
19985
|
+
profile_id: external_exports.string().min(1).max(64),
|
|
19986
|
+
api_config_revision: PersonalCloudRevisionSchema
|
|
19987
|
+
}).strict().refine(hasValidPersonalCloudRevision, "Personal Cloud Agent selection requires its exact revision");
|
|
19988
|
+
|
|
19806
19989
|
// ../protocol/dist/src/history-summary.js
|
|
19807
19990
|
var HISTORY_SUMMARY_MAX_SOURCE_BYTES = 20 * 1024;
|
|
19808
19991
|
var HISTORY_SUMMARY_MAX_PROMPT_BYTES = 30 * 1024;
|
|
@@ -19818,7 +20001,7 @@ function record2(value) {
|
|
|
19818
20001
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
19819
20002
|
}
|
|
19820
20003
|
function historySummaryMarker(event) {
|
|
19821
|
-
if (event.type !== "agent_request")
|
|
20004
|
+
if (event.deleted || event.type !== "agent_request")
|
|
19822
20005
|
return void 0;
|
|
19823
20006
|
const marker = record2(record2(event.payload)?.history_summary);
|
|
19824
20007
|
const ids = marker?.source_event_ids;
|
|
@@ -19827,6 +20010,105 @@ function historySummaryMarker(event) {
|
|
|
19827
20010
|
return { version: 1, source_event_ids: ids, source_digest: marker.source_digest };
|
|
19828
20011
|
}
|
|
19829
20012
|
|
|
20013
|
+
// ../protocol/dist/src/test-gate.js
|
|
20014
|
+
var TestGateExchangeInputSchema = external_exports.object({
|
|
20015
|
+
admission_code: external_exports.string().regex(/^gte_[A-Za-z0-9_-]{43}$/)
|
|
20016
|
+
}).strict();
|
|
20017
|
+
var TestGateStatusSchema = external_exports.object({
|
|
20018
|
+
enabled: external_exports.boolean(),
|
|
20019
|
+
admitted: external_exports.boolean(),
|
|
20020
|
+
environment: external_exports.enum(["production", "test"])
|
|
20021
|
+
}).strict();
|
|
20022
|
+
|
|
20023
|
+
// ../protocol/dist/src/hosted-github.js
|
|
20024
|
+
var Key2 = external_exports.string().min(8).max(200);
|
|
20025
|
+
var HostedGithubRepositoryInputSchema = external_exports.object({
|
|
20026
|
+
repository: external_exports.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u).max(140),
|
|
20027
|
+
base_branch: external_exports.string().min(1).max(200).refine((v) => !/[\s~^:?*\[\\]/u.test(v) && !v.includes("..") && !v.startsWith("-") && !v.endsWith("/") && !v.endsWith(".lock"))
|
|
20028
|
+
}).strict();
|
|
20029
|
+
var HostedGithubTaskInputSchema = external_exports.object({
|
|
20030
|
+
content: external_exports.string().trim().min(1).max(6e3),
|
|
20031
|
+
profile_id: external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/u),
|
|
20032
|
+
idempotency_key: Key2,
|
|
20033
|
+
continue_task_id: external_exports.string().regex(/^gh-task-[a-f0-9]{32}$/u).optional(),
|
|
20034
|
+
reply_to_event_id: external_exports.string().min(1).max(128).nullable().optional(),
|
|
20035
|
+
api_config_revision: PersonalCloudRevisionSchema,
|
|
20036
|
+
content_revision: external_exports.number().int().nonnegative().optional()
|
|
20037
|
+
}).strict().refine(hasValidPersonalCloudRevision, { message: "Personal cloud profiles require their exact configuration revision" });
|
|
20038
|
+
var HostedGithubCompleteInputSchema = external_exports.object({
|
|
20039
|
+
state: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/u),
|
|
20040
|
+
code: external_exports.string().min(1).max(500)
|
|
20041
|
+
}).strict();
|
|
20042
|
+
var HostedGithubPrInputSchema = external_exports.object({
|
|
20043
|
+
title: external_exports.string().trim().min(1).max(200),
|
|
20044
|
+
body: external_exports.string().max(6e3),
|
|
20045
|
+
expected_revision: external_exports.string().regex(/^[a-f0-9]{64}$/u)
|
|
20046
|
+
}).strict();
|
|
20047
|
+
var Login = external_exports.string().regex(/^[A-Za-z0-9-]{1,39}$/u);
|
|
20048
|
+
var TaskId = external_exports.string().regex(/^gh-task-[a-f0-9]{32}$/u);
|
|
20049
|
+
var Sha = external_exports.string().regex(/^[a-f0-9]{40}$/u).nullable();
|
|
20050
|
+
var Revision = external_exports.string().regex(/^[a-f0-9]{64}$/u).nullable();
|
|
20051
|
+
var PullUrl = external_exports.string().max(2048).regex(/^https:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/pull\/[1-9][0-9]*$/u).nullable();
|
|
20052
|
+
var HostedGithubBindingSchema = HostedGithubRepositoryInputSchema;
|
|
20053
|
+
var HostedGithubStatusSchema = external_exports.union([
|
|
20054
|
+
external_exports.object({ enabled: external_exports.literal(false) }).strict(),
|
|
20055
|
+
external_exports.object({
|
|
20056
|
+
enabled: external_exports.literal(true),
|
|
20057
|
+
connected: external_exports.boolean(),
|
|
20058
|
+
login: Login.nullable(),
|
|
20059
|
+
binding: HostedGithubBindingSchema.nullable(),
|
|
20060
|
+
installation_url: external_exports.string().max(2048).regex(/^https:\/\/github\.com\/apps\/[A-Za-z0-9_.-]+\/installations\/new$/u)
|
|
20061
|
+
}).strict().refine((v) => v.connected ? v.login !== null : v.login === null && v.binding === null)
|
|
20062
|
+
]);
|
|
20063
|
+
var HostedGithubAuthorizationSchema = external_exports.object({ authorization_url: external_exports.string().max(4096).refine((value) => {
|
|
20064
|
+
try {
|
|
20065
|
+
const url2 = new URL(value);
|
|
20066
|
+
return url2.origin === "https://github.com" && url2.pathname === "/login/oauth/authorize" && !url2.username && !url2.password && !url2.hash && /^[A-Za-z0-9_-]{43}$/u.test(url2.searchParams.get("state") ?? "") && [...url2.searchParams.keys()].every((key) => ["client_id", "redirect_uri", "state", "code_challenge", "code_challenge_method", "prompt"].includes(key));
|
|
20067
|
+
} catch {
|
|
20068
|
+
return false;
|
|
20069
|
+
}
|
|
20070
|
+
}) }).strict();
|
|
20071
|
+
var HostedGithubConnectionSchema = external_exports.object({ connected: external_exports.literal(true), login: Login }).strict();
|
|
20072
|
+
var HostedGithubDisconnectionSchema = external_exports.object({ connected: external_exports.literal(false) }).strict();
|
|
20073
|
+
var HostedGithubTaskSummarySchema = external_exports.object({
|
|
20074
|
+
id: TaskId,
|
|
20075
|
+
session_id: external_exports.string().min(1).max(128),
|
|
20076
|
+
profile_id: HostedGithubTaskInputSchema.shape.profile_id,
|
|
20077
|
+
resumable: external_exports.boolean(),
|
|
20078
|
+
state: external_exports.enum(["running", "completed", "failed", "interrupted"]),
|
|
20079
|
+
repository: HostedGithubRepositoryInputSchema.shape.repository,
|
|
20080
|
+
base_branch: HostedGithubRepositoryInputSchema.shape.base_branch,
|
|
20081
|
+
base_sha: Sha,
|
|
20082
|
+
revision: Revision,
|
|
20083
|
+
error_code: external_exports.string().regex(/^[a-z][a-z0-9_]{0,63}$/u).nullable(),
|
|
20084
|
+
pull_request_url: PullUrl,
|
|
20085
|
+
expires_at: external_exports.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
20086
|
+
api_config_revision: external_exports.string().uuid().optional()
|
|
20087
|
+
}).strict();
|
|
20088
|
+
var bytes = (value) => value.length / 4 * 3 - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0);
|
|
20089
|
+
var Source = external_exports.string().max(2796204).regex(/^[A-Za-z0-9+/]*={0,2}$/u).refine((value) => value.length % 4 === 0 && bytes(value) <= 2 * 1024 * 1024).nullable();
|
|
20090
|
+
var HostedGithubChangeSchema = external_exports.object({
|
|
20091
|
+
path: external_exports.string().max(4096).refine(isCodeSyncPathAllowed),
|
|
20092
|
+
before_base64: Source,
|
|
20093
|
+
after_base64: Source,
|
|
20094
|
+
before_executable: external_exports.boolean().nullable(),
|
|
20095
|
+
after_executable: external_exports.boolean().nullable()
|
|
20096
|
+
}).strict().refine((v) => v.before_base64 === null === (v.before_executable === null) && v.after_base64 === null === (v.after_executable === null) && (v.before_base64 !== null || v.after_base64 !== null));
|
|
20097
|
+
var HostedGithubTaskSchema = HostedGithubTaskSummarySchema.extend({
|
|
20098
|
+
answer: external_exports.string().max(14e3).nullable(),
|
|
20099
|
+
changes: external_exports.array(HostedGithubChangeSchema).max(2e3)
|
|
20100
|
+
}).strict().refine((v) => {
|
|
20101
|
+
if (new Set(v.changes.map((c) => c.path)).size !== v.changes.length)
|
|
20102
|
+
return false;
|
|
20103
|
+
for (const side of ["before_base64", "after_base64"]) {
|
|
20104
|
+
const values = v.changes.map((c) => c[side]).filter((s) => s !== null);
|
|
20105
|
+
if (values.length > 1e3 || values.reduce((sum, s) => sum + bytes(s), 0) > 8 * 1024 * 1024)
|
|
20106
|
+
return false;
|
|
20107
|
+
}
|
|
20108
|
+
return !v.resumable || v.state !== "running" && v.pull_request_url === null;
|
|
20109
|
+
});
|
|
20110
|
+
var HostedGithubTaskListSchema = external_exports.object({ tasks: external_exports.array(HostedGithubTaskSummarySchema).max(10) }).strict();
|
|
20111
|
+
|
|
19830
20112
|
// ../protocol/dist/src/index.js
|
|
19831
20113
|
var sessionModes = ["solo", "multi"];
|
|
19832
20114
|
var membershipRoles = ["owner", "participant", "viewer"];
|
|
@@ -19841,7 +20123,8 @@ var eventTypes = [
|
|
|
19841
20123
|
"attachment",
|
|
19842
20124
|
"context_snapshot",
|
|
19843
20125
|
"membership_change",
|
|
19844
|
-
"session_state_change"
|
|
20126
|
+
"session_state_change",
|
|
20127
|
+
"message_deleted"
|
|
19845
20128
|
];
|
|
19846
20129
|
var eventVisibilities = ["session", "owner_only"];
|
|
19847
20130
|
var captureFidelities = [
|
|
@@ -19938,8 +20221,18 @@ var CanonicalEventSchema = external_exports.object({
|
|
|
19938
20221
|
visibility: EventVisibilitySchema,
|
|
19939
20222
|
reply_to_event_id: IdSchema.nullable(),
|
|
19940
20223
|
payload: JsonValueSchema,
|
|
19941
|
-
runtime_provenance: RuntimeProvenanceSchema.nullable()
|
|
20224
|
+
runtime_provenance: RuntimeProvenanceSchema.nullable(),
|
|
20225
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
20226
|
+
deleted: external_exports.object({ deleted_at: external_exports.string().datetime(), reason: external_exports.enum(["author", "request", "summary_invalidated"]) }).strict().optional()
|
|
19942
20227
|
});
|
|
20228
|
+
var DeleteMessageInputSchema = external_exports.object({ idempotency_key: IdempotencyKeySchema }).strict();
|
|
20229
|
+
var DeleteMessageResultSchema = external_exports.object({
|
|
20230
|
+
event: CanonicalEventSchema,
|
|
20231
|
+
target_event_id: IdSchema,
|
|
20232
|
+
content_revision: external_exports.number().int().nonnegative(),
|
|
20233
|
+
affected_event_ids: external_exports.array(IdSchema).max(256),
|
|
20234
|
+
affected_truncated: external_exports.boolean()
|
|
20235
|
+
}).strict();
|
|
19943
20236
|
var MessageMentionsSchema = external_exports.array(external_exports.object({
|
|
19944
20237
|
user_id: IdSchema,
|
|
19945
20238
|
start: external_exports.number().int().nonnegative().max(1e6),
|
|
@@ -19968,9 +20261,70 @@ var AppendEventInputSchema = external_exports.object({
|
|
|
19968
20261
|
payload: JsonValueSchema,
|
|
19969
20262
|
runtime_id: IdSchema.optional(),
|
|
19970
20263
|
claim_attempt: external_exports.number().int().positive().optional(),
|
|
20264
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
19971
20265
|
observed_model: external_exports.string().trim().min(1).max(160).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u).optional(),
|
|
19972
20266
|
observed_reasoning_effort: external_exports.string().trim().min(1).max(80).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u).optional()
|
|
19973
20267
|
});
|
|
20268
|
+
var HostedAgentRequestInputSchema = external_exports.object({
|
|
20269
|
+
profile_id: external_exports.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/u).default("default"),
|
|
20270
|
+
content: external_exports.string().trim().min(1).max(6e3),
|
|
20271
|
+
include_code: external_exports.boolean().default(false),
|
|
20272
|
+
idempotency_key: IdempotencyKeySchema,
|
|
20273
|
+
reply_to_event_id: IdSchema.nullable().optional(),
|
|
20274
|
+
github_task_id: external_exports.string().regex(/^gh-task-[a-f0-9]{32}$/u).optional(),
|
|
20275
|
+
continue_from_event_id: IdSchema.optional(),
|
|
20276
|
+
api_config_revision: PersonalCloudRevisionSchema,
|
|
20277
|
+
content_revision: external_exports.number().int().nonnegative().optional()
|
|
20278
|
+
}).strict().refine(hasValidPersonalCloudRevision, { message: "Personal cloud profiles require their exact configuration revision" });
|
|
20279
|
+
var HostedAgentFailurePayloadSchema = external_exports.object({
|
|
20280
|
+
content: external_exports.string().min(1).max(512),
|
|
20281
|
+
status: external_exports.literal("failed"),
|
|
20282
|
+
outcome: external_exports.enum(["timed_out", "failed"]),
|
|
20283
|
+
error: external_exports.object({
|
|
20284
|
+
code: external_exports.string().regex(/^[a-z][a-z0-9_]{0,63}$/u),
|
|
20285
|
+
provider_attempts: external_exports.number().int().min(0).max(64).optional()
|
|
20286
|
+
}).strict()
|
|
20287
|
+
}).strict();
|
|
20288
|
+
var HostedHistorySummaryInputSchema = CreateHistorySummaryInputSchema.omit({ execution_profile: true }).extend({ profile_id: HostedAgentRequestInputSchema.shape.profile_id.unwrap(), api_config_revision: PersonalCloudRevisionSchema, continue_from_event_id: IdSchema.optional() }).strict().refine(hasValidPersonalCloudRevision, { message: "Personal cloud profiles require their exact configuration revision" });
|
|
20289
|
+
var HostedAgentAcceptedSchema = external_exports.object({
|
|
20290
|
+
request_event: CanonicalEventSchema,
|
|
20291
|
+
replayed: external_exports.boolean()
|
|
20292
|
+
}).strict();
|
|
20293
|
+
var HostedAgentPausedSchema = external_exports.object({
|
|
20294
|
+
request_event_id: IdSchema,
|
|
20295
|
+
status: external_exports.literal("paused")
|
|
20296
|
+
}).strict();
|
|
20297
|
+
var HostedAgentStatusSchema = external_exports.discriminatedUnion("enabled", [
|
|
20298
|
+
external_exports.object({ enabled: external_exports.literal(false) }).strict(),
|
|
20299
|
+
external_exports.object({
|
|
20300
|
+
enabled: external_exports.literal(true),
|
|
20301
|
+
harness: external_exports.literal("opencode"),
|
|
20302
|
+
utc_day: external_exports.string(),
|
|
20303
|
+
user_limit_runs: external_exports.number().int().nonnegative().nullable(),
|
|
20304
|
+
user_used_runs: external_exports.number().int().nonnegative(),
|
|
20305
|
+
global_limit_runs: external_exports.number().int().nonnegative().nullable(),
|
|
20306
|
+
global_used_runs: external_exports.number().int().nonnegative(),
|
|
20307
|
+
rate_limits: external_exports.object({
|
|
20308
|
+
user_min_interval_seconds: external_exports.number().int().min(1).max(3600),
|
|
20309
|
+
user_max_concurrent: external_exports.number().int().min(1).max(8),
|
|
20310
|
+
user_active_runs: external_exports.number().int().nonnegative(),
|
|
20311
|
+
retry_after_seconds: external_exports.number().int().nonnegative()
|
|
20312
|
+
}).strict().optional(),
|
|
20313
|
+
profiles: external_exports.array(external_exports.object({
|
|
20314
|
+
id: external_exports.string(),
|
|
20315
|
+
label: external_exports.string(),
|
|
20316
|
+
provider: external_exports.string(),
|
|
20317
|
+
model: external_exports.string(),
|
|
20318
|
+
available: external_exports.boolean(),
|
|
20319
|
+
capacity: external_exports.number().int().nonnegative(),
|
|
20320
|
+
status: external_exports.enum(["available", "busy", "daily_limit", "cooldown", "user_busy", "rate_limit"]),
|
|
20321
|
+
source: external_exports.enum(["service", "personal"]).optional(),
|
|
20322
|
+
api_config_revision: external_exports.string().uuid().optional()
|
|
20323
|
+
}).strict()),
|
|
20324
|
+
capabilities: external_exports.array(external_exports.string()),
|
|
20325
|
+
privacy: external_exports.string()
|
|
20326
|
+
}).strict()
|
|
20327
|
+
]);
|
|
19974
20328
|
var SingleLineTitleSchema = external_exports.string().regex(/^[^\u0000-\u001f\u007f-\u009f]*$/u, "Title must not contain control characters").trim().min(1).max(200);
|
|
19975
20329
|
var SessionTitleSchema = SingleLineTitleSchema;
|
|
19976
20330
|
var ProjectTitleSchema = SingleLineTitleSchema;
|
|
@@ -20017,7 +20371,12 @@ var ProjectListItemSchema = ProjectRecordSchema.omit({ owner_user_id: true }).ex
|
|
|
20017
20371
|
role: MembershipRoleSchema,
|
|
20018
20372
|
session_count: external_exports.number().int().nonnegative()
|
|
20019
20373
|
});
|
|
20374
|
+
var AvatarIdSchema = external_exports.enum(["cat", "fox", "bear", "rabbit", "penguin", "owl"]);
|
|
20375
|
+
var SetAccountAvatarInputSchema = external_exports.object({ avatar_id: AvatarIdSchema.nullable() }).strict();
|
|
20376
|
+
var AccountAvatarProfileSchema = external_exports.object({ user_id: IdSchema, avatar_id: AvatarIdSchema.nullable() });
|
|
20377
|
+
var SessionAvatarProfilesSchema = external_exports.object({ profiles: external_exports.array(AccountAvatarProfileSchema) });
|
|
20020
20378
|
var ProjectMemberRecordSchema = external_exports.object({
|
|
20379
|
+
avatar_id: AvatarIdSchema.nullable().optional(),
|
|
20021
20380
|
user_id: IdSchema,
|
|
20022
20381
|
display_name: external_exports.string().trim().min(1).max(120),
|
|
20023
20382
|
role: MembershipRoleSchema
|
|
@@ -20072,6 +20431,17 @@ var ClaimInvitationInputSchema = external_exports.object({
|
|
|
20072
20431
|
var AcceptInvitationInputSchema = external_exports.object({
|
|
20073
20432
|
invite_token: external_exports.string().min(32).max(512)
|
|
20074
20433
|
});
|
|
20434
|
+
var PreviewProjectInvitationInputSchema = external_exports.object({
|
|
20435
|
+
invite_token: external_exports.string().regex(/^gti_[A-Za-z0-9_-]{32,508}$/)
|
|
20436
|
+
}).strict();
|
|
20437
|
+
var ProjectInvitationPreviewSchema = external_exports.object({
|
|
20438
|
+
project: external_exports.object({ id: IdSchema, title: external_exports.string().min(1).max(200) }).strict(),
|
|
20439
|
+
inviter_display_name: external_exports.string().min(1).max(120),
|
|
20440
|
+
role: InvitationRoleSchema,
|
|
20441
|
+
created_at: external_exports.string().datetime(),
|
|
20442
|
+
expires_at: external_exports.string().datetime(),
|
|
20443
|
+
already_member: external_exports.boolean()
|
|
20444
|
+
}).strict();
|
|
20075
20445
|
var InvitationRecordSchema = external_exports.object({
|
|
20076
20446
|
id: IdSchema,
|
|
20077
20447
|
session_id: IdSchema,
|
|
@@ -20146,6 +20516,10 @@ var DeviceAuthorizationRecordSchema = external_exports.object({
|
|
|
20146
20516
|
claimed_at: external_exports.string().datetime().nullable(),
|
|
20147
20517
|
claimed_by_device_id: IdSchema.nullable()
|
|
20148
20518
|
});
|
|
20519
|
+
var CodexLauncherAuthorizationSchema = external_exports.object({
|
|
20520
|
+
authorization: DeviceAuthorizationRecordSchema.strict(),
|
|
20521
|
+
email: RegistrationEmailSchema
|
|
20522
|
+
}).strict();
|
|
20149
20523
|
var RotateDeviceTokenInputSchema = external_exports.object({
|
|
20150
20524
|
expires_at: external_exports.string().datetime().nullable().optional()
|
|
20151
20525
|
});
|
|
@@ -20180,6 +20554,7 @@ var RegisterRuntimeInputSchema = external_exports.object({
|
|
|
20180
20554
|
provider: external_exports.string().trim().min(1).max(80),
|
|
20181
20555
|
model: external_exports.string().trim().min(1).max(160),
|
|
20182
20556
|
execution_profiles: RuntimeExecutionProfilesSchema.optional(),
|
|
20557
|
+
context_deletion_version: external_exports.literal(1).optional(),
|
|
20183
20558
|
local_session_id: external_exports.string().trim().min(1).max(512),
|
|
20184
20559
|
capture_fidelity: CaptureFidelitySchema
|
|
20185
20560
|
});
|
|
@@ -20193,11 +20568,13 @@ var AgentRequestClaimSchema = external_exports.object({
|
|
|
20193
20568
|
// `paused` is a decision by the requester, not a claim a runtime can execute.
|
|
20194
20569
|
// It is reported so a connector can skip the request without advancing past it.
|
|
20195
20570
|
status: external_exports.enum(["claimed", "completed", "paused"]),
|
|
20196
|
-
attempt_count: external_exports.number().int().positive().optional()
|
|
20571
|
+
attempt_count: external_exports.number().int().positive().optional(),
|
|
20572
|
+
content_revision: external_exports.number().int().nonnegative().optional()
|
|
20197
20573
|
}).strict();
|
|
20198
20574
|
var CompleteAgentRequestInputSchema = external_exports.object({
|
|
20199
20575
|
runtime_id: IdSchema,
|
|
20200
20576
|
claim_attempt: external_exports.number().int().positive().optional(),
|
|
20577
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
20201
20578
|
idempotency_key: IdempotencyKeySchema,
|
|
20202
20579
|
payload: JsonValueSchema,
|
|
20203
20580
|
observed_model: external_exports.string().trim().min(1).max(160).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u).optional(),
|
|
@@ -20212,6 +20589,7 @@ var CommitLocalTurnInputSchema = external_exports.object({
|
|
|
20212
20589
|
local_turn_id: IdSchema,
|
|
20213
20590
|
runtime_id: IdSchema,
|
|
20214
20591
|
based_on_sequence: external_exports.number().int().nonnegative(),
|
|
20592
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
20215
20593
|
occurred_at: external_exports.string().datetime(),
|
|
20216
20594
|
observed_model: external_exports.string().trim().min(1).max(160).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u).optional(),
|
|
20217
20595
|
observed_reasoning_effort: external_exports.string().trim().min(1).max(80).regex(/^[^\u0000-\u001f\u007f-\u009f]+$/u).optional(),
|
|
@@ -20232,6 +20610,7 @@ var snapshotRequestStatuses = ["pending", "claimed", "completed", "failed"];
|
|
|
20232
20610
|
var SnapshotRequestStatusSchema = external_exports.enum(snapshotRequestStatuses);
|
|
20233
20611
|
var snapshotRequestKinds = [
|
|
20234
20612
|
...codeSyncRequestKinds,
|
|
20613
|
+
...githubCodeSyncRequestKinds,
|
|
20235
20614
|
"immutable",
|
|
20236
20615
|
"visible_history_replace",
|
|
20237
20616
|
"local_sync_status",
|
|
@@ -20264,6 +20643,7 @@ var SnapshotRequestRecordSchema = external_exports.object({
|
|
|
20264
20643
|
kind: SnapshotRequestKindSchema,
|
|
20265
20644
|
through_sequence: external_exports.number().int().nonnegative(),
|
|
20266
20645
|
status: SnapshotRequestStatusSchema,
|
|
20646
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
20267
20647
|
target_runtime_id: IdSchema.nullable(),
|
|
20268
20648
|
claimed_by_runtime_id: IdSchema.nullable(),
|
|
20269
20649
|
created_at: external_exports.string().datetime(),
|
|
@@ -20289,7 +20669,8 @@ var SubscribeMessageSchema = external_exports.object({
|
|
|
20289
20669
|
var ReplayResponseSchema = external_exports.object({
|
|
20290
20670
|
events: external_exports.array(CanonicalEventSchema),
|
|
20291
20671
|
cursor: external_exports.number().int().nonnegative(),
|
|
20292
|
-
has_more: external_exports.boolean()
|
|
20672
|
+
has_more: external_exports.boolean(),
|
|
20673
|
+
content_revision: external_exports.number().int().nonnegative().optional()
|
|
20293
20674
|
});
|
|
20294
20675
|
var SessionListItemSchema = external_exports.object({
|
|
20295
20676
|
id: IdSchema,
|
|
@@ -20300,6 +20681,7 @@ var SessionListItemSchema = external_exports.object({
|
|
|
20300
20681
|
state: external_exports.enum(["active", "archived"]),
|
|
20301
20682
|
role: MembershipRoleSchema,
|
|
20302
20683
|
current_sequence: external_exports.number().int().nonnegative(),
|
|
20684
|
+
content_revision: external_exports.number().int().nonnegative().optional(),
|
|
20303
20685
|
member_count: external_exports.number().int().nonnegative().optional(),
|
|
20304
20686
|
updated_at: external_exports.string().datetime()
|
|
20305
20687
|
});
|
|
@@ -20403,7 +20785,8 @@ var HttpCollaborationClient = class {
|
|
|
20403
20785
|
return {
|
|
20404
20786
|
events,
|
|
20405
20787
|
nextSequence: requiredNumber(body.cursor, "cursor"),
|
|
20406
|
-
hasMore: body.has_more === true
|
|
20788
|
+
hasMore: body.has_more === true,
|
|
20789
|
+
...body.content_revision === void 0 ? {} : { contentRevision: requiredRevision(body.content_revision, "events.content_revision") }
|
|
20407
20790
|
};
|
|
20408
20791
|
}
|
|
20409
20792
|
async readContext(sessionId, view, throughSequence) {
|
|
@@ -20461,7 +20844,8 @@ var HttpCollaborationClient = class {
|
|
|
20461
20844
|
status,
|
|
20462
20845
|
requestId: requiredString(body.request_event_id, "claim.request_event_id"),
|
|
20463
20846
|
runtimeId: requiredString(body.runtime_id, "claim.runtime_id"),
|
|
20464
|
-
attemptCount
|
|
20847
|
+
attemptCount,
|
|
20848
|
+
...body.content_revision === void 0 ? {} : { contentRevision: requiredRevision(body.content_revision, "claim.content_revision") }
|
|
20465
20849
|
};
|
|
20466
20850
|
}
|
|
20467
20851
|
async completeAgentRequest(sessionId, requestId, input2) {
|
|
@@ -20470,6 +20854,7 @@ var HttpCollaborationClient = class {
|
|
|
20470
20854
|
body: JSON.stringify({
|
|
20471
20855
|
runtime_id: input2.runtimeId,
|
|
20472
20856
|
...input2.claimAttempt === void 0 ? {} : { claim_attempt: input2.claimAttempt },
|
|
20857
|
+
...input2.contentRevision === void 0 ? {} : { content_revision: input2.contentRevision },
|
|
20473
20858
|
idempotency_key: input2.idempotencyKey,
|
|
20474
20859
|
payload: truncateJsonValue(input2.payload, 160 * 1024),
|
|
20475
20860
|
...input2.observedModel === void 0 ? {} : { observed_model: input2.observedModel },
|
|
@@ -20484,6 +20869,7 @@ var HttpCollaborationClient = class {
|
|
|
20484
20869
|
body: JSON.stringify({
|
|
20485
20870
|
runtime_id: input2.runtimeId,
|
|
20486
20871
|
...input2.claimAttempt === void 0 ? {} : { claim_attempt: input2.claimAttempt },
|
|
20872
|
+
...input2.contentRevision === void 0 ? {} : { content_revision: input2.contentRevision },
|
|
20487
20873
|
idempotency_key: input2.idempotencyKey,
|
|
20488
20874
|
payload: truncateJsonValue(input2.payload, 32 * 1024),
|
|
20489
20875
|
...input2.observedModel === void 0 ? {} : { observed_model: input2.observedModel },
|
|
@@ -20500,6 +20886,7 @@ var HttpCollaborationClient = class {
|
|
|
20500
20886
|
local_turn_id: input2.localTurnId,
|
|
20501
20887
|
runtime_id: input2.runtimeId,
|
|
20502
20888
|
based_on_sequence: input2.basedOnSequence,
|
|
20889
|
+
...input2.contentRevision === void 0 ? {} : { content_revision: input2.contentRevision },
|
|
20503
20890
|
occurred_at: input2.occurredAt,
|
|
20504
20891
|
...input2.observedModel === void 0 ? {} : { observed_model: input2.observedModel },
|
|
20505
20892
|
...input2.observedReasoningEffort === void 0 ? {} : { observed_reasoning_effort: input2.observedReasoningEffort },
|
|
@@ -20553,9 +20940,9 @@ var HttpCollaborationClient = class {
|
|
|
20553
20940
|
}));
|
|
20554
20941
|
return fromWireSnapshotRequest(body.snapshot_request ?? body);
|
|
20555
20942
|
}
|
|
20556
|
-
async #request(
|
|
20943
|
+
async #request(path11, init = {}) {
|
|
20557
20944
|
const signal = combineSignals(init.signal ?? void 0, this.#signal, AbortSignal.timeout(this.#requestTimeoutMs));
|
|
20558
|
-
const response = await this.#fetch(`${this.#baseUrl}${
|
|
20945
|
+
const response = await this.#fetch(`${this.#baseUrl}${path11}`, {
|
|
20559
20946
|
...init,
|
|
20560
20947
|
redirect: "error",
|
|
20561
20948
|
signal,
|
|
@@ -20606,6 +20993,7 @@ function toWireEvent(event) {
|
|
|
20606
20993
|
visibility: event.visibility ?? "session",
|
|
20607
20994
|
runtime_id: event.runtimeId ?? event.runtime?.runtimeId,
|
|
20608
20995
|
claim_attempt: event.claimAttempt,
|
|
20996
|
+
...event.contentRevision === void 0 ? {} : { content_revision: event.contentRevision },
|
|
20609
20997
|
observed_model: event.observedModel,
|
|
20610
20998
|
observed_reasoning_effort: event.observedReasoningEffort
|
|
20611
20999
|
};
|
|
@@ -20631,7 +21019,8 @@ function toWireRuntimeRegistration(runtime) {
|
|
|
20631
21019
|
local_session_id: runtime.localSessionId,
|
|
20632
21020
|
capture_fidelity: runtime.captureFidelity,
|
|
20633
21021
|
...runtime.capabilities === void 0 ? {} : { capabilities: [...runtime.capabilities] },
|
|
20634
|
-
...runtime.purpose === void 0 ? {} : { purpose: runtime.purpose }
|
|
21022
|
+
...runtime.purpose === void 0 ? {} : { purpose: runtime.purpose },
|
|
21023
|
+
...runtime.contextDeletionVersion === void 0 ? {} : { context_deletion_version: runtime.contextDeletionVersion }
|
|
20635
21024
|
};
|
|
20636
21025
|
}
|
|
20637
21026
|
function validateContextReadInput(sessionId, view, throughSequence) {
|
|
@@ -20692,7 +21081,17 @@ function parseHistoryContext(value, expectedView, throughSequence) {
|
|
|
20692
21081
|
if (items.some((item) => sourceIds.has(item.event_id))) {
|
|
20693
21082
|
throw new Error("Collaboration API context repeats summarized sources");
|
|
20694
21083
|
}
|
|
20695
|
-
return {
|
|
21084
|
+
return {
|
|
21085
|
+
view: value.view,
|
|
21086
|
+
through_sequence: Number(value.through_sequence),
|
|
21087
|
+
items,
|
|
21088
|
+
...value.content_revision === void 0 ? {} : { content_revision: requiredRevision(value.content_revision, "context.content_revision") }
|
|
21089
|
+
};
|
|
21090
|
+
}
|
|
21091
|
+
function requiredRevision(value, label) {
|
|
21092
|
+
if (!Number.isSafeInteger(value) || Number(value) < 0)
|
|
21093
|
+
throw new Error(`Collaboration API returned invalid ${label}`);
|
|
21094
|
+
return Number(value);
|
|
20696
21095
|
}
|
|
20697
21096
|
function isContextId(value) {
|
|
20698
21097
|
return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value);
|
|
@@ -20711,9 +21110,17 @@ function fromWireEvent(value) {
|
|
|
20711
21110
|
...typeof input2.reply_to_event_id === "string" ? { replyTo: input2.reply_to_event_id } : {},
|
|
20712
21111
|
timestamp: requiredString(input2.created_at, "event.created_at"),
|
|
20713
21112
|
payload: input2.payload,
|
|
21113
|
+
...input2.content_revision === void 0 ? {} : { contentRevision: requiredRevision(input2.content_revision, "event.content_revision") },
|
|
21114
|
+
...input2.deleted === void 0 ? {} : { deleted: fromWireDeletion(input2.deleted) },
|
|
20714
21115
|
...runtime === void 0 ? {} : { runtime }
|
|
20715
21116
|
};
|
|
20716
21117
|
}
|
|
21118
|
+
function fromWireDeletion(value) {
|
|
21119
|
+
const input2 = requiredObject(value);
|
|
21120
|
+
if (input2.reason !== "author" && input2.reason !== "request" && input2.reason !== "summary_invalidated")
|
|
21121
|
+
throw new Error("Collaboration API returned invalid event.deleted reason");
|
|
21122
|
+
return { deletedAt: requiredString(input2.deleted_at, "event.deleted.deleted_at"), reason: input2.reason };
|
|
21123
|
+
}
|
|
20717
21124
|
function fromWireSnapshotRequest(value) {
|
|
20718
21125
|
const input2 = requiredObject(value);
|
|
20719
21126
|
const status = requiredString(input2.status, "snapshot_request.status");
|
|
@@ -20749,6 +21156,7 @@ function fromWireSnapshotRequest(value) {
|
|
|
20749
21156
|
kind,
|
|
20750
21157
|
...typeof input2.target_runtime_id === "string" ? { targetRuntimeId: input2.target_runtime_id } : {},
|
|
20751
21158
|
throughSequence: requiredNumber(input2.through_sequence, "snapshot_request.through_sequence"),
|
|
21159
|
+
...input2.content_revision === void 0 ? {} : { contentRevision: requiredRevision(input2.content_revision, "snapshot_request.content_revision") },
|
|
20752
21160
|
status,
|
|
20753
21161
|
...createdAt === void 0 ? {} : { createdAt },
|
|
20754
21162
|
..."result" in input2 ? { result: input2.result } : {},
|
|
@@ -20768,7 +21176,8 @@ function fromWireSession(value) {
|
|
|
20768
21176
|
...input2.state === "active" || input2.state === "archived" ? { state: input2.state } : {},
|
|
20769
21177
|
...name2 === void 0 ? {} : { name: name2 },
|
|
20770
21178
|
...role === void 0 ? {} : { role },
|
|
20771
|
-
...latestSequence === void 0 ? {} : { latestSequence }
|
|
21179
|
+
...latestSequence === void 0 ? {} : { latestSequence },
|
|
21180
|
+
...input2.content_revision === void 0 ? {} : { contentRevision: requiredRevision(input2.content_revision, "session.content_revision") }
|
|
20772
21181
|
};
|
|
20773
21182
|
}
|
|
20774
21183
|
function fromWireProject(value) {
|
|
@@ -20815,7 +21224,8 @@ function fromWireRuntime(value) {
|
|
|
20815
21224
|
...input2.execution_profiles === void 0 || input2.execution_profiles === null ? {} : { executionProfiles: fromWireExecutionProfiles(input2.execution_profiles) },
|
|
20816
21225
|
localSessionId: requiredString(input2.local_session_id, "runtime.local_session_id"),
|
|
20817
21226
|
captureFidelity: requiredString(input2.capture_fidelity, "runtime.capture_fidelity"),
|
|
20818
|
-
purpose
|
|
21227
|
+
purpose,
|
|
21228
|
+
...input2.context_deletion_version === 1 ? { contextDeletionVersion: 1 } : {}
|
|
20819
21229
|
};
|
|
20820
21230
|
}
|
|
20821
21231
|
function fromWireExecutionProfiles(value) {
|
|
@@ -20946,8 +21356,8 @@ function isObject2(value) {
|
|
|
20946
21356
|
|
|
20947
21357
|
// ../adapters/dist/src/redaction.js
|
|
20948
21358
|
var REDACTED = "[REDACTED]";
|
|
21359
|
+
var PRIVATE_KEY_PATTERN = /-----BEGIN (?:ENCRYPTED |RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\s\S]*?(?:-----END (?:ENCRYPTED |RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----|$)/g;
|
|
20949
21360
|
var DEFAULT_SECRET_PATTERNS = [
|
|
20950
|
-
/-----BEGIN (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/g,
|
|
20951
21361
|
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
20952
21362
|
/\b(?:ghp|github_pat|glpat|sk|xox[baprs])[-_][A-Za-z0-9_-]{16,}\b/g,
|
|
20953
21363
|
/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}\b/gi,
|
|
@@ -20960,6 +21370,8 @@ var SENSITIVE_KEY_NAMES = /* @__PURE__ */ new Set([
|
|
|
20960
21370
|
"accesstoken",
|
|
20961
21371
|
"authtoken",
|
|
20962
21372
|
"authorization",
|
|
21373
|
+
"cookie",
|
|
21374
|
+
"setcookie",
|
|
20963
21375
|
"bearer",
|
|
20964
21376
|
"clientsecret",
|
|
20965
21377
|
"password",
|
|
@@ -20976,7 +21388,83 @@ var SENSITIVE_KEY_NAMES = /* @__PURE__ */ new Set([
|
|
|
20976
21388
|
]);
|
|
20977
21389
|
function redactText(value, options = {}) {
|
|
20978
21390
|
const replacement = options.replacement ?? REDACTED;
|
|
20979
|
-
return [...DEFAULT_SECRET_PATTERNS, ...options.extraPatterns ?? []].reduce(
|
|
21391
|
+
return [...DEFAULT_SECRET_PATTERNS, ...options.extraPatterns ?? []].reduce(
|
|
21392
|
+
(redacted, pattern) => redacted.replace(cloneGlobal(pattern), (match, prefix) => typeof prefix === "string" && prefix.length < match.length ? `${prefix}${replacement}` : replacement),
|
|
21393
|
+
// Mask PEM first: scanning `private_key: -----BEGIN ...` as an ordinary
|
|
21394
|
+
// assignment must not break its header and leave the following body public.
|
|
21395
|
+
redactSensitiveFields(value.replace(cloneGlobal(PRIVATE_KEY_PATTERN), () => replacement), replacement)
|
|
21396
|
+
);
|
|
21397
|
+
}
|
|
21398
|
+
function redactSensitiveFields(text, replacement) {
|
|
21399
|
+
const fields = /("(?:\\.|[^"\\\r\n])*"|'(?:\\.|[^'\\\r\n])*'|\b[A-Za-z_][A-Za-z0-9_.-]*)(\s*[:=]\s*)/gu;
|
|
21400
|
+
const parts = [];
|
|
21401
|
+
let retainedFrom = 0;
|
|
21402
|
+
let match;
|
|
21403
|
+
while ((match = fields.exec(text)) !== null) {
|
|
21404
|
+
const encodedKey = match[1];
|
|
21405
|
+
let key = encodedKey;
|
|
21406
|
+
if (encodedKey.startsWith('"')) {
|
|
21407
|
+
try {
|
|
21408
|
+
key = JSON.parse(encodedKey);
|
|
21409
|
+
} catch {
|
|
21410
|
+
continue;
|
|
21411
|
+
}
|
|
21412
|
+
} else if (encodedKey.startsWith("'"))
|
|
21413
|
+
key = encodedKey.slice(1, -1);
|
|
21414
|
+
if (!isSensitiveKey(key))
|
|
21415
|
+
continue;
|
|
21416
|
+
const start = fields.lastIndex;
|
|
21417
|
+
const end = sensitiveValueEnd(text, start, encodedKey[0] !== '"' && encodedKey[0] !== "'" && /^(?:authorization|cookie|set-cookie)$/iu.test(key));
|
|
21418
|
+
const quote = text[start];
|
|
21419
|
+
const masked = quote === "'" ? `'${replacement.replace(/\\/gu, "\\\\").replace(/'/gu, "\\'")}'` : quote === '"' || encodedKey.startsWith('"') ? JSON.stringify(replacement) : replacement;
|
|
21420
|
+
parts.push(text.slice(retainedFrom, start), masked);
|
|
21421
|
+
retainedFrom = end;
|
|
21422
|
+
fields.lastIndex = end;
|
|
21423
|
+
}
|
|
21424
|
+
return parts.length === 0 ? text : parts.join("") + text.slice(retainedFrom);
|
|
21425
|
+
}
|
|
21426
|
+
function sensitiveValueEnd(text, start, header) {
|
|
21427
|
+
if (header) {
|
|
21428
|
+
const end = text.indexOf("\n", start);
|
|
21429
|
+
return end < 0 ? text.length : end;
|
|
21430
|
+
}
|
|
21431
|
+
const first = text[start];
|
|
21432
|
+
if (first === '"' || first === "'") {
|
|
21433
|
+
for (let index2 = start + 1; index2 < text.length; index2++) {
|
|
21434
|
+
if (text[index2] === "\\")
|
|
21435
|
+
index2++;
|
|
21436
|
+
else if (text[index2] === first)
|
|
21437
|
+
return index2 + 1;
|
|
21438
|
+
}
|
|
21439
|
+
return text.length;
|
|
21440
|
+
}
|
|
21441
|
+
if (first === "{" || first === "[") {
|
|
21442
|
+
const closing = [first === "{" ? "}" : "]"];
|
|
21443
|
+
let quote;
|
|
21444
|
+
for (let index2 = start + 1; index2 < text.length; index2++) {
|
|
21445
|
+
const character = text[index2];
|
|
21446
|
+
if (quote !== void 0) {
|
|
21447
|
+
if (character === "\\")
|
|
21448
|
+
index2++;
|
|
21449
|
+
else if (character === quote)
|
|
21450
|
+
quote = void 0;
|
|
21451
|
+
} else if (character === '"' || character === "'")
|
|
21452
|
+
quote = character;
|
|
21453
|
+
else if (character === "{" || character === "[")
|
|
21454
|
+
closing.push(character === "{" ? "}" : "]");
|
|
21455
|
+
else if (character === "}" || character === "]") {
|
|
21456
|
+
if (character !== closing.pop())
|
|
21457
|
+
return text.length;
|
|
21458
|
+
if (closing.length === 0)
|
|
21459
|
+
return index2 + 1;
|
|
21460
|
+
}
|
|
21461
|
+
}
|
|
21462
|
+
return text.length;
|
|
21463
|
+
}
|
|
21464
|
+
let index = start;
|
|
21465
|
+
while (index < text.length && !/[\s,;}\]]/u.test(text[index]))
|
|
21466
|
+
index++;
|
|
21467
|
+
return index;
|
|
20980
21468
|
}
|
|
20981
21469
|
function redactValue(value, options = {}) {
|
|
20982
21470
|
if (typeof value === "string")
|
|
@@ -20998,6 +21486,208 @@ function cloneGlobal(pattern) {
|
|
|
20998
21486
|
return new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
|
|
20999
21487
|
}
|
|
21000
21488
|
|
|
21489
|
+
// ../adapters/dist/src/live-output.js
|
|
21490
|
+
import { randomUUID } from "node:crypto";
|
|
21491
|
+
var MAX_ITEMS = 128;
|
|
21492
|
+
var MAX_INPUT_BYTES = 96 * 1024;
|
|
21493
|
+
var MAX_PUBLIC_BYTES = 24 * 1024;
|
|
21494
|
+
var MAX_PUBLICATIONS = 1200;
|
|
21495
|
+
function createLiveOutputPublisher(options) {
|
|
21496
|
+
const items = /* @__PURE__ */ new Map();
|
|
21497
|
+
const now = options.now ?? Date.now;
|
|
21498
|
+
const intervalMs = options.intervalMs ?? 1500;
|
|
21499
|
+
let lastPublished, publications = 0, closed = false;
|
|
21500
|
+
let timer;
|
|
21501
|
+
let queue = Promise.resolve();
|
|
21502
|
+
let lastVisited;
|
|
21503
|
+
const serial = (operation) => {
|
|
21504
|
+
queue = queue.then(operation, operation);
|
|
21505
|
+
return queue;
|
|
21506
|
+
};
|
|
21507
|
+
const itemFor = (key, kind, title) => {
|
|
21508
|
+
let item = items.get(key);
|
|
21509
|
+
if (!item && items.size < MAX_ITEMS) {
|
|
21510
|
+
item = {
|
|
21511
|
+
streamId: randomUUID(),
|
|
21512
|
+
raw: "",
|
|
21513
|
+
kind,
|
|
21514
|
+
state: "running",
|
|
21515
|
+
revision: 0,
|
|
21516
|
+
acknowledged: "",
|
|
21517
|
+
dirty: true,
|
|
21518
|
+
truncated: false
|
|
21519
|
+
};
|
|
21520
|
+
items.set(key, item);
|
|
21521
|
+
}
|
|
21522
|
+
if (item && title !== void 0)
|
|
21523
|
+
item.title = bound(redactText(title, options.redaction), 256);
|
|
21524
|
+
return item;
|
|
21525
|
+
};
|
|
21526
|
+
const safe = (item) => {
|
|
21527
|
+
let content = item.raw;
|
|
21528
|
+
if (item.state === "running") {
|
|
21529
|
+
content = content.replace(/(?:\bBearer\s+|\b(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|password|passwd|secret|token)["']?\s*[=:]\s*["']?)[^\n,;]*$/giu, "");
|
|
21530
|
+
let end = content.length;
|
|
21531
|
+
while (end > 0 && /[A-Za-z0-9_./~+=:@%\-]/u.test(content[end - 1]))
|
|
21532
|
+
end--;
|
|
21533
|
+
content = content.slice(0, end);
|
|
21534
|
+
}
|
|
21535
|
+
content = redactText(content, options.redaction);
|
|
21536
|
+
return bound(content, MAX_PUBLIC_BYTES, item.truncated);
|
|
21537
|
+
};
|
|
21538
|
+
const signatureFor = (item) => JSON.stringify([safe(item), item.state, item.title, item.plan]);
|
|
21539
|
+
const schedule = () => {
|
|
21540
|
+
if (closed || options.schedule === false || timer || ![...items.values()].some((x) => x.dirty))
|
|
21541
|
+
return;
|
|
21542
|
+
timer = setTimeout(() => {
|
|
21543
|
+
timer = void 0;
|
|
21544
|
+
void flush().catch(() => void 0);
|
|
21545
|
+
}, intervalMs);
|
|
21546
|
+
timer.unref();
|
|
21547
|
+
};
|
|
21548
|
+
const publishOne = async (item) => {
|
|
21549
|
+
if (!item.dirty || publications >= MAX_PUBLICATIONS)
|
|
21550
|
+
return;
|
|
21551
|
+
const content = safe(item);
|
|
21552
|
+
const signature = JSON.stringify([content, item.state, item.title, item.plan]);
|
|
21553
|
+
if (!item.pending && signature === item.acknowledged) {
|
|
21554
|
+
item.dirty = false;
|
|
21555
|
+
return;
|
|
21556
|
+
}
|
|
21557
|
+
if (!item.pending && !content.trim() && item.plan === void 0)
|
|
21558
|
+
return;
|
|
21559
|
+
const revision = item.revision + 1;
|
|
21560
|
+
const update = {
|
|
21561
|
+
id: `${item.streamId}:${revision}`,
|
|
21562
|
+
content,
|
|
21563
|
+
phase: "stream",
|
|
21564
|
+
streamId: item.streamId,
|
|
21565
|
+
streamMode: "snapshot",
|
|
21566
|
+
streamKind: item.kind,
|
|
21567
|
+
streamRevision: revision,
|
|
21568
|
+
streamState: item.state,
|
|
21569
|
+
...item.title === void 0 ? {} : { title: item.title },
|
|
21570
|
+
...item.plan === void 0 ? {} : { plan: item.plan }
|
|
21571
|
+
};
|
|
21572
|
+
item.pending ??= { update, signature };
|
|
21573
|
+
const pending = item.pending;
|
|
21574
|
+
try {
|
|
21575
|
+
if (await options.publish(pending.update) === false)
|
|
21576
|
+
return;
|
|
21577
|
+
item.revision = pending.update.streamRevision;
|
|
21578
|
+
item.acknowledged = pending.signature;
|
|
21579
|
+
delete item.pending;
|
|
21580
|
+
item.dirty = signatureFor(item) !== item.acknowledged;
|
|
21581
|
+
lastPublished = now();
|
|
21582
|
+
publications++;
|
|
21583
|
+
} catch {
|
|
21584
|
+
}
|
|
21585
|
+
};
|
|
21586
|
+
const flush = () => serial(async () => {
|
|
21587
|
+
if (closed || lastPublished !== void 0 && now() - lastPublished < intervalMs) {
|
|
21588
|
+
schedule();
|
|
21589
|
+
return;
|
|
21590
|
+
}
|
|
21591
|
+
const ordered = [...items.values()];
|
|
21592
|
+
const start = lastVisited === void 0 ? 0 : (ordered.indexOf(lastVisited) + 1) % ordered.length;
|
|
21593
|
+
for (let offset = 0; offset < ordered.length; offset++) {
|
|
21594
|
+
const item = ordered[(start + offset) % ordered.length];
|
|
21595
|
+
if (!item.dirty)
|
|
21596
|
+
continue;
|
|
21597
|
+
lastVisited = item;
|
|
21598
|
+
await publishOne(item);
|
|
21599
|
+
if (lastPublished !== void 0 && now() - lastPublished < intervalMs)
|
|
21600
|
+
break;
|
|
21601
|
+
}
|
|
21602
|
+
schedule();
|
|
21603
|
+
});
|
|
21604
|
+
return {
|
|
21605
|
+
async onDelta(key, delta, kind = "answer", title) {
|
|
21606
|
+
if (closed || !delta)
|
|
21607
|
+
return;
|
|
21608
|
+
const item = itemFor(key, kind, title);
|
|
21609
|
+
if (!item || item.state !== "running" || item.truncated)
|
|
21610
|
+
return;
|
|
21611
|
+
const raw = item.raw + delta;
|
|
21612
|
+
item.raw = bound(raw, MAX_INPUT_BYTES);
|
|
21613
|
+
item.truncated = Buffer.byteLength(raw) > MAX_INPUT_BYTES;
|
|
21614
|
+
item.dirty = true;
|
|
21615
|
+
await flush();
|
|
21616
|
+
},
|
|
21617
|
+
async onSnapshot(key, text, kind = "answer", title, state = "running") {
|
|
21618
|
+
if (closed)
|
|
21619
|
+
return;
|
|
21620
|
+
const item = itemFor(key, kind, title);
|
|
21621
|
+
if (!item)
|
|
21622
|
+
return;
|
|
21623
|
+
item.raw = bound(text, MAX_INPUT_BYTES);
|
|
21624
|
+
item.truncated = Buffer.byteLength(text) > MAX_INPUT_BYTES;
|
|
21625
|
+
item.state = state;
|
|
21626
|
+
item.dirty = true;
|
|
21627
|
+
await flush();
|
|
21628
|
+
},
|
|
21629
|
+
async onPlan(key, steps) {
|
|
21630
|
+
if (closed)
|
|
21631
|
+
return;
|
|
21632
|
+
const item = itemFor(key, "plan");
|
|
21633
|
+
if (!item)
|
|
21634
|
+
return;
|
|
21635
|
+
item.plan = steps.slice(0, 64).map((step) => ({ step: bound(redactText(step.step, options.redaction), 512), status: step.status }));
|
|
21636
|
+
item.raw = item.plan.map((step) => `${step.status}: ${step.step}`).join("\n");
|
|
21637
|
+
item.dirty = true;
|
|
21638
|
+
await flush();
|
|
21639
|
+
},
|
|
21640
|
+
flush,
|
|
21641
|
+
async finish(success2 = true) {
|
|
21642
|
+
if (timer) {
|
|
21643
|
+
clearTimeout(timer);
|
|
21644
|
+
timer = void 0;
|
|
21645
|
+
}
|
|
21646
|
+
await serial(async () => {
|
|
21647
|
+
if (closed)
|
|
21648
|
+
return;
|
|
21649
|
+
for (const item of [...items.values()].slice(0, 64)) {
|
|
21650
|
+
if (item.state === "running") {
|
|
21651
|
+
item.state = success2 ? "completed" : "failed";
|
|
21652
|
+
item.dirty = true;
|
|
21653
|
+
}
|
|
21654
|
+
await publishOne(item);
|
|
21655
|
+
if (item.dirty && !item.pending)
|
|
21656
|
+
await publishOne(item);
|
|
21657
|
+
}
|
|
21658
|
+
closed = true;
|
|
21659
|
+
items.clear();
|
|
21660
|
+
});
|
|
21661
|
+
},
|
|
21662
|
+
lastPublishedAt: () => lastPublished
|
|
21663
|
+
};
|
|
21664
|
+
}
|
|
21665
|
+
function liveOutputPayload(update) {
|
|
21666
|
+
return {
|
|
21667
|
+
content: update.content,
|
|
21668
|
+
phase: update.phase,
|
|
21669
|
+
stream_id: update.streamId,
|
|
21670
|
+
stream_mode: update.streamMode,
|
|
21671
|
+
stream_kind: update.streamKind,
|
|
21672
|
+
stream_revision: update.streamRevision,
|
|
21673
|
+
stream_state: update.streamState,
|
|
21674
|
+
...update.title === void 0 ? {} : { title: update.title },
|
|
21675
|
+
...update.plan === void 0 ? {} : { plan: update.plan.map((step) => ({ step: step.step, status: step.status })) }
|
|
21676
|
+
};
|
|
21677
|
+
}
|
|
21678
|
+
function bound(text, maxBytes, alreadyTruncated = false) {
|
|
21679
|
+
if (!alreadyTruncated && Buffer.byteLength(text) <= maxBytes)
|
|
21680
|
+
return text;
|
|
21681
|
+
const suffix = "\n[TRUNCATED]";
|
|
21682
|
+
const bytes2 = Buffer.from(text).subarray(0, maxBytes - Buffer.byteLength(suffix));
|
|
21683
|
+
let end = bytes2.length;
|
|
21684
|
+
while (end > 0 && (bytes2[end - 1] & 192) === 128)
|
|
21685
|
+
end--;
|
|
21686
|
+
if (end < bytes2.length || (bytes2[end - 1] & 128) !== 0)
|
|
21687
|
+
end = Math.max(0, end - 1);
|
|
21688
|
+
return bytes2.subarray(0, end).toString("utf8") + suffix;
|
|
21689
|
+
}
|
|
21690
|
+
|
|
21001
21691
|
// ../adapters/dist/src/jsonl.js
|
|
21002
21692
|
var DEFAULT_MAX_READ_BYTES = 4 * 1024 * 1024;
|
|
21003
21693
|
|
|
@@ -21204,7 +21894,7 @@ function isNodeError(error62) {
|
|
|
21204
21894
|
var MAX_FRAME_BYTES = 1024 * 1024;
|
|
21205
21895
|
|
|
21206
21896
|
// ../bridge/dist/src/code-sync.js
|
|
21207
|
-
import { createHash as createHash2, randomUUID } from "node:crypto";
|
|
21897
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
21208
21898
|
import { execFile } from "node:child_process";
|
|
21209
21899
|
import { constants } from "node:fs";
|
|
21210
21900
|
import { chmod as chmod2, lstat as lstat2, mkdir as mkdir2, open as open3, readFile as readFile2, realpath, rename, unlink } from "node:fs/promises";
|
|
@@ -21228,10 +21918,14 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21228
21918
|
#apiUrl;
|
|
21229
21919
|
#root = "";
|
|
21230
21920
|
#stateRoot = "";
|
|
21921
|
+
#lockRoot = "";
|
|
21231
21922
|
#initialized;
|
|
21232
21923
|
#stableDigest = "";
|
|
21233
21924
|
#stableSince = 0;
|
|
21234
21925
|
#lastAutomaticAttempt = 0;
|
|
21926
|
+
get #limits() {
|
|
21927
|
+
return this.#options.storage?.limits ?? CLOUD_CODE_SYNC_LIMITS;
|
|
21928
|
+
}
|
|
21235
21929
|
constructor(options) {
|
|
21236
21930
|
const url2 = new URL(options.apiUrl);
|
|
21237
21931
|
if (url2.username || url2.password || url2.search || url2.hash || url2.protocol !== "https:" && !(url2.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url2.hostname))) {
|
|
@@ -21265,6 +21959,9 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21265
21959
|
this.#root = path2.join(await realpath(path2.dirname(requestedRoot)), path2.basename(requestedRoot));
|
|
21266
21960
|
}
|
|
21267
21961
|
this.#stateRoot = this.#options.stateRoot ?? path2.join(homedir2(), ".gatherthread", "code-sync", digest(this.#root));
|
|
21962
|
+
const sharedRoot = this.#stateRoot;
|
|
21963
|
+
if (this.#options.storage)
|
|
21964
|
+
this.#stateRoot = path2.join(this.#stateRoot, digest(this.#options.storage.key));
|
|
21268
21965
|
if (isWithin(this.#root, path2.resolve(this.#stateRoot)))
|
|
21269
21966
|
throw new CodeSyncError("code_sync_binding", "Code sync state must stay outside source files.");
|
|
21270
21967
|
await mkdir2(this.#stateRoot, { recursive: true, mode: 448 });
|
|
@@ -21274,6 +21971,7 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21274
21971
|
if (isWithin(this.#root, this.#stateRoot))
|
|
21275
21972
|
throw new CodeSyncError("code_sync_binding", "Private code sync state resolves inside source files.");
|
|
21276
21973
|
await chmod2(this.#stateRoot, 448);
|
|
21974
|
+
this.#lockRoot = await realpath(sharedRoot);
|
|
21277
21975
|
await this.#locked(async () => {
|
|
21278
21976
|
const inventoryPath = path2.join(this.#stateRoot, "inventory.git");
|
|
21279
21977
|
try {
|
|
@@ -21284,6 +21982,7 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21284
21982
|
throw error62;
|
|
21285
21983
|
}
|
|
21286
21984
|
await git(["init", "--bare", "--quiet", inventoryPath], this.#stateRoot);
|
|
21985
|
+
await this.#options.storage?.initialize(this.#stateRoot);
|
|
21287
21986
|
const filename = path2.join(this.#stateRoot, "binding.json");
|
|
21288
21987
|
try {
|
|
21289
21988
|
await this.#readBinding();
|
|
@@ -21297,7 +21996,9 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21297
21996
|
actor_id: this.#options.actorId,
|
|
21298
21997
|
workspace_path: this.#root,
|
|
21299
21998
|
base_commit: null,
|
|
21300
|
-
|
|
21999
|
+
// GitHub authorization is an explicit local action. New GitHub
|
|
22000
|
+
// bindings follow settled source edits automatically thereafter.
|
|
22001
|
+
automatic_upload: this.#options.storage?.metadata.provider === "github",
|
|
21301
22002
|
baseline: {}
|
|
21302
22003
|
}));
|
|
21303
22004
|
}
|
|
@@ -21348,7 +22049,7 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21348
22049
|
idempotency_key: `code-upload-${digest(JSON.stringify([this.#options.projectId, this.#options.actorId, uploadBase, inventory.hashes]))}`
|
|
21349
22050
|
};
|
|
21350
22051
|
await this.#requireWorkspace();
|
|
21351
|
-
const parsed = CodeMutationResultSchema.safeParse(await this.#request("/checkpoints", body));
|
|
22052
|
+
const parsed = CodeMutationResultSchema.safeParse(this.#options.storage ? await this.#options.storage.checkpoint(body) : await this.#request("/checkpoints", body));
|
|
21352
22053
|
if (!parsed.success)
|
|
21353
22054
|
throw invalidResponse();
|
|
21354
22055
|
const verified = await this.#snapshot(parsed.data.status);
|
|
@@ -21383,13 +22084,13 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21383
22084
|
if (!sameHashes(current.hashes, binding.baseline))
|
|
21384
22085
|
throw new CodeSyncError("code_sync_dirty", "Local files have unuploaded changes. Upload first, or recover cloud code into a new folder.");
|
|
21385
22086
|
await applySnapshot(this.#root, snapshot.files, binding.baseline, {
|
|
21386
|
-
backupRoot: path2.join(this.#stateRoot, "download-backups",
|
|
22087
|
+
backupRoot: path2.join(this.#stateRoot, "download-backups", randomUUID2()),
|
|
21387
22088
|
beforeWrite: async () => {
|
|
21388
22089
|
binding.interrupted_download = true;
|
|
21389
22090
|
await this.#save(binding);
|
|
21390
22091
|
},
|
|
21391
22092
|
baselineExecutables: new Set(binding.executable_paths ?? [])
|
|
21392
|
-
});
|
|
22093
|
+
}, this.#limits);
|
|
21393
22094
|
const verified = await this.#inventory(new Set(snapshot.files.filter((file2) => file2.executable).map((file2) => file2.path)));
|
|
21394
22095
|
if (!sameHashes(verified.hashes, nextHashes))
|
|
21395
22096
|
throw new CodeSyncError("code_sync_conflict", "Local files changed during download. Check the workspace before retrying.");
|
|
@@ -21427,14 +22128,28 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21427
22128
|
const recoveryStem = [...path2.basename(this.#root)];
|
|
21428
22129
|
while (Buffer.byteLength(recoveryStem.join("")) > 180)
|
|
21429
22130
|
recoveryStem.pop();
|
|
21430
|
-
const recoveryName = `${recoveryStem.join("")}-recovered-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/gu, "").slice(0, 14)}-${
|
|
22131
|
+
const recoveryName = `${recoveryStem.join("")}-recovered-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/gu, "").slice(0, 14)}-${randomUUID2().slice(0, 8)}`;
|
|
21431
22132
|
const recoveryPath = path2.join(path2.dirname(this.#root), recoveryName);
|
|
21432
22133
|
await mkdir2(recoveryPath, { mode: 448 });
|
|
21433
|
-
await applySnapshot(recoveryPath, snapshot.files, {});
|
|
22134
|
+
await applySnapshot(recoveryPath, snapshot.files, {}, void 0, this.#limits);
|
|
21434
22135
|
const restored = new _ProjectCodeSync({
|
|
21435
22136
|
...this.#options,
|
|
21436
22137
|
workspacePath: recoveryPath,
|
|
21437
|
-
stateRoot: path2.join(path2.dirname(this.#stateRoot), digest(recoveryPath))
|
|
22138
|
+
...this.#options.stateRoot ? { stateRoot: path2.join(path2.dirname(this.#options.stateRoot), digest(recoveryPath)) } : {},
|
|
22139
|
+
// The transport has private mutable Git state; recovery only seeds the
|
|
22140
|
+
// baseline and does not need another transport initialization here.
|
|
22141
|
+
...this.#options.storage ? { storage: {
|
|
22142
|
+
...this.#options.storage,
|
|
22143
|
+
key: this.#options.storage.key,
|
|
22144
|
+
limits: this.#limits,
|
|
22145
|
+
metadata: this.#options.storage.metadata,
|
|
22146
|
+
initialize: async () => {
|
|
22147
|
+
},
|
|
22148
|
+
status: () => this.#cloudStatus(),
|
|
22149
|
+
snapshot: (status) => this.#snapshot(status),
|
|
22150
|
+
checkpoint: (input2) => this.#options.storage.checkpoint(input2),
|
|
22151
|
+
update: () => this.#options.storage.update()
|
|
22152
|
+
} } : {}
|
|
21438
22153
|
});
|
|
21439
22154
|
await restored.initialize();
|
|
21440
22155
|
await restored.#locked(async () => {
|
|
@@ -21460,7 +22175,18 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21460
22175
|
await this.initialize();
|
|
21461
22176
|
return this.#locked(async () => {
|
|
21462
22177
|
const binding = await this.#readBinding();
|
|
21463
|
-
|
|
22178
|
+
if (!enabled) {
|
|
22179
|
+
binding.automatic_upload = false;
|
|
22180
|
+
await this.#save(binding);
|
|
22181
|
+
}
|
|
22182
|
+
let cloud;
|
|
22183
|
+
try {
|
|
22184
|
+
cloud = await this.#cloudStatus();
|
|
22185
|
+
} catch (error62) {
|
|
22186
|
+
if (enabled)
|
|
22187
|
+
throw error62;
|
|
22188
|
+
return { ...this.#status(binding, { repository: { enabled: false, main_commit: null }, branches: [], own_branch_id: null }, { files: [], hashes: binding.baseline, excluded: 0 }), local_status_unknown: true };
|
|
22189
|
+
}
|
|
21464
22190
|
if (enabled)
|
|
21465
22191
|
assertEnabled(cloud);
|
|
21466
22192
|
binding.automatic_upload = enabled;
|
|
@@ -21505,7 +22231,12 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21505
22231
|
return this.#upload(inventory.hashes);
|
|
21506
22232
|
}
|
|
21507
22233
|
async execute(kind, { busy = false, operationId } = {}) {
|
|
21508
|
-
if (
|
|
22234
|
+
if (kind.startsWith("github_") !== (this.#options.storage?.metadata.provider === "github")) {
|
|
22235
|
+
throw new CodeSyncError("code_sync_unsupported", "The requested action belongs to a different code provider.");
|
|
22236
|
+
}
|
|
22237
|
+
if (kind.startsWith("github_"))
|
|
22238
|
+
kind = kind.slice(7);
|
|
22239
|
+
if (busy && ["code_upload", "code_download", "code_recover", "code_auto_upload_enable", "code_update"].includes(kind)) {
|
|
21509
22240
|
throw new CodeSyncError("code_sync_busy", "An Agent is working in this workspace. Wait until it finishes before syncing code.");
|
|
21510
22241
|
}
|
|
21511
22242
|
switch (kind) {
|
|
@@ -21521,11 +22252,26 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21521
22252
|
return this.setAutomaticUpload(true);
|
|
21522
22253
|
case "code_auto_upload_disable":
|
|
21523
22254
|
return this.setAutomaticUpload(false);
|
|
22255
|
+
case "code_update": {
|
|
22256
|
+
await this.initialize();
|
|
22257
|
+
if (!this.#options.storage)
|
|
22258
|
+
throw new CodeSyncError("code_sync_unsupported", "Use the cloud review controls to update this branch.");
|
|
22259
|
+
return this.#locked(async () => {
|
|
22260
|
+
const binding = await this.#readBinding();
|
|
22261
|
+
const inventory = await this.#inventory();
|
|
22262
|
+
if (!sameHashes(binding.baseline, inventory.hashes)) {
|
|
22263
|
+
throw new CodeSyncError("code_sync_dirty", "Local source files have unuploaded changes. Upload them or recover the remote version before updating your GitHub branch.");
|
|
22264
|
+
}
|
|
22265
|
+
return this.#status(binding, await this.#options.storage.update(), inventory);
|
|
22266
|
+
});
|
|
22267
|
+
}
|
|
21524
22268
|
default:
|
|
21525
22269
|
throw new CodeSyncError("code_sync_unsupported", "Unsupported code sync action.");
|
|
21526
22270
|
}
|
|
21527
22271
|
}
|
|
21528
22272
|
async #cloudStatus() {
|
|
22273
|
+
if (this.#options.storage)
|
|
22274
|
+
return this.#options.storage.status();
|
|
21529
22275
|
const parsed = CodeStatusSchema.safeParse(await this.#request(""));
|
|
21530
22276
|
if (!parsed.success)
|
|
21531
22277
|
throw invalidResponse();
|
|
@@ -21534,6 +22280,8 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21534
22280
|
async #snapshot(cloud) {
|
|
21535
22281
|
if (!cloud.repository.main_commit && !cloud.own_branch_id)
|
|
21536
22282
|
throw new CodeSyncError("code_sync_empty", "No code has been uploaded yet.");
|
|
22283
|
+
if (this.#options.storage)
|
|
22284
|
+
return this.#options.storage.snapshot(cloud);
|
|
21537
22285
|
const parsed = CodeSnapshotResultSchema.safeParse(await this.#request(`/snapshot?branch_id=${encodeURIComponent(cloud.own_branch_id ?? "main")}`));
|
|
21538
22286
|
if (!parsed.success)
|
|
21539
22287
|
throw invalidResponse();
|
|
@@ -21542,6 +22290,7 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21542
22290
|
#status(binding, cloud, inventory) {
|
|
21543
22291
|
const cloudCommit = cloud.branches.find((branch) => branch.id === cloud.own_branch_id)?.head_commit ?? cloud.repository.main_commit;
|
|
21544
22292
|
return {
|
|
22293
|
+
...this.#options.storage?.metadata,
|
|
21545
22294
|
enabled: cloud.repository.enabled,
|
|
21546
22295
|
automatic_upload: binding.automatic_upload,
|
|
21547
22296
|
local_changes: changedCount(binding.baseline, inventory.hashes),
|
|
@@ -21609,7 +22358,7 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21609
22358
|
async #readBinding() {
|
|
21610
22359
|
const file2 = path2.join(this.#stateRoot, "binding.json");
|
|
21611
22360
|
const info = await lstat2(file2);
|
|
21612
|
-
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.size > MAX_BINDING_BYTES)
|
|
22361
|
+
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1 || info.size > MAX_BINDING_BYTES * (this.#options.storage ? 10 : 1))
|
|
21613
22362
|
throw new CodeSyncError("code_sync_binding", "Private code sync state is not a regular bounded file.");
|
|
21614
22363
|
let binding;
|
|
21615
22364
|
try {
|
|
@@ -21624,13 +22373,13 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21624
22373
|
}
|
|
21625
22374
|
if (binding.interrupted_download !== void 0 && typeof binding.interrupted_download !== "boolean")
|
|
21626
22375
|
throw new CodeSyncError("code_sync_binding", "Private download recovery state is invalid.");
|
|
21627
|
-
if (binding.executable_paths !== void 0 && (!Array.isArray(binding.executable_paths) || binding.executable_paths.length >
|
|
22376
|
+
if (binding.executable_paths !== void 0 && (!Array.isArray(binding.executable_paths) || binding.executable_paths.length > this.#limits.maxFiles || binding.executable_paths.some((name2) => typeof name2 !== "string" || !isCodeSyncPathAllowed(name2))))
|
|
21628
22377
|
throw new CodeSyncError("code_sync_binding", "Private code file-mode state is invalid.");
|
|
21629
22378
|
const baseline = Object.entries(binding.baseline);
|
|
21630
|
-
if (baseline.length >
|
|
22379
|
+
if (baseline.length > this.#limits.maxFiles)
|
|
21631
22380
|
throw new CodeSyncError("code_sync_binding", "Private code sync baseline exceeds the file limit.");
|
|
21632
|
-
for (const [name2,
|
|
21633
|
-
if (!isCodeSyncPathAllowed(name2) || typeof
|
|
22381
|
+
for (const [name2, hash3] of baseline) {
|
|
22382
|
+
if (!isCodeSyncPathAllowed(name2) || typeof hash3 !== "string" || !/^[a-f0-9]{64}$/u.test(hash3))
|
|
21634
22383
|
throw new CodeSyncError("code_sync_binding", "Private code sync baseline is invalid.");
|
|
21635
22384
|
}
|
|
21636
22385
|
return binding;
|
|
@@ -21663,19 +22412,23 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21663
22412
|
const nativeExecutables = /* @__PURE__ */ new Set();
|
|
21664
22413
|
let candidates;
|
|
21665
22414
|
let nativeRoot = "";
|
|
22415
|
+
const inventoryOutputLimit = this.#options.storage ? this.#limits.maxFiles * 2100 : 2 * 1024 * 1024;
|
|
21666
22416
|
try {
|
|
21667
22417
|
nativeRoot = (await git(["rev-parse", "--show-toplevel"], this.#root)).trim();
|
|
21668
22418
|
} catch {
|
|
21669
22419
|
}
|
|
21670
22420
|
if (nativeRoot && await realpath(nativeRoot) === this.#root) {
|
|
21671
|
-
const trackedEntries = (await git(["ls-files", "--stage", "-z"], this.#root)).split("\0").filter(Boolean);
|
|
22421
|
+
const trackedEntries = (await git(["ls-files", "--stage", "-z"], this.#root, inventoryOutputLimit)).split("\0").filter(Boolean);
|
|
21672
22422
|
tracked = new Set(trackedEntries.map((entry) => entry.slice(entry.indexOf(" ") + 1)));
|
|
22423
|
+
if (this.#options.storage?.metadata.provider === "github" && trackedEntries.some((entry) => !/^(?:100644|100755) /u.test(entry))) {
|
|
22424
|
+
throw new CodeSyncError("code_sync_unsupported", "Tracked links or submodules cannot be synchronized to GitHub. Remove them from the native Git index or use native Git.");
|
|
22425
|
+
}
|
|
21673
22426
|
for (const entry of trackedEntries)
|
|
21674
22427
|
if (entry.startsWith("100755 "))
|
|
21675
22428
|
nativeExecutables.add(entry.slice(entry.indexOf(" ") + 1));
|
|
21676
|
-
candidates = [...tracked, ...(await git(["ls-files", "--others", "--exclude-standard", "-z"], this.#root)).split("\0").filter(Boolean)];
|
|
22429
|
+
candidates = [...tracked, ...(await git(["ls-files", "--others", "--exclude-standard", "-z"], this.#root, inventoryOutputLimit)).split("\0").filter(Boolean)];
|
|
21677
22430
|
} else {
|
|
21678
|
-
candidates = (await git([`--git-dir=${path2.join(this.#stateRoot, "inventory.git")}`, `--work-tree=${this.#root}`, "ls-files", "--others", "--exclude-standard", "-z"], this.#root)).split("\0").filter(Boolean);
|
|
22431
|
+
candidates = (await git([`--git-dir=${path2.join(this.#stateRoot, "inventory.git")}`, `--work-tree=${this.#root}`, "ls-files", "--others", "--exclude-standard", "-z"], this.#root, inventoryOutputLimit)).split("\0").filter(Boolean);
|
|
21679
22432
|
}
|
|
21680
22433
|
const binding = await this.#readBinding();
|
|
21681
22434
|
const executablePaths = executableHints ?? new Set(binding.executable_paths ?? []);
|
|
@@ -21688,6 +22441,9 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21688
22441
|
let totalBytes = 0;
|
|
21689
22442
|
for (const name2 of [...new Set(candidates)].sort()) {
|
|
21690
22443
|
if (!isCodeSyncPathAllowed(name2) || BUILD_PATH.test(name2)) {
|
|
22444
|
+
if (this.#options.storage?.metadata.provider === "github" && tracked.has(name2)) {
|
|
22445
|
+
throw new CodeSyncError("code_sync_unsupported", "A tracked private, unsafe or generated path cannot be synchronized to GitHub. Remove it from the native Git index or use native Git.");
|
|
22446
|
+
}
|
|
21691
22447
|
if (tracked.has(name2) && !BUILD_PATH.test(name2))
|
|
21692
22448
|
throw new CodeSyncError("code_sync_secret", "A tracked private or unsafe file is excluded from code sync. Remove it from version control before uploading.");
|
|
21693
22449
|
excluded += 1;
|
|
@@ -21695,6 +22451,8 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21695
22451
|
}
|
|
21696
22452
|
const target = path2.join(this.#root, ...name2.split("/"));
|
|
21697
22453
|
if (!await safeAncestors(this.#root, name2)) {
|
|
22454
|
+
if (this.#options.storage?.metadata.provider === "github" && tracked.has(name2))
|
|
22455
|
+
throw new CodeSyncError("code_sync_unsupported", "A tracked path crosses a link or unsafe directory. Use native Git after reviewing it.");
|
|
21698
22456
|
excluded += 1;
|
|
21699
22457
|
continue;
|
|
21700
22458
|
}
|
|
@@ -21707,26 +22465,28 @@ var ProjectCodeSync = class _ProjectCodeSync {
|
|
|
21707
22465
|
throw error62;
|
|
21708
22466
|
}
|
|
21709
22467
|
if (!info.isFile() || info.isSymbolicLink() || info.nlink !== 1) {
|
|
22468
|
+
if (this.#options.storage?.metadata.provider === "github" && tracked.has(name2))
|
|
22469
|
+
throw new CodeSyncError("code_sync_unsupported", "A tracked link or non-regular file cannot be synchronized to GitHub. Use native Git after reviewing it.");
|
|
21710
22470
|
excluded += 1;
|
|
21711
22471
|
continue;
|
|
21712
22472
|
}
|
|
21713
|
-
if (info.size >
|
|
21714
|
-
throw new CodeSyncError("code_sync_limit", "A source file exceeds the
|
|
21715
|
-
const source = await readBoundedSource(target);
|
|
21716
|
-
const
|
|
21717
|
-
if (containsCodeSyncSecret(
|
|
22473
|
+
if (info.size > this.#limits.maxFileBytes)
|
|
22474
|
+
throw new CodeSyncError("code_sync_limit", "A source file exceeds the selected provider's file limit.");
|
|
22475
|
+
const source = await readBoundedSource(target, this.#limits.maxFileBytes);
|
|
22476
|
+
const bytes2 = source.bytes;
|
|
22477
|
+
if (containsCodeSyncSecret(bytes2.toString("utf8")))
|
|
21718
22478
|
throw new CodeSyncError("code_sync_secret", "A source file contains a recognizable credential or private key. Remove it before uploading.");
|
|
21719
|
-
totalBytes +=
|
|
21720
|
-
if (totalBytes >
|
|
21721
|
-
throw new CodeSyncError("code_sync_limit", "
|
|
21722
|
-
files.push({ path: name2, content_base64:
|
|
22479
|
+
totalBytes += bytes2.byteLength;
|
|
22480
|
+
if (totalBytes > this.#limits.maxSnapshotBytes || files.length >= this.#limits.maxFiles)
|
|
22481
|
+
throw new CodeSyncError("code_sync_limit", "Source files exceed the selected provider's checkpoint limit.");
|
|
22482
|
+
files.push({ path: name2, content_base64: bytes2.toString("base64"), executable: process.platform === "win32" ? executablePaths.has(name2) || nativeExecutables.has(name2) : source.executable });
|
|
21723
22483
|
}
|
|
21724
|
-
if (!
|
|
22484
|
+
if (!createCodeFilesSchema(this.#limits).safeParse(files).success)
|
|
21725
22485
|
throw new CodeSyncError("code_sync_unsafe_path", "Source files contain non-portable or conflicting paths.");
|
|
21726
22486
|
return { files, hashes: fileHashes(files), excluded };
|
|
21727
22487
|
}
|
|
21728
22488
|
async #locked(operation) {
|
|
21729
|
-
const lockPath = path2.join(this.#
|
|
22489
|
+
const lockPath = path2.join(this.#lockRoot, "operation.lock");
|
|
21730
22490
|
let handle;
|
|
21731
22491
|
try {
|
|
21732
22492
|
handle = await open3(lockPath, "wx", 384);
|
|
@@ -21798,14 +22558,14 @@ function isWithin(root, target) {
|
|
|
21798
22558
|
const relative = path2.relative(root, target);
|
|
21799
22559
|
return !relative || !relative.startsWith(`..${path2.sep}`) && relative !== ".." && !path2.isAbsolute(relative);
|
|
21800
22560
|
}
|
|
21801
|
-
async function git(args, cwd) {
|
|
22561
|
+
async function git(args, cwd, maxBuffer = 2 * 1024 * 1024) {
|
|
21802
22562
|
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_") && !key.startsWith("GATHERTHREAD_")));
|
|
21803
22563
|
try {
|
|
21804
22564
|
const result = await execFileAsync("git", ["-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null", ...args], {
|
|
21805
22565
|
cwd,
|
|
21806
22566
|
env: { ...env, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", GIT_OPTIONAL_LOCKS: "0", GIT_TERMINAL_PROMPT: "0" },
|
|
21807
22567
|
timeout: 15e3,
|
|
21808
|
-
maxBuffer
|
|
22568
|
+
maxBuffer,
|
|
21809
22569
|
encoding: "utf8",
|
|
21810
22570
|
windowsHide: true
|
|
21811
22571
|
});
|
|
@@ -21815,7 +22575,7 @@ async function git(args, cwd) {
|
|
|
21815
22575
|
}
|
|
21816
22576
|
}
|
|
21817
22577
|
async function writePrivate(filename, content) {
|
|
21818
|
-
const temporary = `${filename}.${
|
|
22578
|
+
const temporary = `${filename}.${randomUUID2()}.tmp`;
|
|
21819
22579
|
const handle = await open3(temporary, "wx", 384);
|
|
21820
22580
|
try {
|
|
21821
22581
|
await handle.writeFile(content);
|
|
@@ -21844,7 +22604,7 @@ async function safeAncestors(root, relative, create = false) {
|
|
|
21844
22604
|
}
|
|
21845
22605
|
return true;
|
|
21846
22606
|
}
|
|
21847
|
-
async function fileHash(root, name2, executableHint) {
|
|
22607
|
+
async function fileHash(root, name2, executableHint, maxBytes = CODE_SYNC_MAX_FILE_BYTES) {
|
|
21848
22608
|
if (!await safeAncestors(root, name2))
|
|
21849
22609
|
throw new CodeSyncError("code_sync_unsafe_path", "A destination directory is a symbolic link or not a directory.");
|
|
21850
22610
|
const target = path2.join(root, ...name2.split("/"));
|
|
@@ -21856,17 +22616,17 @@ async function fileHash(root, name2, executableHint) {
|
|
|
21856
22616
|
return;
|
|
21857
22617
|
throw error62;
|
|
21858
22618
|
}
|
|
21859
|
-
if (!info.isFile() || info.isSymbolicLink() || info.size >
|
|
22619
|
+
if (!info.isFile() || info.isSymbolicLink() || info.size > maxBytes)
|
|
21860
22620
|
throw new CodeSyncError("code_sync_unsafe_path", "A destination is not a bounded regular source file.");
|
|
21861
|
-
const source = await readBoundedSource(target);
|
|
22621
|
+
const source = await readBoundedSource(target, maxBytes);
|
|
21862
22622
|
const executable = process.platform === "win32" && executableHint !== void 0 ? executableHint : source.executable;
|
|
21863
22623
|
return digest(`${executable ? "1" : "0"}\0${source.bytes.toString("base64")}`);
|
|
21864
22624
|
}
|
|
21865
|
-
async function readBoundedSource(filename) {
|
|
22625
|
+
async function readBoundedSource(filename, maxBytes = CODE_SYNC_MAX_FILE_BYTES) {
|
|
21866
22626
|
const handle = await open3(filename, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
|
|
21867
22627
|
try {
|
|
21868
22628
|
const before = await handle.stat();
|
|
21869
|
-
if (!before.isFile() || before.nlink !== 1 || before.size >
|
|
22629
|
+
if (!before.isFile() || before.nlink !== 1 || before.size > maxBytes)
|
|
21870
22630
|
throw new CodeSyncError("code_sync_unsafe_path", "A source path is not a bounded regular file.");
|
|
21871
22631
|
const buffer = Buffer.alloc(before.size + 1);
|
|
21872
22632
|
const result = await handle.read(buffer, 0, buffer.length, 0);
|
|
@@ -21878,18 +22638,18 @@ async function readBoundedSource(filename) {
|
|
|
21878
22638
|
await handle.close();
|
|
21879
22639
|
}
|
|
21880
22640
|
}
|
|
21881
|
-
async function applySnapshot(root, files, baseline, options) {
|
|
21882
|
-
if (!
|
|
22641
|
+
async function applySnapshot(root, files, baseline, options, limits = CLOUD_CODE_SYNC_LIMITS) {
|
|
22642
|
+
if (!createCodeFilesSchema(limits).safeParse(files).success)
|
|
21883
22643
|
throw new CodeSyncError("code_sync_unsafe_path", "Cloud code contains unsafe paths.");
|
|
21884
22644
|
const names = /* @__PURE__ */ new Set([...Object.keys(baseline), ...files.map((file2) => file2.path)]);
|
|
21885
22645
|
for (const name2 of names) {
|
|
21886
|
-
if (await fileHash(root, name2, options?.baselineExecutables?.has(name2)) !== baseline[name2])
|
|
22646
|
+
if (await fileHash(root, name2, options?.baselineExecutables?.has(name2), limits.maxFileBytes) !== baseline[name2])
|
|
21887
22647
|
throw new CodeSyncError("code_sync_dirty", "Cloud code would overwrite local or ignored files. Recover into a new folder instead.");
|
|
21888
22648
|
}
|
|
21889
22649
|
if (options) {
|
|
21890
22650
|
await mkdir2(options.backupRoot, { recursive: true, mode: 448 });
|
|
21891
22651
|
for (const name2 of Object.keys(baseline)) {
|
|
21892
|
-
const source = await readBoundedSource(path2.join(root, ...name2.split("/")));
|
|
22652
|
+
const source = await readBoundedSource(path2.join(root, ...name2.split("/")), limits.maxFileBytes);
|
|
21893
22653
|
if (process.platform === "win32" && options.baselineExecutables)
|
|
21894
22654
|
source.executable = options.baselineExecutables.has(name2);
|
|
21895
22655
|
if (digest(`${source.executable ? "1" : "0"}\0${source.bytes.toString("base64")}`) !== baseline[name2])
|
|
@@ -21908,10 +22668,10 @@ async function applySnapshot(root, files, baseline, options) {
|
|
|
21908
22668
|
}
|
|
21909
22669
|
const targetNames = new Set(files.map((file2) => file2.path));
|
|
21910
22670
|
for (const file2 of files) {
|
|
21911
|
-
if (!await safeAncestors(root, file2.path, true) || await fileHash(root, file2.path, options?.baselineExecutables?.has(file2.path)) !== baseline[file2.path])
|
|
22671
|
+
if (!await safeAncestors(root, file2.path, true) || await fileHash(root, file2.path, options?.baselineExecutables?.has(file2.path), limits.maxFileBytes) !== baseline[file2.path])
|
|
21912
22672
|
throw new CodeSyncError("code_sync_busy", "Local files changed during download. No further files were applied.");
|
|
21913
22673
|
const filename = path2.join(root, ...file2.path.split("/"));
|
|
21914
|
-
const temporary = path2.join(path2.dirname(filename), `.gatherthread-${
|
|
22674
|
+
const temporary = path2.join(path2.dirname(filename), `.gatherthread-${randomUUID2()}.tmp`);
|
|
21915
22675
|
const handle = await open3(temporary, "wx", file2.executable ? 448 : 384);
|
|
21916
22676
|
try {
|
|
21917
22677
|
await handle.writeFile(Buffer.from(file2.content_base64, "base64"));
|
|
@@ -21919,7 +22679,7 @@ async function applySnapshot(root, files, baseline, options) {
|
|
|
21919
22679
|
} finally {
|
|
21920
22680
|
await handle.close();
|
|
21921
22681
|
}
|
|
21922
|
-
if (!await safeAncestors(root, file2.path) || await fileHash(root, file2.path, options?.baselineExecutables?.has(file2.path)) !== baseline[file2.path]) {
|
|
22682
|
+
if (!await safeAncestors(root, file2.path) || await fileHash(root, file2.path, options?.baselineExecutables?.has(file2.path), limits.maxFileBytes) !== baseline[file2.path]) {
|
|
21923
22683
|
await unlink(temporary);
|
|
21924
22684
|
throw new CodeSyncError("code_sync_busy", "Source files changed while preparing a download. Original files were backed up; no further files were applied.");
|
|
21925
22685
|
}
|
|
@@ -21927,55 +22687,442 @@ async function applySnapshot(root, files, baseline, options) {
|
|
|
21927
22687
|
}
|
|
21928
22688
|
for (const name2 of Object.keys(baseline)) {
|
|
21929
22689
|
if (!targetNames.has(name2)) {
|
|
21930
|
-
if (await fileHash(root, name2, options?.baselineExecutables?.has(name2)) !== baseline[name2])
|
|
22690
|
+
if (await fileHash(root, name2, options?.baselineExecutables?.has(name2), limits.maxFileBytes) !== baseline[name2])
|
|
21931
22691
|
throw new CodeSyncError("code_sync_busy", "Local files changed during download. No further files were applied.");
|
|
21932
22692
|
await unlink(path2.join(root, ...name2.split("/")));
|
|
21933
22693
|
}
|
|
21934
22694
|
}
|
|
21935
22695
|
}
|
|
21936
22696
|
|
|
22697
|
+
// ../bridge/dist/src/github-code-sync.js
|
|
22698
|
+
import { createHash as createHash3, randomUUID as randomUUID3 } from "node:crypto";
|
|
22699
|
+
import { spawn } from "node:child_process";
|
|
22700
|
+
import { constants as constants2 } from "node:fs";
|
|
22701
|
+
import { lstat as lstat3, mkdir as mkdir3, open as open4, unlink as unlink2 } from "node:fs/promises";
|
|
22702
|
+
import path3 from "node:path";
|
|
22703
|
+
var hash2 = (text) => createHash3("sha256").update(text).digest("hex");
|
|
22704
|
+
var Commit2 = /^[a-f0-9]{40}$/u;
|
|
22705
|
+
var filesSchema = createCodeFilesSchema(GITHUB_CODE_SYNC_LIMITS);
|
|
22706
|
+
var generatedPath = /(?:^|\/)(?:node_modules|dist|build|coverage|\.next|\.cache|\.turbo|\.venv|venv|__pycache__)(?:\/|$)/u;
|
|
22707
|
+
function createGitHubCodeSync(options, runner = runGitHubGit) {
|
|
22708
|
+
return new ProjectCodeSync({ ...options, storage: new GitHubCodeStorage(options, runner) });
|
|
22709
|
+
}
|
|
22710
|
+
var GitHubCodeStorage = class {
|
|
22711
|
+
limits = GITHUB_CODE_SYNC_LIMITS;
|
|
22712
|
+
key;
|
|
22713
|
+
metadata;
|
|
22714
|
+
#options;
|
|
22715
|
+
#run;
|
|
22716
|
+
#remote;
|
|
22717
|
+
#branchId;
|
|
22718
|
+
#directory = "";
|
|
22719
|
+
#revision;
|
|
22720
|
+
constructor(options, runner) {
|
|
22721
|
+
if (!GitHubConnectionSchema.safeParse({ repository: options.repository, base_branch: options.baseBranch, revision: options.revision, enabled: true }).success) {
|
|
22722
|
+
throw new CodeSyncError("code_sync_binding", "GitHub requires an owner/repository and a valid base branch.");
|
|
22723
|
+
}
|
|
22724
|
+
const branch = `gatherthread/${hash2(options.projectId).slice(0, 24)}/${hash2(options.actorId).slice(0, 24)}`;
|
|
22725
|
+
if (options.baseBranch.startsWith("gatherthread/"))
|
|
22726
|
+
throw new CodeSyncError("code_sync_binding", "Choose a base branch outside GatherThread personal branches.");
|
|
22727
|
+
this.#options = options;
|
|
22728
|
+
this.#run = runner;
|
|
22729
|
+
this.#revision = options.revision;
|
|
22730
|
+
this.#remote = `https://github.com/${options.repository}.git`;
|
|
22731
|
+
this.#branchId = `branch-${hash2(branch).slice(0, 24)}`;
|
|
22732
|
+
this.key = JSON.stringify(["github", options.apiUrl.replace(/\/$/u, "").replace(/\/v1$/u, ""), options.projectId, options.actorId, options.repository.toLowerCase(), options.baseBranch, options.revision]);
|
|
22733
|
+
this.metadata = { provider: "github", repository: options.repository, branch, base_branch: options.baseBranch };
|
|
22734
|
+
}
|
|
22735
|
+
async initialize(privateDirectory) {
|
|
22736
|
+
this.#directory = path3.join(privateDirectory, "github.git");
|
|
22737
|
+
try {
|
|
22738
|
+
if ((await lstat3(this.#directory)).isSymbolicLink())
|
|
22739
|
+
throw new CodeSyncError("code_sync_binding", "Private GitHub state cannot be a symbolic link.");
|
|
22740
|
+
} catch (error62) {
|
|
22741
|
+
if (error62.code !== "ENOENT")
|
|
22742
|
+
throw error62;
|
|
22743
|
+
}
|
|
22744
|
+
await mkdir3(this.#directory, { recursive: true, mode: 448 });
|
|
22745
|
+
await this.#git(["init", "--bare", "--quiet", "."]);
|
|
22746
|
+
const info = path3.join(this.#directory, "info");
|
|
22747
|
+
await mkdir3(info, { recursive: true, mode: 448 });
|
|
22748
|
+
if ((await lstat3(info)).isSymbolicLink())
|
|
22749
|
+
throw new CodeSyncError("code_sync_binding", "Private GitHub attributes directory is unsafe.");
|
|
22750
|
+
const attributes = await open4(path3.join(info, "attributes"), constants2.O_WRONLY | constants2.O_CREAT | (constants2.O_NOFOLLOW ?? 0), 384);
|
|
22751
|
+
try {
|
|
22752
|
+
if (!(await attributes.stat()).isFile() || (await attributes.stat()).nlink !== 1)
|
|
22753
|
+
throw new CodeSyncError("code_sync_binding", "Private GitHub attributes must be a regular file.");
|
|
22754
|
+
await attributes.truncate(0);
|
|
22755
|
+
await attributes.writeFile("* conflict-marker-size=7\n");
|
|
22756
|
+
} finally {
|
|
22757
|
+
await attributes.close();
|
|
22758
|
+
}
|
|
22759
|
+
}
|
|
22760
|
+
async #authorize() {
|
|
22761
|
+
const url2 = new URL(this.#options.apiUrl);
|
|
22762
|
+
url2.pathname = url2.pathname.replace(/\/$/u, "").replace(/\/v1$/u, "") + `/v1/projects/${encodeURIComponent(this.#options.projectId)}/github`;
|
|
22763
|
+
let response;
|
|
22764
|
+
try {
|
|
22765
|
+
response = await (this.#options.fetch ?? fetch)(url2.toString(), {
|
|
22766
|
+
headers: { authorization: `Bearer ${this.#options.token}` },
|
|
22767
|
+
redirect: "error",
|
|
22768
|
+
signal: AbortSignal.timeout(15e3)
|
|
22769
|
+
});
|
|
22770
|
+
} catch {
|
|
22771
|
+
throw new CodeSyncError("code_sync_unavailable", "GatherThread could not verify current GitHub project access.");
|
|
22772
|
+
}
|
|
22773
|
+
if (!response.ok) {
|
|
22774
|
+
await response.body?.cancel();
|
|
22775
|
+
throw new CodeSyncError("code_sync_forbidden", "Current GatherThread project access is required for GitHub synchronization.");
|
|
22776
|
+
}
|
|
22777
|
+
if (!response.body)
|
|
22778
|
+
throw new CodeSyncError("code_sync_unavailable", "Invalid GitHub configuration response.");
|
|
22779
|
+
const reader = response.body.getReader();
|
|
22780
|
+
const chunks = [];
|
|
22781
|
+
let size = 0;
|
|
22782
|
+
for (; ; ) {
|
|
22783
|
+
const part = await reader.read();
|
|
22784
|
+
if (part.done)
|
|
22785
|
+
break;
|
|
22786
|
+
size += part.value.length;
|
|
22787
|
+
if (size > 16384) {
|
|
22788
|
+
await reader.cancel();
|
|
22789
|
+
throw new CodeSyncError("code_sync_limit", "GitHub configuration response is too large.");
|
|
22790
|
+
}
|
|
22791
|
+
chunks.push(part.value);
|
|
22792
|
+
}
|
|
22793
|
+
let data;
|
|
22794
|
+
try {
|
|
22795
|
+
data = GitHubProjectStatusSchema.parse(JSON.parse(Buffer.concat(chunks).toString("utf8")).data);
|
|
22796
|
+
} catch {
|
|
22797
|
+
throw new CodeSyncError("code_sync_unavailable", "Invalid GitHub configuration response.");
|
|
22798
|
+
}
|
|
22799
|
+
const connection = data.connection;
|
|
22800
|
+
if (!data.can_write || data.branch !== this.metadata.branch || !connection || connection.repository.toLowerCase() !== this.#options.repository.toLowerCase() || connection.base_branch !== this.#options.baseBranch) {
|
|
22801
|
+
throw new CodeSyncError("code_sync_binding", "GitHub target or project permissions changed. Review the connection and authorize it locally again.");
|
|
22802
|
+
}
|
|
22803
|
+
if (!connection.enabled)
|
|
22804
|
+
return false;
|
|
22805
|
+
if (this.#revision !== connection.revision)
|
|
22806
|
+
throw new CodeSyncError("code_sync_binding", "GitHub configuration changed. Reconnect and authorize the current target locally again.");
|
|
22807
|
+
return true;
|
|
22808
|
+
}
|
|
22809
|
+
async #enabled() {
|
|
22810
|
+
if (!await this.#authorize())
|
|
22811
|
+
throw new CodeSyncError("code_sync_disabled", "GitHub synchronization is paused for this project.");
|
|
22812
|
+
}
|
|
22813
|
+
async status() {
|
|
22814
|
+
if (!await this.#authorize())
|
|
22815
|
+
return { repository: { enabled: false, main_commit: null }, branches: [], own_branch_id: null };
|
|
22816
|
+
const refs = (await this.#git(["ls-remote", "--refs", this.#remote, `refs/heads/${this.#options.baseBranch}`, `refs/heads/${this.metadata.branch}`])).toString("utf8");
|
|
22817
|
+
let main = null;
|
|
22818
|
+
let own2 = null;
|
|
22819
|
+
for (const line of refs.trim().split("\n")) {
|
|
22820
|
+
const [oid, ref] = line.split(" ");
|
|
22821
|
+
if (!oid || !Commit2.test(oid))
|
|
22822
|
+
continue;
|
|
22823
|
+
if (ref === `refs/heads/${this.#options.baseBranch}`)
|
|
22824
|
+
main = oid;
|
|
22825
|
+
if (ref === `refs/heads/${this.metadata.branch}`)
|
|
22826
|
+
own2 = oid;
|
|
22827
|
+
}
|
|
22828
|
+
return {
|
|
22829
|
+
repository: { enabled: true, main_commit: main },
|
|
22830
|
+
own_branch_id: own2 ? this.#branchId : null,
|
|
22831
|
+
branches: own2 ? [{ id: this.#branchId, name: "My GitHub branch", user_id: this.#options.actorId, head_commit: own2, review_status: "draft" }] : []
|
|
22832
|
+
};
|
|
22833
|
+
}
|
|
22834
|
+
#head(status) {
|
|
22835
|
+
return status.branches[0]?.head_commit ?? status.repository.main_commit;
|
|
22836
|
+
}
|
|
22837
|
+
async #fetch(status) {
|
|
22838
|
+
await this.#enabled();
|
|
22839
|
+
const refs = [];
|
|
22840
|
+
if (status.repository.main_commit)
|
|
22841
|
+
refs.push(`+refs/heads/${this.#options.baseBranch}:refs/gt/base`);
|
|
22842
|
+
if (status.own_branch_id)
|
|
22843
|
+
refs.push(`+refs/heads/${this.metadata.branch}:refs/gt/own`);
|
|
22844
|
+
if (refs.length)
|
|
22845
|
+
await this.#git(["fetch", "--quiet", "--no-tags", "--no-recurse-submodules", this.#remote, ...refs]);
|
|
22846
|
+
for (const [ref, expected] of [["refs/gt/base", status.repository.main_commit], ["refs/gt/own", status.branches[0]?.head_commit]]) {
|
|
22847
|
+
if (expected && (await this.#git(["rev-parse", "--verify", ref])).toString().trim() !== expected)
|
|
22848
|
+
throw conflict();
|
|
22849
|
+
}
|
|
22850
|
+
}
|
|
22851
|
+
async snapshot(status) {
|
|
22852
|
+
const commit = this.#head(status);
|
|
22853
|
+
if (!commit)
|
|
22854
|
+
throw new CodeSyncError("code_sync_empty", "No GitHub files have been uploaded yet.");
|
|
22855
|
+
await this.#fetch(status);
|
|
22856
|
+
return { commit, files: await this.#files(commit) };
|
|
22857
|
+
}
|
|
22858
|
+
async #files(commit) {
|
|
22859
|
+
if (!Commit2.test(commit))
|
|
22860
|
+
throw conflict();
|
|
22861
|
+
const listing = await this.#git(["ls-tree", "-r", "-z", "-l", commit]);
|
|
22862
|
+
if (!Buffer.from(listing.toString("utf8"), "utf8").equals(listing))
|
|
22863
|
+
throw new CodeSyncError("code_sync_unsafe_path", "The GitHub tree contains non-UTF-8 filenames. Use native Git for this repository.");
|
|
22864
|
+
const entries = listing.toString("utf8").split("\0").filter(Boolean).map((entry) => {
|
|
22865
|
+
const match = /^(100644|100755) blob ([a-f0-9]{40})\s+(\d+)\t(.+)$/u.exec(entry);
|
|
22866
|
+
if (!match)
|
|
22867
|
+
throw new CodeSyncError("code_sync_unsupported", "This GitHub tree contains unsupported links, submodules or paths. Use native Git for this repository.");
|
|
22868
|
+
return { mode: match[1], oid: match[2], size: Number(match[3]), path: match[4] };
|
|
22869
|
+
});
|
|
22870
|
+
if (entries.length > this.limits.maxFiles || entries.some((entry) => entry.size > this.limits.maxFileBytes) || entries.reduce((sum, entry) => sum + entry.size, 0) > this.limits.maxSnapshotBytes)
|
|
22871
|
+
throw new CodeSyncError("code_sync_limit", "GitHub source exceeds the supported checkpoint limit. Use native Git for larger trees.");
|
|
22872
|
+
const output2 = entries.length ? await this.#git(["cat-file", "--batch"], Buffer.from(entries.map((entry) => entry.oid).join("\n") + "\n")) : Buffer.alloc(0);
|
|
22873
|
+
let offset = 0;
|
|
22874
|
+
const files = [];
|
|
22875
|
+
for (const entry of entries) {
|
|
22876
|
+
const end = output2.indexOf(10, offset);
|
|
22877
|
+
if (end < 0 || output2.subarray(offset, end).toString() !== `${entry.oid} blob ${entry.size}`)
|
|
22878
|
+
throw conflict();
|
|
22879
|
+
const content = output2.subarray(end + 1, end + 1 + entry.size);
|
|
22880
|
+
offset = end + 2 + entry.size;
|
|
22881
|
+
if (content.length !== entry.size || output2[offset - 1] !== 10)
|
|
22882
|
+
throw conflict();
|
|
22883
|
+
if (generatedPath.test(entry.path) || containsCodeSyncSecret(content.toString("utf8")) || content.subarray(0, 80).toString().startsWith("version https://git-lfs.github.com/spec/v1")) {
|
|
22884
|
+
throw new CodeSyncError("code_sync_unsupported", "This GitHub tree contains excluded generated files, credentials or Git LFS pointers. Nothing was applied; use native Git or a source-only repository.");
|
|
22885
|
+
}
|
|
22886
|
+
files.push({ path: entry.path, content_base64: content.toString("base64"), executable: entry.mode === "100755" });
|
|
22887
|
+
}
|
|
22888
|
+
if (!filesSchema.safeParse(files).success)
|
|
22889
|
+
throw new CodeSyncError("code_sync_unsafe_path", "The GitHub tree contains non-portable or private paths. Nothing was applied.");
|
|
22890
|
+
return files;
|
|
22891
|
+
}
|
|
22892
|
+
async #tree(files) {
|
|
22893
|
+
if (!filesSchema.safeParse(files).success)
|
|
22894
|
+
throw new CodeSyncError("code_sync_limit", "Source tree exceeds GitHub checkpoint limits.");
|
|
22895
|
+
const index = path3.join(this.#directory, `index-${randomUUID3()}`);
|
|
22896
|
+
try {
|
|
22897
|
+
await this.#git(["read-tree", "--empty"], void 0, index);
|
|
22898
|
+
const records = [];
|
|
22899
|
+
for (const file2 of files) {
|
|
22900
|
+
const bytes2 = Buffer.from(file2.content_base64, "base64");
|
|
22901
|
+
if (containsCodeSyncSecret(bytes2.toString("utf8")))
|
|
22902
|
+
throw new CodeSyncError("code_sync_secret", "A recognizable credential was detected in source.");
|
|
22903
|
+
if (generatedPath.test(file2.path) || bytes2.subarray(0, 80).toString().startsWith("version https://git-lfs.github.com/spec/v1"))
|
|
22904
|
+
throw new CodeSyncError("code_sync_unsupported", "Use native Git for Git LFS or generated-file repositories.");
|
|
22905
|
+
const oid = (await this.#git(["hash-object", "-w", "--stdin", "--no-filters"], bytes2)).toString().trim();
|
|
22906
|
+
if (!Commit2.test(oid))
|
|
22907
|
+
throw conflict();
|
|
22908
|
+
records.push(`${file2.executable ? "100755" : "100644"} ${oid} ${file2.path}\0`);
|
|
22909
|
+
}
|
|
22910
|
+
await this.#git(["update-index", "-z", "--index-info"], Buffer.from(records.join("")), index);
|
|
22911
|
+
return (await this.#git(["write-tree"], void 0, index)).toString().trim();
|
|
22912
|
+
} finally {
|
|
22913
|
+
await unlink2(index).catch(() => {
|
|
22914
|
+
});
|
|
22915
|
+
}
|
|
22916
|
+
}
|
|
22917
|
+
async checkpoint(input2) {
|
|
22918
|
+
const status = await this.status();
|
|
22919
|
+
await this.#enabled();
|
|
22920
|
+
const current = this.#head(status);
|
|
22921
|
+
await this.#fetch(status);
|
|
22922
|
+
if (current)
|
|
22923
|
+
await this.#files(current);
|
|
22924
|
+
const tree = await this.#tree(input2.files);
|
|
22925
|
+
if (current && (await this.#git(["rev-parse", `${current}^{tree}`])).toString().trim() === tree)
|
|
22926
|
+
return { status, commit: current };
|
|
22927
|
+
if (input2.base_commit !== current)
|
|
22928
|
+
throw conflict();
|
|
22929
|
+
const commit = (await this.#git(["commit-tree", tree, ...current ? ["-p", current] : [], "-m", "GatherThread workspace checkpoint"])).toString().trim();
|
|
22930
|
+
await this.#push(commit, status);
|
|
22931
|
+
const next = await this.status();
|
|
22932
|
+
if (this.#head(next) !== commit)
|
|
22933
|
+
throw conflict();
|
|
22934
|
+
return { status: next, commit };
|
|
22935
|
+
}
|
|
22936
|
+
async #push(commit, expected) {
|
|
22937
|
+
await this.#enabled();
|
|
22938
|
+
const latest = await this.status();
|
|
22939
|
+
if (this.#head(latest) !== this.#head(expected) || latest.own_branch_id !== expected.own_branch_id)
|
|
22940
|
+
throw conflict();
|
|
22941
|
+
await this.#git(["push", "--porcelain", "--no-verify", this.#remote, `${commit}:refs/heads/${this.metadata.branch}`]);
|
|
22942
|
+
}
|
|
22943
|
+
async update() {
|
|
22944
|
+
const status = await this.status();
|
|
22945
|
+
await this.#enabled();
|
|
22946
|
+
if (!status.own_branch_id || !status.repository.main_commit)
|
|
22947
|
+
return status;
|
|
22948
|
+
await this.#fetch(status);
|
|
22949
|
+
const own2 = this.#head(status);
|
|
22950
|
+
const base = status.repository.main_commit;
|
|
22951
|
+
await this.#files(own2);
|
|
22952
|
+
await this.#files(base);
|
|
22953
|
+
if (own2 === base)
|
|
22954
|
+
return status;
|
|
22955
|
+
try {
|
|
22956
|
+
await this.#git(["merge-base", "--is-ancestor", base, own2]);
|
|
22957
|
+
return status;
|
|
22958
|
+
} catch {
|
|
22959
|
+
}
|
|
22960
|
+
let tree;
|
|
22961
|
+
try {
|
|
22962
|
+
tree = (await this.#git(["merge-tree", "--write-tree", own2, base])).toString().split("\n")[0].trim();
|
|
22963
|
+
} catch {
|
|
22964
|
+
throw new CodeSyncError("code_sync_conflict", "The base branch could not be merged automatically. Resolve conflicts using native Git or GitHub, then download again. Git 2.38 or newer is required.");
|
|
22965
|
+
}
|
|
22966
|
+
if (!Commit2.test(tree))
|
|
22967
|
+
throw conflict();
|
|
22968
|
+
await this.#files(tree);
|
|
22969
|
+
const commit = (await this.#git(["commit-tree", tree, "-p", own2, "-p", base, "-m", "Merge shared GitHub base into my GatherThread branch"])).toString().trim();
|
|
22970
|
+
await this.#push(commit, status);
|
|
22971
|
+
return this.status();
|
|
22972
|
+
}
|
|
22973
|
+
#git(args, input2, index) {
|
|
22974
|
+
return this.#run(args, { cwd: this.#directory, ...input2 ? { input: input2 } : {}, ...index ? { index } : {} });
|
|
22975
|
+
}
|
|
22976
|
+
};
|
|
22977
|
+
function conflict() {
|
|
22978
|
+
return new CodeSyncError("code_sync_conflict", "GitHub progress changed. Refresh, then download or recover before uploading again.");
|
|
22979
|
+
}
|
|
22980
|
+
async function runGitHubGit(args, options) {
|
|
22981
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith("GIT_") && !key.startsWith("GATHERTHREAD_") && !["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GH_HOST", "GH_PROMPT_DISABLED"].includes(key)));
|
|
22982
|
+
return new Promise((resolve, reject) => {
|
|
22983
|
+
const child = spawn("git", [
|
|
22984
|
+
"-c",
|
|
22985
|
+
"core.hooksPath=/dev/null",
|
|
22986
|
+
"-c",
|
|
22987
|
+
"core.fsmonitor=false",
|
|
22988
|
+
"-c",
|
|
22989
|
+
"commit.gpgSign=false",
|
|
22990
|
+
"-c",
|
|
22991
|
+
"protocol.allow=never",
|
|
22992
|
+
"-c",
|
|
22993
|
+
"protocol.https.allow=always",
|
|
22994
|
+
"-c",
|
|
22995
|
+
"http.followRedirects=false",
|
|
22996
|
+
"-c",
|
|
22997
|
+
"credential.helper=",
|
|
22998
|
+
"-c",
|
|
22999
|
+
"credential.helper=!gh auth git-credential",
|
|
23000
|
+
"-c",
|
|
23001
|
+
"credential.interactive=false",
|
|
23002
|
+
"-c",
|
|
23003
|
+
"user.name=GatherThread",
|
|
23004
|
+
"-c",
|
|
23005
|
+
"user.email=checkpoint@users.noreply.github.com",
|
|
23006
|
+
...args
|
|
23007
|
+
], {
|
|
23008
|
+
cwd: options.cwd,
|
|
23009
|
+
windowsHide: true,
|
|
23010
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
23011
|
+
env: { ...env, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: process.platform === "win32" ? "NUL" : "/dev/null", GIT_TERMINAL_PROMPT: "0", GCM_INTERACTIVE: "never", ...options.index ? { GIT_INDEX_FILE: options.index } : {} }
|
|
23012
|
+
});
|
|
23013
|
+
const chunks = [];
|
|
23014
|
+
let size = 0;
|
|
23015
|
+
let failed = false;
|
|
23016
|
+
const fail = () => {
|
|
23017
|
+
if (failed)
|
|
23018
|
+
return;
|
|
23019
|
+
failed = true;
|
|
23020
|
+
child.kill();
|
|
23021
|
+
reject(new CodeSyncError("code_sync_unavailable", "GitHub Git operation failed. Check Git 2.38+, GitHub CLI login, repository access and network. No credentials are sent through GatherThread."));
|
|
23022
|
+
};
|
|
23023
|
+
const timer = setTimeout(fail, 12e4);
|
|
23024
|
+
timer.unref();
|
|
23025
|
+
child.stdout.on("data", (chunk) => {
|
|
23026
|
+
size += chunk.length;
|
|
23027
|
+
if (size > GITHUB_CODE_SYNC_LIMITS.maxSnapshotBytes + 8 * 1024 * 1024)
|
|
23028
|
+
fail();
|
|
23029
|
+
else
|
|
23030
|
+
chunks.push(chunk);
|
|
23031
|
+
});
|
|
23032
|
+
child.stderr.resume();
|
|
23033
|
+
child.stdin.on("error", () => {
|
|
23034
|
+
});
|
|
23035
|
+
child.on("error", fail);
|
|
23036
|
+
child.on("close", (code) => {
|
|
23037
|
+
clearTimeout(timer);
|
|
23038
|
+
if (code !== 0)
|
|
23039
|
+
fail();
|
|
23040
|
+
else if (!failed)
|
|
23041
|
+
resolve(Buffer.concat(chunks));
|
|
23042
|
+
});
|
|
23043
|
+
child.stdin.end(options.input);
|
|
23044
|
+
});
|
|
23045
|
+
}
|
|
23046
|
+
|
|
23047
|
+
// ../bridge/dist/src/github-auth.js
|
|
23048
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
23049
|
+
async function connectGitHubAccount(command = runGitHubAuthCommand) {
|
|
23050
|
+
if (await command(["auth", "status", "--hostname", "github.com"], 1e4))
|
|
23051
|
+
return;
|
|
23052
|
+
const signedIn = await command(["auth", "login", "--hostname", "github.com", "--git-protocol", "https", "--web", "--clipboard"], 24e4);
|
|
23053
|
+
if (!signedIn || !await command(["auth", "status", "--hostname", "github.com"], 1e4)) {
|
|
23054
|
+
throw new CodeSyncError("github_auth_failed", "GitHub sign-in was not completed on this device. Reopen the connection and finish in your browser.");
|
|
23055
|
+
}
|
|
23056
|
+
}
|
|
23057
|
+
function runGitHubAuthCommand(args, timeoutMs) {
|
|
23058
|
+
return new Promise((resolve, reject) => {
|
|
23059
|
+
const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !["GH_TOKEN", "GITHUB_TOKEN", "GH_ENTERPRISE_TOKEN", "GH_HOST", "GH_PROMPT_DISABLED"].includes(key)));
|
|
23060
|
+
const child = spawn2("gh", [...args], { env, windowsHide: true, stdio: ["ignore", "ignore", "ignore"] });
|
|
23061
|
+
let settled = false;
|
|
23062
|
+
const finish = (value) => {
|
|
23063
|
+
if (settled)
|
|
23064
|
+
return;
|
|
23065
|
+
settled = true;
|
|
23066
|
+
clearTimeout(timer);
|
|
23067
|
+
resolve(value);
|
|
23068
|
+
};
|
|
23069
|
+
const timer = setTimeout(() => {
|
|
23070
|
+
child.kill();
|
|
23071
|
+
finish(false);
|
|
23072
|
+
}, timeoutMs);
|
|
23073
|
+
child.on("error", (error62) => {
|
|
23074
|
+
if (settled)
|
|
23075
|
+
return;
|
|
23076
|
+
settled = true;
|
|
23077
|
+
clearTimeout(timer);
|
|
23078
|
+
reject(new CodeSyncError(error62.code === "ENOENT" ? "github_cli_unavailable" : "github_auth_failed", error62.code === "ENOENT" ? "Install GitHub CLI on this device before connecting." : "GitHub CLI could not start on this device."));
|
|
23079
|
+
});
|
|
23080
|
+
child.on("close", (code) => finish(code === 0));
|
|
23081
|
+
});
|
|
23082
|
+
}
|
|
23083
|
+
|
|
21937
23084
|
// dist/src/native-plugin.js
|
|
21938
|
-
import { createHash as
|
|
21939
|
-
import
|
|
23085
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
23086
|
+
import path10 from "node:path";
|
|
21940
23087
|
|
|
21941
23088
|
// dist/src/code-sync-controller.js
|
|
21942
|
-
import { constants as
|
|
21943
|
-
import { lstat as
|
|
21944
|
-
import { randomUUID as
|
|
21945
|
-
import
|
|
23089
|
+
import { constants as constants3 } from "node:fs";
|
|
23090
|
+
import { lstat as lstat5, mkdir as mkdir4, open as open5, rename as rename2, unlink as unlink3 } from "node:fs/promises";
|
|
23091
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
23092
|
+
import path5 from "node:path";
|
|
21946
23093
|
|
|
21947
23094
|
// dist/src/path-security.js
|
|
21948
|
-
import { lstat as
|
|
21949
|
-
import
|
|
23095
|
+
import { lstat as lstat4, realpath as realpath2 } from "node:fs/promises";
|
|
23096
|
+
import path4 from "node:path";
|
|
21950
23097
|
async function prospectiveRealPath(candidate) {
|
|
21951
|
-
let current =
|
|
23098
|
+
let current = path4.resolve(candidate);
|
|
21952
23099
|
const missing = [];
|
|
21953
23100
|
while (true) {
|
|
21954
23101
|
try {
|
|
21955
|
-
return
|
|
23102
|
+
return path4.join(await realpath2(current), ...missing);
|
|
21956
23103
|
} catch (error62) {
|
|
21957
23104
|
if (!isNodeError2(error62) || error62.code !== "ENOENT")
|
|
21958
23105
|
throw error62;
|
|
21959
|
-
const parent =
|
|
23106
|
+
const parent = path4.dirname(current);
|
|
21960
23107
|
if (parent === current)
|
|
21961
23108
|
throw new Error(`No existing ancestor for path: ${candidate}`);
|
|
21962
|
-
missing.unshift(
|
|
23109
|
+
missing.unshift(path4.basename(current));
|
|
21963
23110
|
current = parent;
|
|
21964
23111
|
}
|
|
21965
23112
|
}
|
|
21966
23113
|
}
|
|
21967
23114
|
async function secureDirectoryChain(directory) {
|
|
21968
|
-
const absolute =
|
|
21969
|
-
const root =
|
|
21970
|
-
const relative =
|
|
21971
|
-
const components = relative ? relative.split(
|
|
23115
|
+
const absolute = path4.resolve(directory);
|
|
23116
|
+
const root = path4.parse(absolute).root;
|
|
23117
|
+
const relative = path4.relative(root, absolute);
|
|
23118
|
+
const components = relative ? relative.split(path4.sep) : [];
|
|
21972
23119
|
const identities = [];
|
|
21973
23120
|
let current = root;
|
|
21974
23121
|
for (const component of components) {
|
|
21975
|
-
current =
|
|
23122
|
+
current = path4.join(current, component);
|
|
21976
23123
|
let metadata;
|
|
21977
23124
|
try {
|
|
21978
|
-
metadata = await
|
|
23125
|
+
metadata = await lstat4(current);
|
|
21979
23126
|
} catch (error62) {
|
|
21980
23127
|
if (isNodeError2(error62) && error62.code === "ENOENT")
|
|
21981
23128
|
break;
|
|
@@ -22012,6 +23159,7 @@ var ACTIONS = /* @__PURE__ */ new Set([
|
|
|
22012
23159
|
"code_auto_upload_enable",
|
|
22013
23160
|
"code_auto_upload_disable"
|
|
22014
23161
|
]);
|
|
23162
|
+
var GITHUB_CODE_ACTIONS = githubCodeSyncRequestKinds.filter((kind) => kind !== "github_auth_connect");
|
|
22015
23163
|
var DshCodeSyncController = class {
|
|
22016
23164
|
#authorized = false;
|
|
22017
23165
|
#status;
|
|
@@ -22021,8 +23169,10 @@ var DshCodeSyncController = class {
|
|
|
22021
23169
|
#poll;
|
|
22022
23170
|
#stopped = false;
|
|
22023
23171
|
#options;
|
|
23172
|
+
#actions;
|
|
22024
23173
|
constructor(options) {
|
|
22025
23174
|
this.#options = options;
|
|
23175
|
+
this.#actions = options.actionKinds ? new Set(options.actionKinds) : ACTIONS;
|
|
22026
23176
|
}
|
|
22027
23177
|
view() {
|
|
22028
23178
|
return {
|
|
@@ -22091,7 +23241,7 @@ var DshCodeSyncController = class {
|
|
|
22091
23241
|
if (this.#stopped)
|
|
22092
23242
|
return;
|
|
22093
23243
|
const runtime = runtimes.get(job.sessionId);
|
|
22094
|
-
if (!runtime || job.targetRuntimeId !== runtime || !
|
|
23244
|
+
if (!runtime || job.targetRuntimeId !== runtime || !this.#actions.has(job.kind))
|
|
22095
23245
|
continue;
|
|
22096
23246
|
let claimed;
|
|
22097
23247
|
try {
|
|
@@ -22134,11 +23284,11 @@ var DshCodeSyncController = class {
|
|
|
22134
23284
|
await this.#tail;
|
|
22135
23285
|
}
|
|
22136
23286
|
async #execute(kind, operationId) {
|
|
22137
|
-
if (!
|
|
23287
|
+
if (!this.#actions.has(kind))
|
|
22138
23288
|
throw new CodeSyncLocalError("code_action_invalid");
|
|
22139
23289
|
if (!this.#authorized)
|
|
22140
23290
|
throw new CodeSyncLocalError("code_sync_disabled");
|
|
22141
|
-
if (this.#options.isBusy() &&
|
|
23291
|
+
if (this.#options.isBusy() && !["code_sync_status", "code_auto_upload_disable", "github_code_sync_status", "github_code_auto_upload_disable"].includes(kind)) {
|
|
22142
23292
|
throw new CodeSyncLocalError("code_sync_busy");
|
|
22143
23293
|
}
|
|
22144
23294
|
return this.#requireEngine().execute(kind, {
|
|
@@ -22169,13 +23319,13 @@ var CodeSyncLocalError = class extends Error {
|
|
|
22169
23319
|
};
|
|
22170
23320
|
function codeSyncErrorCode(error62) {
|
|
22171
23321
|
const code = typeof error62 === "object" && error62 !== null && "code" in error62 ? error62.code : void 0;
|
|
22172
|
-
return typeof code === "string" && /^code_[a-z_]{1,60}$/u.test(code) ? code : "code_sync_failed";
|
|
23322
|
+
return typeof code === "string" && /^(?:github_)?code_[a-z_]{1,60}$/u.test(code) ? code : "code_sync_failed";
|
|
22173
23323
|
}
|
|
22174
23324
|
async function loadConsent(file2, binding) {
|
|
22175
|
-
const ancestors = await secureDirectoryChain(
|
|
23325
|
+
const ancestors = await secureDirectoryChain(path5.dirname(file2));
|
|
22176
23326
|
let before;
|
|
22177
23327
|
try {
|
|
22178
|
-
before = await
|
|
23328
|
+
before = await lstat5(file2);
|
|
22179
23329
|
} catch (error62) {
|
|
22180
23330
|
if (error62.code === "ENOENT")
|
|
22181
23331
|
return false;
|
|
@@ -22184,25 +23334,25 @@ async function loadConsent(file2, binding) {
|
|
|
22184
23334
|
if (!before.isFile() || before.isSymbolicLink() || before.nlink !== 1 || before.size > 4096 || process.platform !== "win32" && (before.mode & 63) !== 0) {
|
|
22185
23335
|
throw new CodeSyncLocalError("code_consent_invalid");
|
|
22186
23336
|
}
|
|
22187
|
-
const handle = await
|
|
23337
|
+
const handle = await open5(file2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
|
|
22188
23338
|
try {
|
|
22189
23339
|
const opened = await handle.stat();
|
|
22190
23340
|
if (before.dev !== opened.dev || before.ino !== opened.ino)
|
|
22191
23341
|
throw new CodeSyncLocalError("code_consent_invalid");
|
|
22192
23342
|
const value = JSON.parse(await handle.readFile("utf8"));
|
|
22193
|
-
assertSameDirectoryChain(ancestors, await secureDirectoryChain(
|
|
23343
|
+
assertSameDirectoryChain(ancestors, await secureDirectoryChain(path5.dirname(file2)));
|
|
22194
23344
|
return value.version === 1 && value.binding === binding && value.enabled === true;
|
|
22195
23345
|
} finally {
|
|
22196
23346
|
await handle.close();
|
|
22197
23347
|
}
|
|
22198
23348
|
}
|
|
22199
23349
|
async function saveConsent(file2, binding, enabled) {
|
|
22200
|
-
await secureDirectoryChain(
|
|
22201
|
-
await
|
|
22202
|
-
const ancestors = await secureDirectoryChain(
|
|
23350
|
+
await secureDirectoryChain(path5.dirname(file2));
|
|
23351
|
+
await mkdir4(path5.dirname(file2), { recursive: true, mode: 448 });
|
|
23352
|
+
const ancestors = await secureDirectoryChain(path5.dirname(file2));
|
|
22203
23353
|
await loadConsent(file2, binding);
|
|
22204
|
-
const temporary = `${file2}.${
|
|
22205
|
-
const handle = await
|
|
23354
|
+
const temporary = `${file2}.${randomUUID4()}.tmp`;
|
|
23355
|
+
const handle = await open5(temporary, "wx", 384);
|
|
22206
23356
|
try {
|
|
22207
23357
|
await handle.writeFile(JSON.stringify({ version: 1, binding, enabled }));
|
|
22208
23358
|
await handle.sync();
|
|
@@ -22210,14 +23360,136 @@ async function saveConsent(file2, binding, enabled) {
|
|
|
22210
23360
|
await handle.close();
|
|
22211
23361
|
}
|
|
22212
23362
|
try {
|
|
22213
|
-
assertSameDirectoryChain(ancestors, await secureDirectoryChain(
|
|
23363
|
+
assertSameDirectoryChain(ancestors, await secureDirectoryChain(path5.dirname(file2)));
|
|
22214
23364
|
await rename2(temporary, file2);
|
|
22215
23365
|
} catch (error62) {
|
|
22216
|
-
await
|
|
23366
|
+
await unlink3(temporary).catch(() => void 0);
|
|
22217
23367
|
throw error62;
|
|
22218
23368
|
}
|
|
22219
23369
|
}
|
|
22220
23370
|
|
|
23371
|
+
// dist/src/github-code-sync-controller.js
|
|
23372
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
23373
|
+
var DshGitHubCodeSyncController = class {
|
|
23374
|
+
options;
|
|
23375
|
+
#controller;
|
|
23376
|
+
#metadata;
|
|
23377
|
+
#key;
|
|
23378
|
+
#error;
|
|
23379
|
+
#tail = Promise.resolve();
|
|
23380
|
+
#stopped = false;
|
|
23381
|
+
#poll;
|
|
23382
|
+
constructor(options) {
|
|
23383
|
+
this.options = options;
|
|
23384
|
+
}
|
|
23385
|
+
view() {
|
|
23386
|
+
return {
|
|
23387
|
+
...this.#controller?.view() ?? { authorized: false },
|
|
23388
|
+
connection: this.#metadata?.connection ? { ...this.#metadata.connection } : null,
|
|
23389
|
+
branch: this.#metadata?.branch ?? null,
|
|
23390
|
+
...this.#error ? { error: this.#error } : {}
|
|
23391
|
+
};
|
|
23392
|
+
}
|
|
23393
|
+
async start() {
|
|
23394
|
+
await this.#serialize(async () => {
|
|
23395
|
+
await this.#refresh();
|
|
23396
|
+
});
|
|
23397
|
+
}
|
|
23398
|
+
authorize(enabled, expected) {
|
|
23399
|
+
return this.#serialize(async () => {
|
|
23400
|
+
if (!enabled) {
|
|
23401
|
+
await this.#controller?.authorize(false);
|
|
23402
|
+
return this.view();
|
|
23403
|
+
}
|
|
23404
|
+
await this.#refresh();
|
|
23405
|
+
const target = this.#metadata?.connection;
|
|
23406
|
+
if (!target || !expected || targetKey(target) !== targetKey(expected))
|
|
23407
|
+
throw coded("code_github_binding_changed");
|
|
23408
|
+
if (!this.#metadata?.can_write || !target.enabled)
|
|
23409
|
+
throw coded("code_sync_forbidden");
|
|
23410
|
+
await this.#controller.authorize(true);
|
|
23411
|
+
return this.view();
|
|
23412
|
+
});
|
|
23413
|
+
}
|
|
23414
|
+
execute(kind) {
|
|
23415
|
+
return this.#serialize(async () => {
|
|
23416
|
+
if (kind === "github_code_auto_upload_disable" && this.#controller) {
|
|
23417
|
+
await this.#controller.execute(kind);
|
|
23418
|
+
return this.view();
|
|
23419
|
+
}
|
|
23420
|
+
await this.#refresh();
|
|
23421
|
+
if (kind === "github_code_sync_status" && !this.#controller?.view().authorized)
|
|
23422
|
+
return this.view();
|
|
23423
|
+
if (!this.#controller)
|
|
23424
|
+
throw coded("code_github_not_configured");
|
|
23425
|
+
if (!this.#metadata?.connection?.enabled && kind !== "github_code_sync_status")
|
|
23426
|
+
throw coded("code_not_enabled");
|
|
23427
|
+
await this.#controller.execute(kind);
|
|
23428
|
+
return this.view();
|
|
23429
|
+
});
|
|
23430
|
+
}
|
|
23431
|
+
poll() {
|
|
23432
|
+
if (this.#stopped)
|
|
23433
|
+
return Promise.resolve();
|
|
23434
|
+
this.#poll ??= this.#serialize(async () => {
|
|
23435
|
+
await this.#refresh();
|
|
23436
|
+
await this.#controller?.poll();
|
|
23437
|
+
}).finally(() => {
|
|
23438
|
+
this.#poll = void 0;
|
|
23439
|
+
});
|
|
23440
|
+
return this.#poll;
|
|
23441
|
+
}
|
|
23442
|
+
async stop() {
|
|
23443
|
+
this.#stopped = true;
|
|
23444
|
+
await this.#tail;
|
|
23445
|
+
await this.#controller?.stop();
|
|
23446
|
+
}
|
|
23447
|
+
async #refresh() {
|
|
23448
|
+
const metadata = await this.options.readMetadata();
|
|
23449
|
+
const target = metadata.connection;
|
|
23450
|
+
const key = target && metadata.can_write ? targetKey(target) : void 0;
|
|
23451
|
+
if (key !== this.#key) {
|
|
23452
|
+
await this.#controller?.authorize(false);
|
|
23453
|
+
await this.#controller?.stop();
|
|
23454
|
+
this.#controller = void 0;
|
|
23455
|
+
this.#key = void 0;
|
|
23456
|
+
if (target && key) {
|
|
23457
|
+
const capturedTarget = { ...target };
|
|
23458
|
+
this.#controller = new DshCodeSyncController({
|
|
23459
|
+
...this.options,
|
|
23460
|
+
binding: createHash4("sha256").update(JSON.stringify([this.options.binding, key])).digest("hex"),
|
|
23461
|
+
actionKinds: GITHUB_CODE_ACTIONS,
|
|
23462
|
+
createEngine: () => this.options.createEngine(capturedTarget)
|
|
23463
|
+
});
|
|
23464
|
+
await this.#controller.start();
|
|
23465
|
+
this.#key = key;
|
|
23466
|
+
}
|
|
23467
|
+
}
|
|
23468
|
+
this.#metadata = metadata;
|
|
23469
|
+
}
|
|
23470
|
+
#serialize(operation) {
|
|
23471
|
+
const pending = this.#tail.then(async () => {
|
|
23472
|
+
if (this.#stopped)
|
|
23473
|
+
throw coded("code_sync_stopped");
|
|
23474
|
+
this.#error = void 0;
|
|
23475
|
+
try {
|
|
23476
|
+
return await operation();
|
|
23477
|
+
} catch (error62) {
|
|
23478
|
+
this.#error = codeSyncErrorCode(error62);
|
|
23479
|
+
throw coded(this.#error);
|
|
23480
|
+
}
|
|
23481
|
+
});
|
|
23482
|
+
this.#tail = pending.catch(() => void 0);
|
|
23483
|
+
return pending;
|
|
23484
|
+
}
|
|
23485
|
+
};
|
|
23486
|
+
function targetKey(target) {
|
|
23487
|
+
return JSON.stringify([target.repository, target.base_branch, target.revision]);
|
|
23488
|
+
}
|
|
23489
|
+
function coded(code) {
|
|
23490
|
+
return Object.assign(new Error(code), { code });
|
|
23491
|
+
}
|
|
23492
|
+
|
|
22221
23493
|
// dist/src/collaboration-api.js
|
|
22222
23494
|
function createHttpDshCollaborationApi(options) {
|
|
22223
23495
|
const client = new HttpCollaborationClient({
|
|
@@ -22293,9 +23565,9 @@ function clipToUtf8Bytes(value, maxBytes) {
|
|
|
22293
23565
|
}
|
|
22294
23566
|
|
|
22295
23567
|
// dist/src/config.js
|
|
22296
|
-
import { createHash as
|
|
22297
|
-
import { lstat as
|
|
22298
|
-
import
|
|
23568
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
23569
|
+
import { lstat as lstat6 } from "node:fs/promises";
|
|
23570
|
+
import path6 from "node:path";
|
|
22299
23571
|
var CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
22300
23572
|
"enabled",
|
|
22301
23573
|
"bindingMode",
|
|
@@ -22408,7 +23680,7 @@ async function assertSafeDshStateRoot(config2) {
|
|
|
22408
23680
|
await assertRealStateSeparation(config2.workspacePath, config2.stateRoot, "config.stateRoot");
|
|
22409
23681
|
await secureDirectoryChain(config2.stateRoot);
|
|
22410
23682
|
try {
|
|
22411
|
-
const target = await
|
|
23683
|
+
const target = await lstat6(config2.stateRoot);
|
|
22412
23684
|
if (target.isSymbolicLink() || !target.isDirectory()) {
|
|
22413
23685
|
throw new Error("config.stateRoot must be a real directory, never a symbolic link");
|
|
22414
23686
|
}
|
|
@@ -22437,11 +23709,11 @@ function rejectModeKeys(value, forbidden, bindingMode) {
|
|
|
22437
23709
|
}
|
|
22438
23710
|
}
|
|
22439
23711
|
function deriveDshSessionId(projectId, sessionId, workspacePath) {
|
|
22440
|
-
const digest3 =
|
|
23712
|
+
const digest3 = createHash5("sha256").update(JSON.stringify([projectId, sessionId, path6.resolve(workspacePath)])).digest("hex").slice(0, 32);
|
|
22441
23713
|
return `gatherthread-${digest3}`;
|
|
22442
23714
|
}
|
|
22443
23715
|
function deriveNativeDshSessionId(projectId, sessionId, workspacePath) {
|
|
22444
|
-
const digest3 =
|
|
23716
|
+
const digest3 = createHash5("sha256").update(JSON.stringify(["native-web-v2", projectId, sessionId, path6.resolve(workspacePath)])).digest("hex").slice(0, 32);
|
|
22445
23717
|
return `gatherthread-${digest3}`;
|
|
22446
23718
|
}
|
|
22447
23719
|
function normalizeApiUrl(value) {
|
|
@@ -22479,13 +23751,13 @@ function safeText(value, label, maximum) {
|
|
|
22479
23751
|
}
|
|
22480
23752
|
function absolutePath(value, label) {
|
|
22481
23753
|
const text = requiredString2(value, label, 4096);
|
|
22482
|
-
if (!
|
|
23754
|
+
if (!path6.isAbsolute(text))
|
|
22483
23755
|
throw new Error(`${label} must be absolute`);
|
|
22484
|
-
return
|
|
23756
|
+
return path6.resolve(text);
|
|
22485
23757
|
}
|
|
22486
23758
|
function isWithin2(parent, candidate) {
|
|
22487
|
-
const relative =
|
|
22488
|
-
return relative === "" || !relative.startsWith(`..${
|
|
23759
|
+
const relative = path6.relative(parent, candidate);
|
|
23760
|
+
return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== "..";
|
|
22489
23761
|
}
|
|
22490
23762
|
function isNodeError3(error62) {
|
|
22491
23763
|
return error62 instanceof Error && "code" in error62;
|
|
@@ -22524,7 +23796,7 @@ function rejectUnknownKeys(value, allowed, label) {
|
|
|
22524
23796
|
}
|
|
22525
23797
|
|
|
22526
23798
|
// dist/src/connector.js
|
|
22527
|
-
import { createHash as
|
|
23799
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
22528
23800
|
|
|
22529
23801
|
// dist/src/canonical-prompt.js
|
|
22530
23802
|
var MAX_EVENT_TEXT_BYTES = 8 * 1024;
|
|
@@ -22597,12 +23869,12 @@ ${footer}`, "utf8");
|
|
|
22597
23869
|
const retained = [];
|
|
22598
23870
|
let used = 0;
|
|
22599
23871
|
for (const line of contextLines.toReversed()) {
|
|
22600
|
-
const
|
|
23872
|
+
const bytes2 = Buffer.byteLength(`${line}
|
|
22601
23873
|
`, "utf8");
|
|
22602
|
-
if (used +
|
|
23874
|
+
if (used + bytes2 > contextBudget)
|
|
22603
23875
|
continue;
|
|
22604
23876
|
retained.push(line);
|
|
22605
|
-
used +=
|
|
23877
|
+
used += bytes2;
|
|
22606
23878
|
}
|
|
22607
23879
|
retained.reverse();
|
|
22608
23880
|
const omitted = retained.length < contextLines.length ? [`[${contextLines.length - retained.length} older public context update(s) omitted by size limit]`] : [];
|
|
@@ -22816,41 +24088,290 @@ function safeJson(value) {
|
|
|
22816
24088
|
return "[Unserializable tool value]";
|
|
22817
24089
|
}
|
|
22818
24090
|
}
|
|
22819
|
-
function boundText2(value, maximumBytes) {
|
|
22820
|
-
if (Buffer.byteLength(value, "utf8") <= maximumBytes)
|
|
22821
|
-
return value;
|
|
22822
|
-
const suffix = "\n[TRUNCATED]";
|
|
22823
|
-
const target = Math.max(0, maximumBytes - Buffer.byteLength(suffix, "utf8"));
|
|
22824
|
-
let low = 0;
|
|
22825
|
-
let high = value.length;
|
|
22826
|
-
while (low < high) {
|
|
22827
|
-
const middle = Math.ceil((low + high) / 2);
|
|
22828
|
-
if (Buffer.byteLength(value.slice(0, middle), "utf8") <= target)
|
|
22829
|
-
low = middle;
|
|
22830
|
-
else
|
|
22831
|
-
high = middle - 1;
|
|
24091
|
+
function boundText2(value, maximumBytes) {
|
|
24092
|
+
if (Buffer.byteLength(value, "utf8") <= maximumBytes)
|
|
24093
|
+
return value;
|
|
24094
|
+
const suffix = "\n[TRUNCATED]";
|
|
24095
|
+
const target = Math.max(0, maximumBytes - Buffer.byteLength(suffix, "utf8"));
|
|
24096
|
+
let low = 0;
|
|
24097
|
+
let high = value.length;
|
|
24098
|
+
while (low < high) {
|
|
24099
|
+
const middle = Math.ceil((low + high) / 2);
|
|
24100
|
+
if (Buffer.byteLength(value.slice(0, middle), "utf8") <= target)
|
|
24101
|
+
low = middle;
|
|
24102
|
+
else
|
|
24103
|
+
high = middle - 1;
|
|
24104
|
+
}
|
|
24105
|
+
return `${value.slice(0, low)}${suffix}`;
|
|
24106
|
+
}
|
|
24107
|
+
function checkedLimit(value, fallback) {
|
|
24108
|
+
if (value === void 0)
|
|
24109
|
+
return fallback;
|
|
24110
|
+
if (!Number.isSafeInteger(value) || value < 1024 || value > 2 * 1024 * 1024) {
|
|
24111
|
+
throw new Error("DSH event mapping byte limits must be integers from 1024 to 2097152");
|
|
24112
|
+
}
|
|
24113
|
+
return value;
|
|
24114
|
+
}
|
|
24115
|
+
function safeTimestamp(value) {
|
|
24116
|
+
try {
|
|
24117
|
+
const timestamp3 = new Date(value);
|
|
24118
|
+
if (!Number.isFinite(timestamp3.getTime()))
|
|
24119
|
+
return void 0;
|
|
24120
|
+
return timestamp3.toISOString();
|
|
24121
|
+
} catch {
|
|
24122
|
+
return void 0;
|
|
24123
|
+
}
|
|
24124
|
+
}
|
|
24125
|
+
function asObject2(value) {
|
|
24126
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
24127
|
+
}
|
|
24128
|
+
|
|
24129
|
+
// dist/src/live-event-mapper.js
|
|
24130
|
+
var MAX_ATTEMPTS = 64;
|
|
24131
|
+
var MAX_BLOCKS = 128;
|
|
24132
|
+
var MAX_TEXT = 96 * 1024;
|
|
24133
|
+
function createDshLiveEventMapper(publisher) {
|
|
24134
|
+
const attempts = /* @__PURE__ */ new Map();
|
|
24135
|
+
const steps = /* @__PURE__ */ new Map();
|
|
24136
|
+
const tools = /* @__PURE__ */ new Map();
|
|
24137
|
+
let revision = 0;
|
|
24138
|
+
const deliver = (operation) => {
|
|
24139
|
+
void operation.catch(() => void 0);
|
|
24140
|
+
};
|
|
24141
|
+
const snapshot = (block, state) => deliver(publisher.onSnapshot(block.key, block.text, block.kind, block.title, state));
|
|
24142
|
+
const toolFor = (id, fallbackKey) => {
|
|
24143
|
+
let tool = tools.get(id);
|
|
24144
|
+
if (tool === void 0 && tools.size < MAX_BLOCKS) {
|
|
24145
|
+
tool = { key: fallbackKey, arguments: "" };
|
|
24146
|
+
tools.set(id, tool);
|
|
24147
|
+
}
|
|
24148
|
+
return tool;
|
|
24149
|
+
};
|
|
24150
|
+
const settle2 = (attempt, state) => {
|
|
24151
|
+
if (state === "failed")
|
|
24152
|
+
attempt.failed = true;
|
|
24153
|
+
for (const block of attempt.blocks.values()) {
|
|
24154
|
+
if (block.kind !== "tool")
|
|
24155
|
+
snapshot(block, state);
|
|
24156
|
+
else if (state === "failed") {
|
|
24157
|
+
const argumentsText = block.callId === void 0 ? void 0 : tools.get(block.callId)?.arguments;
|
|
24158
|
+
deliver(publisher.onSnapshot(block.key, `Arguments:
|
|
24159
|
+
${argumentsText || "[Unparseable tool arguments]"}`, "tool", block.title, state));
|
|
24160
|
+
}
|
|
24161
|
+
}
|
|
24162
|
+
};
|
|
24163
|
+
const onFrame = (frame) => {
|
|
24164
|
+
if (frame.revision <= revision)
|
|
24165
|
+
return;
|
|
24166
|
+
const identity = frame.attemptId;
|
|
24167
|
+
if (frame.type === "start") {
|
|
24168
|
+
if (attempts.has(identity) || attempts.size >= MAX_ATTEMPTS)
|
|
24169
|
+
return;
|
|
24170
|
+
revision = frame.revision;
|
|
24171
|
+
const attempt2 = { turn: frame.turn, step: frame.step, nextIndex: 0, closed: false, failed: false, blocks: /* @__PURE__ */ new Map() };
|
|
24172
|
+
attempts.set(identity, attempt2);
|
|
24173
|
+
steps.set(`${frame.turn}:${frame.step}`, attempt2);
|
|
24174
|
+
return;
|
|
24175
|
+
}
|
|
24176
|
+
const attempt = attempts.get(identity);
|
|
24177
|
+
if (attempt === void 0 || attempt.closed || frame.index !== attempt.nextIndex)
|
|
24178
|
+
return;
|
|
24179
|
+
revision = frame.revision;
|
|
24180
|
+
if (frame.type === "end") {
|
|
24181
|
+
attempt.closed = true;
|
|
24182
|
+
settle2(attempt, attempt.failed || frame.outcome.kind === "abandoned" || frame.outcome.eventType === "assistant/attempt" ? "failed" : "completed");
|
|
24183
|
+
return;
|
|
24184
|
+
}
|
|
24185
|
+
attempt.nextIndex++;
|
|
24186
|
+
const chunk = object2(frame.chunk);
|
|
24187
|
+
if (chunk?.type === "finish") {
|
|
24188
|
+
const reason = object2(chunk.reason)?.kind ?? chunk.reason;
|
|
24189
|
+
if (reason === "error" || reason === "aborted")
|
|
24190
|
+
settle2(attempt, "failed");
|
|
24191
|
+
return;
|
|
24192
|
+
}
|
|
24193
|
+
if (!integer2(chunk?.index) || Number(chunk.index) >= MAX_BLOCKS)
|
|
24194
|
+
return;
|
|
24195
|
+
const index = Number(chunk.index);
|
|
24196
|
+
if (chunk.type === "block-start") {
|
|
24197
|
+
const kind = chunk.blockType === "text" ? "answer" : chunk.blockType === "reasoning" ? "reasoning" : chunk.blockType === "tool-call" ? "tool" : void 0;
|
|
24198
|
+
if (kind !== void 0 && !attempt.blocks.has(index)) {
|
|
24199
|
+
attempt.blocks.set(index, { key: `${identity}:${index}`, kind, text: "" });
|
|
24200
|
+
}
|
|
24201
|
+
return;
|
|
24202
|
+
}
|
|
24203
|
+
const block = attempt.blocks.get(index);
|
|
24204
|
+
if (block === void 0)
|
|
24205
|
+
return;
|
|
24206
|
+
if (chunk.type === "text-delta" && block.kind === "answer" || chunk.type === "reasoning-delta" && block.kind === "reasoning") {
|
|
24207
|
+
if (typeof chunk.text !== "string")
|
|
24208
|
+
return;
|
|
24209
|
+
block.text = bound2(block.text + chunk.text);
|
|
24210
|
+
deliver(publisher.onDelta(block.key, chunk.text, block.kind));
|
|
24211
|
+
} else if (chunk.type === "tool-call-delta" && block.kind === "tool") {
|
|
24212
|
+
if (!identifier(chunk.id) || typeof chunk.argumentsDelta !== "string" || block.callId !== void 0 && block.callId !== chunk.id)
|
|
24213
|
+
return;
|
|
24214
|
+
block.callId = chunk.id;
|
|
24215
|
+
if (typeof chunk.name === "string")
|
|
24216
|
+
block.title = bound2(chunk.name).slice(0, 256);
|
|
24217
|
+
const tool = toolFor(block.callId, block.key);
|
|
24218
|
+
if (tool === void 0)
|
|
24219
|
+
return;
|
|
24220
|
+
block.key = tool.key;
|
|
24221
|
+
block.text = bound2(block.text + chunk.argumentsDelta);
|
|
24222
|
+
if (block.title !== void 0)
|
|
24223
|
+
tool.title = block.title;
|
|
24224
|
+
const argumentsText = parsedArguments(block.text);
|
|
24225
|
+
if (argumentsText !== void 0)
|
|
24226
|
+
tool.arguments = argumentsText;
|
|
24227
|
+
deliver(publisher.onSnapshot(block.key, argumentsText === void 0 ? "Preparing tool arguments.\n" : `Arguments:
|
|
24228
|
+
${argumentsText}`, "tool", block.title));
|
|
24229
|
+
} else if (chunk.type === "block-end") {
|
|
24230
|
+
const content = object2(chunk.block);
|
|
24231
|
+
if ((content?.type === "text" && block.kind === "answer" || content?.type === "reasoning" && block.kind === "reasoning") && typeof content.text === "string") {
|
|
24232
|
+
block.text = bound2(content.text);
|
|
24233
|
+
snapshot(block, "completed");
|
|
24234
|
+
} else if (content?.type === "tool-call" && block.kind === "tool" && identifier(content.id) && typeof content.arguments === "string" && (block.callId === void 0 || content.id === block.callId)) {
|
|
24235
|
+
const tool = toolFor(content.id, block.key);
|
|
24236
|
+
if (tool === void 0)
|
|
24237
|
+
return;
|
|
24238
|
+
block.callId = content.id;
|
|
24239
|
+
block.key = tool.key;
|
|
24240
|
+
block.text = publicArguments(content.arguments);
|
|
24241
|
+
if (typeof content.name === "string")
|
|
24242
|
+
block.title = content.name.slice(0, 256);
|
|
24243
|
+
tool.arguments = block.text;
|
|
24244
|
+
if (block.title !== void 0)
|
|
24245
|
+
tool.title = block.title;
|
|
24246
|
+
snapshot(block, "running");
|
|
24247
|
+
}
|
|
24248
|
+
}
|
|
24249
|
+
};
|
|
24250
|
+
const onDurable = (event) => {
|
|
24251
|
+
const data = object2(event.data);
|
|
24252
|
+
if (data === void 0)
|
|
24253
|
+
return;
|
|
24254
|
+
if (event.type === "assistant/message" && integer2(data.turn) && integer2(data.step)) {
|
|
24255
|
+
const attempt = steps.get(`${data.turn}:${data.step}`);
|
|
24256
|
+
if (attempt !== void 0 && data.interrupted === true)
|
|
24257
|
+
attempt.failed = true;
|
|
24258
|
+
const message = object2(data.message);
|
|
24259
|
+
if (!Array.isArray(message?.content))
|
|
24260
|
+
return;
|
|
24261
|
+
const nativeBlocks = [...attempt?.blocks.values() ?? []];
|
|
24262
|
+
for (const [index, value] of message.content.slice(0, MAX_BLOCKS).entries()) {
|
|
24263
|
+
const content = object2(value);
|
|
24264
|
+
const kind = content?.type === "text" ? "answer" : content?.type === "reasoning" ? "reasoning" : void 0;
|
|
24265
|
+
if (kind !== void 0 && typeof content.text === "string") {
|
|
24266
|
+
const candidate = nativeBlocks[index];
|
|
24267
|
+
const key = candidate?.kind === kind ? candidate.key : `assistant:${event.seq}:${index}`;
|
|
24268
|
+
if (candidate?.kind === kind)
|
|
24269
|
+
candidate.text = bound2(content.text);
|
|
24270
|
+
deliver(publisher.onSnapshot(key, content.text, kind, void 0, data.interrupted === true ? "failed" : "completed"));
|
|
24271
|
+
}
|
|
24272
|
+
}
|
|
24273
|
+
} else if (event.type === "assistant/attempt" && integer2(data.turn) && integer2(data.step)) {
|
|
24274
|
+
const attempt = steps.get(`${data.turn}:${data.step}`);
|
|
24275
|
+
if (attempt !== void 0)
|
|
24276
|
+
settle2(attempt, "failed");
|
|
24277
|
+
} else if (event.type === "tool/call" && identifier(data.callId) && typeof data.name === "string" && typeof data.arguments === "string") {
|
|
24278
|
+
const tool = toolFor(data.callId, `tool:${event.seq}`);
|
|
24279
|
+
if (tool === void 0)
|
|
24280
|
+
return;
|
|
24281
|
+
tool.arguments = publicArguments(data.arguments);
|
|
24282
|
+
tool.title = data.name.slice(0, 256);
|
|
24283
|
+
deliver(publisher.onSnapshot(tool.key, `Arguments:
|
|
24284
|
+
${tool.arguments}`, "tool", tool.title));
|
|
24285
|
+
} else if (event.type === "tool/result") {
|
|
24286
|
+
const message = object2(data.message);
|
|
24287
|
+
const content = Array.isArray(message?.content) ? message.content : [];
|
|
24288
|
+
const wrapped = content.map(object2).find((block) => block?.type === "tool-result");
|
|
24289
|
+
const id = wrapped?.toolCallId ?? message?.toolCallId;
|
|
24290
|
+
if (!identifier(id))
|
|
24291
|
+
return;
|
|
24292
|
+
const tool = tools.get(id);
|
|
24293
|
+
if (tool === void 0)
|
|
24294
|
+
return;
|
|
24295
|
+
const blocks = wrapped === void 0 ? content : wrapped.content;
|
|
24296
|
+
if (!Array.isArray(blocks))
|
|
24297
|
+
return;
|
|
24298
|
+
const text = blocks.slice(0, MAX_BLOCKS).flatMap((value) => {
|
|
24299
|
+
const block = object2(value);
|
|
24300
|
+
return block?.type === "text" && typeof block.text === "string" ? [bound2(block.text)] : [];
|
|
24301
|
+
}).join("");
|
|
24302
|
+
const failed = wrapped?.isError === true || message?.isError === true || data.error !== void 0;
|
|
24303
|
+
deliver(publisher.onSnapshot(tool.key, `Arguments:
|
|
24304
|
+
${tool.arguments}
|
|
24305
|
+
|
|
24306
|
+
Result:
|
|
24307
|
+
${bound2(text)}`, "tool", tool.title, failed ? "failed" : "completed"));
|
|
24308
|
+
} else if (event.type === "todo/write" && Array.isArray(data.todos) && data.todos.length <= 64) {
|
|
24309
|
+
const plan = [];
|
|
24310
|
+
for (const value of data.todos) {
|
|
24311
|
+
const item = object2(value);
|
|
24312
|
+
if (typeof item?.content !== "string" || !["pending", "in_progress", "completed"].includes(String(item.status)))
|
|
24313
|
+
return;
|
|
24314
|
+
plan.push({ step: item.content, status: item.status });
|
|
24315
|
+
}
|
|
24316
|
+
deliver(publisher.onPlan("plan", plan));
|
|
24317
|
+
}
|
|
24318
|
+
};
|
|
24319
|
+
return {
|
|
24320
|
+
onEvent(event) {
|
|
24321
|
+
if (event.kind === "assistant-stream")
|
|
24322
|
+
onFrame(event.frame);
|
|
24323
|
+
else
|
|
24324
|
+
onDurable(event.event);
|
|
24325
|
+
},
|
|
24326
|
+
async finish(success2 = true) {
|
|
24327
|
+
await publisher.finish(success2);
|
|
24328
|
+
}
|
|
24329
|
+
};
|
|
24330
|
+
}
|
|
24331
|
+
function parseDshAssistantStreamFrame(value) {
|
|
24332
|
+
const frame = object2(value);
|
|
24333
|
+
if (!identifier(frame?.attemptId) || !integer2(frame?.revision) || Number(frame.revision) < 1)
|
|
24334
|
+
return void 0;
|
|
24335
|
+
const shared = { attemptId: frame.attemptId, revision: Number(frame.revision) };
|
|
24336
|
+
if (frame.type === "start" && integer2(frame.turn) && integer2(frame.step)) {
|
|
24337
|
+
return { type: "start", ...shared, turn: Number(frame.turn), step: Number(frame.step) };
|
|
22832
24338
|
}
|
|
22833
|
-
|
|
22834
|
-
}
|
|
22835
|
-
function checkedLimit(value, fallback) {
|
|
22836
|
-
if (value === void 0)
|
|
22837
|
-
return fallback;
|
|
22838
|
-
if (!Number.isSafeInteger(value) || value < 1024 || value > 2 * 1024 * 1024) {
|
|
22839
|
-
throw new Error("DSH event mapping byte limits must be integers from 1024 to 2097152");
|
|
24339
|
+
if (frame.type === "chunk" && integer2(frame.index) && integer2(frame.time) && object2(frame.chunk) !== void 0) {
|
|
24340
|
+
return { type: "chunk", ...shared, index: Number(frame.index), time: Number(frame.time), chunk: frame.chunk };
|
|
22840
24341
|
}
|
|
22841
|
-
|
|
24342
|
+
const outcome = object2(frame.outcome);
|
|
24343
|
+
if (frame.type === "end" && integer2(frame.index)) {
|
|
24344
|
+
if (outcome?.kind === "abandoned")
|
|
24345
|
+
return { type: "end", ...shared, index: Number(frame.index), outcome: { kind: "abandoned" } };
|
|
24346
|
+
if (outcome?.kind === "committed" && integer2(outcome.seq) && (outcome.eventType === "assistant/message" || outcome.eventType === "assistant/attempt")) {
|
|
24347
|
+
return { type: "end", ...shared, index: Number(frame.index), outcome: { kind: "committed", eventType: outcome.eventType, seq: Number(outcome.seq) } };
|
|
24348
|
+
}
|
|
24349
|
+
}
|
|
24350
|
+
return void 0;
|
|
22842
24351
|
}
|
|
22843
|
-
function
|
|
24352
|
+
function publicArguments(text) {
|
|
24353
|
+
return parsedArguments(text) ?? "[Unparseable tool arguments]";
|
|
24354
|
+
}
|
|
24355
|
+
function parsedArguments(text) {
|
|
24356
|
+
if (text.length > MAX_TEXT)
|
|
24357
|
+
return void 0;
|
|
22844
24358
|
try {
|
|
22845
|
-
|
|
22846
|
-
if (!Number.isFinite(timestamp3.getTime()))
|
|
22847
|
-
return void 0;
|
|
22848
|
-
return timestamp3.toISOString();
|
|
24359
|
+
return JSON.stringify(redactValue(JSON.parse(text)));
|
|
22849
24360
|
} catch {
|
|
22850
24361
|
return void 0;
|
|
22851
24362
|
}
|
|
22852
24363
|
}
|
|
22853
|
-
function
|
|
24364
|
+
function bound2(text) {
|
|
24365
|
+
return text.length > MAX_TEXT ? `${text.slice(0, MAX_TEXT)}
|
|
24366
|
+
[TRUNCATED]` : text;
|
|
24367
|
+
}
|
|
24368
|
+
function integer2(value) {
|
|
24369
|
+
return Number.isSafeInteger(value) && Number(value) >= 0;
|
|
24370
|
+
}
|
|
24371
|
+
function identifier(value) {
|
|
24372
|
+
return typeof value === "string" && value.length > 0 && value.length <= 512;
|
|
24373
|
+
}
|
|
24374
|
+
function object2(value) {
|
|
22854
24375
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22855
24376
|
}
|
|
22856
24377
|
|
|
@@ -22881,6 +24402,7 @@ var DshHostConnector = class {
|
|
|
22881
24402
|
#publicationPromise;
|
|
22882
24403
|
#heartbeatOffline = false;
|
|
22883
24404
|
#liveProgressPromise = Promise.resolve();
|
|
24405
|
+
#activeLiveOutput;
|
|
22884
24406
|
#lastLiveProgressKey;
|
|
22885
24407
|
#lastLiveProgressAt = 0;
|
|
22886
24408
|
#stopPromise;
|
|
@@ -22891,6 +24413,8 @@ var DshHostConnector = class {
|
|
|
22891
24413
|
#activeExecutionSelection;
|
|
22892
24414
|
#summaryScanSequence = 0;
|
|
22893
24415
|
#hasSummaryHistory = false;
|
|
24416
|
+
#revisionPollPromise;
|
|
24417
|
+
#revisionSupported = false;
|
|
22894
24418
|
#liveEventSequences = /* @__PURE__ */ new Set();
|
|
22895
24419
|
#session;
|
|
22896
24420
|
#visibleState = "connecting";
|
|
@@ -22916,6 +24440,15 @@ var DshHostConnector = class {
|
|
|
22916
24440
|
get stopped() {
|
|
22917
24441
|
return this.#stopped;
|
|
22918
24442
|
}
|
|
24443
|
+
/** Project refresh is authoritative for the mode of future DSH bubbles. */
|
|
24444
|
+
updateSessionSummary(session) {
|
|
24445
|
+
if (!this.#started || this.#stopped)
|
|
24446
|
+
throw new Error("DSH connector is not running");
|
|
24447
|
+
if (session.id !== this.#config.sessionId || session.projectId !== this.#config.projectId || session.state !== "active" || !isSessionWritableBy(session, this.#actorUserId)) {
|
|
24448
|
+
throw new Error("Refreshed GatherThread Session is not this writable binding");
|
|
24449
|
+
}
|
|
24450
|
+
this.#session = session;
|
|
24451
|
+
}
|
|
22919
24452
|
/**
|
|
22920
24453
|
* Adopt a refreshed execution-profile catalog on a running connection.
|
|
22921
24454
|
*
|
|
@@ -22986,7 +24519,8 @@ var DshHostConnector = class {
|
|
|
22986
24519
|
...executionProfiles === void 0 ? {} : {
|
|
22987
24520
|
executionProfiles: cloneExecutionProfiles(executionProfiles)
|
|
22988
24521
|
},
|
|
22989
|
-
purpose: "execution"
|
|
24522
|
+
purpose: "execution",
|
|
24523
|
+
...this.#host.contextDeletionVersion === 1 ? { contextDeletionVersion: 1 } : {}
|
|
22990
24524
|
};
|
|
22991
24525
|
}
|
|
22992
24526
|
get executionRuntimeId() {
|
|
@@ -23019,6 +24553,7 @@ var DshHostConnector = class {
|
|
|
23019
24553
|
}
|
|
23020
24554
|
uploadLocalTurns() {
|
|
23021
24555
|
return this.#withLocalSyncControl(async () => {
|
|
24556
|
+
await this.#refreshContentRevision();
|
|
23022
24557
|
const before = this.localSyncStatus();
|
|
23023
24558
|
await this.#flushOutbox(true);
|
|
23024
24559
|
await this.#captureLocalTurns(0);
|
|
@@ -23041,6 +24576,15 @@ var DshHostConnector = class {
|
|
|
23041
24576
|
this.#started = true;
|
|
23042
24577
|
this.#automaticPolling = options.schedule !== false;
|
|
23043
24578
|
this.#notifyLifecycle("connecting");
|
|
24579
|
+
if (this.#host.onLiveOutput !== void 0) {
|
|
24580
|
+
this.#listenerDisposers.push(this.#host.onLiveOutput((event) => {
|
|
24581
|
+
const active = this.#state?.activeRequest;
|
|
24582
|
+
const output2 = this.#activeLiveOutput;
|
|
24583
|
+
if (this.#stopped || active === void 0 || output2 === void 0 || event.sourceSessionId !== output2.sourceSessionId || event.sourceSessionId !== (active.contextExecution?.sessionId ?? this.#host.sessionId))
|
|
24584
|
+
return;
|
|
24585
|
+
output2.mapper.onEvent(event);
|
|
24586
|
+
}));
|
|
24587
|
+
}
|
|
23044
24588
|
this.#listenerDisposers.push(this.#host.onSessionEvent((event) => {
|
|
23045
24589
|
const active = this.#state?.activeRequest;
|
|
23046
24590
|
if (active?.contextExecution !== void 0 && event.sourceSessionId !== active.contextExecution.sessionId)
|
|
@@ -23086,6 +24630,7 @@ var DshHostConnector = class {
|
|
|
23086
24630
|
}));
|
|
23087
24631
|
try {
|
|
23088
24632
|
const session = await this.#verifySessionBinding();
|
|
24633
|
+
this.#revisionSupported = session.contentRevision !== void 0;
|
|
23089
24634
|
this.#session = session;
|
|
23090
24635
|
this.#notifyLifecycle("connecting");
|
|
23091
24636
|
const storedState = await this.#stateStore.load();
|
|
@@ -23093,8 +24638,16 @@ var DshHostConnector = class {
|
|
|
23093
24638
|
throw new Error("GatherThread Session head precedes the persisted DSH cursor; refusing identity reuse");
|
|
23094
24639
|
}
|
|
23095
24640
|
const mode = await this.#host.open();
|
|
24641
|
+
if (storedState?.projectionSessionId && (storedState.contentRevision ?? 0) > 0) {
|
|
24642
|
+
if (!this.#host.resetCanonicalContext)
|
|
24643
|
+
throw new Error("DSH deletion-safe projection requires an updated native facade");
|
|
24644
|
+
const restored = await this.#host.resetCanonicalContext(storedState.contentRevision);
|
|
24645
|
+
if (restored.sessionId !== storedState.projectionSessionId)
|
|
24646
|
+
throw new Error("DSH restored another native projection");
|
|
24647
|
+
}
|
|
23096
24648
|
this.#state = this.#reconcileState(storedState, mode);
|
|
23097
24649
|
await this.#stateStore.save(this.#state);
|
|
24650
|
+
await this.#refreshContentRevision();
|
|
23098
24651
|
this.#runtime = await this.#api.registerRuntime(this.#runtimeRegistration());
|
|
23099
24652
|
this.#assertRuntime(this.#runtime);
|
|
23100
24653
|
this.#notifyLifecycle("idle");
|
|
@@ -23166,6 +24719,8 @@ var DshHostConnector = class {
|
|
|
23166
24719
|
stop() {
|
|
23167
24720
|
this.#stopPromise ??= (async () => {
|
|
23168
24721
|
this.#stopped = true;
|
|
24722
|
+
const liveOutput = this.#activeLiveOutput;
|
|
24723
|
+
this.#activeLiveOutput = void 0;
|
|
23169
24724
|
this.#notifyLifecycle("stopped");
|
|
23170
24725
|
this.#lifecycleAbort.abort(new Error("DSH connector stopped"));
|
|
23171
24726
|
if (this.#timer !== void 0) {
|
|
@@ -23179,6 +24734,7 @@ var DshHostConnector = class {
|
|
|
23179
24734
|
for (const dispose of this.#listenerDisposers.splice(0))
|
|
23180
24735
|
dispose();
|
|
23181
24736
|
await this.#host.dispose();
|
|
24737
|
+
void liveOutput?.mapper.finish(false).catch(() => void 0);
|
|
23182
24738
|
try {
|
|
23183
24739
|
await this.#pollPromise;
|
|
23184
24740
|
} catch {
|
|
@@ -23191,6 +24747,7 @@ var DshHostConnector = class {
|
|
|
23191
24747
|
return this.#stopPromise;
|
|
23192
24748
|
}
|
|
23193
24749
|
async #poll() {
|
|
24750
|
+
await this.#refreshContentRevision();
|
|
23194
24751
|
const state = this.#requireState();
|
|
23195
24752
|
if (state.activeRequest !== void 0) {
|
|
23196
24753
|
await this.#withExecutionPermit(() => this.#recoverActiveRequest());
|
|
@@ -23207,6 +24764,12 @@ var DshHostConnector = class {
|
|
|
23207
24764
|
let scanned = 0;
|
|
23208
24765
|
while (!this.#stopped) {
|
|
23209
24766
|
const page = await this.#api.readEvents(this.#config.sessionId, scanCursor, this.#config.pollLimit);
|
|
24767
|
+
if (page.contentRevision !== void 0)
|
|
24768
|
+
this.#revisionSupported = true;
|
|
24769
|
+
if ((page.contentRevision ?? 0) !== (state.contentRevision ?? 0)) {
|
|
24770
|
+
await this.#refreshContentRevision();
|
|
24771
|
+
return this.#poll();
|
|
24772
|
+
}
|
|
23210
24773
|
this.#validatePage(page.events, scanCursor);
|
|
23211
24774
|
const passive = [];
|
|
23212
24775
|
for (const event of page.events) {
|
|
@@ -23240,6 +24803,39 @@ var DshHostConnector = class {
|
|
|
23240
24803
|
}
|
|
23241
24804
|
throw new Error("DSH connector stopped during event replay");
|
|
23242
24805
|
}
|
|
24806
|
+
async #refreshContentRevision() {
|
|
24807
|
+
const state = this.#requireState();
|
|
24808
|
+
if (!this.#revisionSupported && (state.contentRevision ?? 0) === 0)
|
|
24809
|
+
return;
|
|
24810
|
+
const page = await this.#api.readEvents(this.#config.sessionId, 0, 1);
|
|
24811
|
+
const revision = page.contentRevision ?? 0;
|
|
24812
|
+
if (!Number.isSafeInteger(revision) || revision < (state.contentRevision ?? 0))
|
|
24813
|
+
throw new Error("Stale DSH shared context revision");
|
|
24814
|
+
if (revision === (state.contentRevision ?? 0)) {
|
|
24815
|
+
if (revision > 0 && state.projectionSessionId && this.#host.resetCanonicalContext) {
|
|
24816
|
+
const restored = await this.#host.resetCanonicalContext(revision);
|
|
24817
|
+
if (restored.sessionId !== state.projectionSessionId)
|
|
24818
|
+
throw new Error("DSH clean projection binding changed on reconnect");
|
|
24819
|
+
}
|
|
24820
|
+
return;
|
|
24821
|
+
}
|
|
24822
|
+
state.localOnlySessionIds = [.../* @__PURE__ */ new Set([...state.localOnlySessionIds ?? [], this.#config.dshSessionId, this.#host.sessionId])];
|
|
24823
|
+
state.outbox = [];
|
|
24824
|
+
delete state.activeRequest;
|
|
24825
|
+
this.#activeStatuses = [];
|
|
24826
|
+
this.#activeExecutionSelection = void 0;
|
|
24827
|
+
this.#summaryScanSequence = 0;
|
|
24828
|
+
this.#hasSummaryHistory = false;
|
|
24829
|
+
await this.#stateStore.save(state);
|
|
24830
|
+
if (this.#host.resetCanonicalContext) {
|
|
24831
|
+
const replacement = await this.#host.resetCanonicalContext(revision);
|
|
24832
|
+
state.projectionSessionId = replacement.sessionId;
|
|
24833
|
+
state.projectionCursor = 0;
|
|
24834
|
+
state.publishedDshSequence = this.#host.currentSequence();
|
|
24835
|
+
}
|
|
24836
|
+
state.contentRevision = revision;
|
|
24837
|
+
await this.#stateStore.save(state);
|
|
24838
|
+
}
|
|
23243
24839
|
async #executeRequest(request, selection) {
|
|
23244
24840
|
if (this.#stopped)
|
|
23245
24841
|
throw new Error("DSH connector stopped before claiming an Agent request");
|
|
@@ -23258,6 +24854,8 @@ var DshHostConnector = class {
|
|
|
23258
24854
|
await this.#projectCanonicalBatch([request], request.sequence);
|
|
23259
24855
|
return { claimed: false, completed: false };
|
|
23260
24856
|
}
|
|
24857
|
+
if ((claim2.contentRevision ?? state.contentRevision ?? 0) !== (state.contentRevision ?? 0))
|
|
24858
|
+
throw new Error("context_changed");
|
|
23261
24859
|
const quotedEvent = request.replyTo ? (await this.#readThroughRequest(request.id, request.sequence)).find((event) => event.id === request.replyTo) : void 0;
|
|
23262
24860
|
const prompt = buildDshRequestPrompt(request, quotedEvent);
|
|
23263
24861
|
const context = await this.#contextForRequest(request);
|
|
@@ -23270,6 +24868,7 @@ var DshHostConnector = class {
|
|
|
23270
24868
|
if (prepared !== void 0 && prepared.fromSequence !== baseline)
|
|
23271
24869
|
throw new Error("DSH context preparation returned an inconsistent sequence");
|
|
23272
24870
|
state.activeRequest = {
|
|
24871
|
+
contentRevision: state.contentRevision ?? 0,
|
|
23273
24872
|
requestId: request.id,
|
|
23274
24873
|
requestSequence: request.sequence,
|
|
23275
24874
|
dshFromSequence: baseline,
|
|
@@ -23283,7 +24882,7 @@ var DshHostConnector = class {
|
|
|
23283
24882
|
this.#activeExecutionSelection = selection;
|
|
23284
24883
|
this.#liveEventSequences.clear();
|
|
23285
24884
|
await this.#stateStore.save(state);
|
|
23286
|
-
const result = await this.#
|
|
24885
|
+
const result = await this.#promptWithLiveOutput(prompt, selection, prepared?.sessionId);
|
|
23287
24886
|
if (result.fromSequence !== baseline || result.toSequence < result.fromSequence) {
|
|
23288
24887
|
throw new Error("DSH Host prompt returned an inconsistent durable event range");
|
|
23289
24888
|
}
|
|
@@ -23305,7 +24904,7 @@ var DshHostConnector = class {
|
|
|
23305
24904
|
throw new Error("GatherThread history summary request has invalid source metadata");
|
|
23306
24905
|
if (selectedOnly)
|
|
23307
24906
|
this.#hasSummaryHistory = true;
|
|
23308
|
-
if (!selectedOnly && !await this.#sessionHasSummaryBefore(request.sequence))
|
|
24907
|
+
if (!selectedOnly && (this.#requireState().contentRevision ?? 0) === 0 && !await this.#sessionHasSummaryBefore(request.sequence))
|
|
23309
24908
|
return void 0;
|
|
23310
24909
|
if (!selectedOnly && this.#api.readContext === void 0) {
|
|
23311
24910
|
throw new Error("GatherThread summarized context is unavailable; update the connector before continuing");
|
|
@@ -23313,7 +24912,9 @@ var DshHostConnector = class {
|
|
|
23313
24912
|
if (this.#host.prepareContextExecution === void 0) {
|
|
23314
24913
|
throw new Error("DSH isolated context execution is unavailable; refusing to run a summary-aware request in native history");
|
|
23315
24914
|
}
|
|
23316
|
-
const received = selectedOnly ? { view: "original", through_sequence: request.sequence - 1, items: [] } : parseHistoryContext(await this.#api.readContext(this.#config.sessionId, void 0, request.sequence - 1), void 0, request.sequence - 1);
|
|
24915
|
+
const received = selectedOnly ? { view: "original", content_revision: this.#requireState().contentRevision ?? 0, through_sequence: request.sequence - 1, items: [] } : parseHistoryContext(await this.#api.readContext(this.#config.sessionId, void 0, request.sequence - 1), void 0, request.sequence - 1);
|
|
24916
|
+
if ((received.content_revision ?? 0) !== (this.#requireState().contentRevision ?? 0))
|
|
24917
|
+
throw new Error("context_changed");
|
|
23317
24918
|
const historyContext = { ...received, items: received.items.map((item) => ({ ...item, content: redactText(item.content) })) };
|
|
23318
24919
|
return { historyContext, selectedOnly };
|
|
23319
24920
|
}
|
|
@@ -23411,6 +25012,10 @@ var DshHostConnector = class {
|
|
|
23411
25012
|
throw new Error("Active GatherThread request no longer has a recoverable claim");
|
|
23412
25013
|
}
|
|
23413
25014
|
const claimAttempt = claim2.attemptCount ?? active.claimAttempt ?? 1;
|
|
25015
|
+
if ((active.contentRevision ?? 0) !== (state.contentRevision ?? 0) || (claim2.contentRevision ?? active.contentRevision ?? 0) !== (active.contentRevision ?? 0)) {
|
|
25016
|
+
await this.#retireFencedActiveRequest(state, active);
|
|
25017
|
+
return;
|
|
25018
|
+
}
|
|
23414
25019
|
active.claimAttempt = claimAttempt;
|
|
23415
25020
|
if (active.dshToSequence !== void 0) {
|
|
23416
25021
|
state.outbox = state.outbox.map((operation) => {
|
|
@@ -23483,7 +25088,7 @@ var DshHostConnector = class {
|
|
|
23483
25088
|
this.#activeStatuses = [];
|
|
23484
25089
|
this.#liveEventSequences.clear();
|
|
23485
25090
|
await this.#stateStore.save(state);
|
|
23486
|
-
const result = await this.#
|
|
25091
|
+
const result = await this.#promptWithLiveOutput(continuation, selection, execution?.sessionId);
|
|
23487
25092
|
if (result.fromSequence !== baseline || result.toSequence < result.fromSequence) {
|
|
23488
25093
|
throw new Error("DSH Host recovery prompt returned an inconsistent durable event range");
|
|
23489
25094
|
}
|
|
@@ -23512,6 +25117,94 @@ var DshHostConnector = class {
|
|
|
23512
25117
|
await this.#flushOutbox();
|
|
23513
25118
|
await this.#finalizeDeliveredRequest();
|
|
23514
25119
|
}
|
|
25120
|
+
async #promptWithLiveOutput(text, selection, executionSessionId) {
|
|
25121
|
+
const active = this.#requireState().activeRequest;
|
|
25122
|
+
if (active === void 0)
|
|
25123
|
+
throw new Error("No claimed DSH request owns live output");
|
|
25124
|
+
const requestId = active.requestId;
|
|
25125
|
+
const claimAttempt = active.claimAttempt ?? 1;
|
|
25126
|
+
const fromSequence = active.dshFromSequence;
|
|
25127
|
+
const runtime = this.#requireRuntime();
|
|
25128
|
+
const sourceSessionId = executionSessionId ?? this.#host.sessionId;
|
|
25129
|
+
const publisher = createLiveOutputPublisher({
|
|
25130
|
+
publish: async (update) => {
|
|
25131
|
+
const publication = this.#liveProgressPromise.then(async () => {
|
|
25132
|
+
const current = this.#state?.activeRequest;
|
|
25133
|
+
if (this.#stopped || this.#activeLiveOutput?.publisher !== publisher || current?.requestId !== requestId || (current.claimAttempt ?? 1) !== claimAttempt || current.dshFromSequence !== fromSequence || (current.contextExecution?.sessionId ?? this.#host.sessionId) !== sourceSessionId || this.#runtime?.id !== runtime.id || this.#activeExecutionSelection !== selection)
|
|
25134
|
+
return false;
|
|
25135
|
+
await this.#api.appendAgentProgress(this.#config.sessionId, requestId, {
|
|
25136
|
+
runtimeId: runtime.id,
|
|
25137
|
+
claimAttempt,
|
|
25138
|
+
contentRevision: active.contentRevision ?? 0,
|
|
25139
|
+
idempotencyKey: `${this.#config.deviceId}:${digest2(requestId).slice(0, 24)}:${claimAttempt}:live-${update.id}`,
|
|
25140
|
+
payload: { ...liveOutputPayload(update), capture_fidelity: "harness_transcript", source_harness: "deepseek-harness" },
|
|
25141
|
+
observedModel: selection.model,
|
|
25142
|
+
...selection.reasoningEffort === void 0 ? {} : { observedReasoningEffort: selection.reasoningEffort }
|
|
25143
|
+
});
|
|
25144
|
+
return true;
|
|
25145
|
+
});
|
|
25146
|
+
this.#liveProgressPromise = publication.then(() => void 0).catch((error62) => {
|
|
25147
|
+
this.#onBackgroundError?.(publicError(error62));
|
|
25148
|
+
});
|
|
25149
|
+
return publication.catch(() => false);
|
|
25150
|
+
}
|
|
25151
|
+
});
|
|
25152
|
+
const output2 = { publisher, mapper: createDshLiveEventMapper(publisher), sourceSessionId };
|
|
25153
|
+
this.#activeLiveOutput = output2;
|
|
25154
|
+
const abort = new AbortController();
|
|
25155
|
+
const abortLifecycle = () => abort.abort(this.#lifecycleAbort.signal.reason);
|
|
25156
|
+
this.#lifecycleAbort.signal.addEventListener("abort", abortLifecycle, { once: true });
|
|
25157
|
+
const verifyRevision = async () => {
|
|
25158
|
+
if (!this.#revisionSupported && (active.contentRevision ?? 0) === 0)
|
|
25159
|
+
return;
|
|
25160
|
+
try {
|
|
25161
|
+
const page = await this.#api.readEvents(this.#config.sessionId, 0, 1);
|
|
25162
|
+
if ((page.contentRevision ?? 0) !== (active.contentRevision ?? 0))
|
|
25163
|
+
abort.abort(new Error("context_changed"));
|
|
25164
|
+
} catch (error62) {
|
|
25165
|
+
abort.abort(error62);
|
|
25166
|
+
}
|
|
25167
|
+
};
|
|
25168
|
+
const timer = setInterval(() => {
|
|
25169
|
+
if (this.#revisionPollPromise)
|
|
25170
|
+
return;
|
|
25171
|
+
const poll = verifyRevision();
|
|
25172
|
+
this.#revisionPollPromise = poll;
|
|
25173
|
+
void poll.finally(() => {
|
|
25174
|
+
if (this.#revisionPollPromise === poll)
|
|
25175
|
+
this.#revisionPollPromise = void 0;
|
|
25176
|
+
});
|
|
25177
|
+
}, 1e3);
|
|
25178
|
+
timer.unref();
|
|
25179
|
+
try {
|
|
25180
|
+
await verifyRevision();
|
|
25181
|
+
abort.signal.throwIfAborted();
|
|
25182
|
+
const result = await this.#host.prompt(text, selection, executionSessionId, abort.signal);
|
|
25183
|
+
await verifyRevision();
|
|
25184
|
+
abort.signal.throwIfAborted();
|
|
25185
|
+
await output2.mapper.finish();
|
|
25186
|
+
return result;
|
|
25187
|
+
} catch (error62) {
|
|
25188
|
+
if (abort.signal.aborted) {
|
|
25189
|
+
const state = this.#requireState();
|
|
25190
|
+
state.outbox = state.outbox.filter((operation) => !outboxBelongsToRequest(operation, requestId));
|
|
25191
|
+
delete state.activeRequest;
|
|
25192
|
+
this.#activeExecutionSelection = void 0;
|
|
25193
|
+
await this.#stateStore.save(state);
|
|
25194
|
+
}
|
|
25195
|
+
throw error62;
|
|
25196
|
+
} finally {
|
|
25197
|
+
clearInterval(timer);
|
|
25198
|
+
this.#lifecycleAbort.signal.removeEventListener("abort", abortLifecycle);
|
|
25199
|
+
await this.#revisionPollPromise;
|
|
25200
|
+
if (this.#activeLiveOutput === output2)
|
|
25201
|
+
this.#activeLiveOutput = void 0;
|
|
25202
|
+
if (this.#stopped)
|
|
25203
|
+
void output2.mapper.finish(false).catch(() => void 0);
|
|
25204
|
+
else
|
|
25205
|
+
await output2.mapper.finish(false);
|
|
25206
|
+
}
|
|
25207
|
+
}
|
|
23515
25208
|
#queueLiveProgress(idSuffix, payload, throttle = true) {
|
|
23516
25209
|
const active = this.#state?.activeRequest;
|
|
23517
25210
|
const runtime = this.#runtime;
|
|
@@ -23521,6 +25214,9 @@ var DshHostConnector = class {
|
|
|
23521
25214
|
const claimAttempt = active.claimAttempt ?? 1;
|
|
23522
25215
|
const progressKey = `${requestId}:${String(claimAttempt)}`;
|
|
23523
25216
|
const now = Date.now();
|
|
25217
|
+
const streamedAt = this.#activeLiveOutput?.publisher.lastPublishedAt();
|
|
25218
|
+
if (throttle && streamedAt !== void 0 && now - streamedAt < LIVE_PROGRESS_MIN_INTERVAL_MS)
|
|
25219
|
+
return;
|
|
23524
25220
|
if (throttle && this.#lastLiveProgressKey === progressKey && now - this.#lastLiveProgressAt < LIVE_PROGRESS_MIN_INTERVAL_MS) {
|
|
23525
25221
|
return;
|
|
23526
25222
|
}
|
|
@@ -23531,7 +25227,7 @@ var DshHostConnector = class {
|
|
|
23531
25227
|
const idempotencyKey = `${this.#config.deviceId}:${digest2(requestId).slice(0, 24)}:${String(claimAttempt)}:${idSuffix}`;
|
|
23532
25228
|
this.#liveProgressPromise = this.#liveProgressPromise.then(async () => {
|
|
23533
25229
|
const current = this.#state?.activeRequest;
|
|
23534
|
-
if (current?.requestId !== requestId || (current.claimAttempt ?? 1) !== claimAttempt)
|
|
25230
|
+
if (this.#stopped || current?.requestId !== requestId || (current.claimAttempt ?? 1) !== claimAttempt || this.#runtime?.id !== runtime.id)
|
|
23535
25231
|
return;
|
|
23536
25232
|
const selection = this.#activeExecutionSelection;
|
|
23537
25233
|
if (selection === void 0)
|
|
@@ -23539,6 +25235,7 @@ var DshHostConnector = class {
|
|
|
23539
25235
|
await this.#api.appendAgentProgress(this.#config.sessionId, requestId, {
|
|
23540
25236
|
runtimeId: runtime.id,
|
|
23541
25237
|
claimAttempt,
|
|
25238
|
+
contentRevision: active.contentRevision ?? 0,
|
|
23542
25239
|
idempotencyKey,
|
|
23543
25240
|
payload,
|
|
23544
25241
|
observedModel: selection.model,
|
|
@@ -23556,11 +25253,15 @@ var DshHostConnector = class {
|
|
|
23556
25253
|
}
|
|
23557
25254
|
async #projectCanonicalBatch(events, throughSequence) {
|
|
23558
25255
|
const state = this.#requireState();
|
|
25256
|
+
if (state.localOnlySessionIds?.includes(this.#host.sessionId))
|
|
25257
|
+
return;
|
|
23559
25258
|
if (throughSequence <= state.projectionCursor)
|
|
23560
25259
|
return;
|
|
23561
25260
|
const runtime = this.#requireRuntime();
|
|
23562
25261
|
const projections = [];
|
|
23563
25262
|
for (const event of events) {
|
|
25263
|
+
if (event.deleted)
|
|
25264
|
+
continue;
|
|
23564
25265
|
if (event.type === "agent_request" && asObject3(event.payload)?.history_summary !== void 0) {
|
|
23565
25266
|
this.#hasSummaryHistory = true;
|
|
23566
25267
|
continue;
|
|
@@ -23577,7 +25278,7 @@ var DshHostConnector = class {
|
|
|
23577
25278
|
role: "user",
|
|
23578
25279
|
content,
|
|
23579
25280
|
occurredAt: event.timestamp,
|
|
23580
|
-
...event.actorDisplayName === void 0 ? {} : {
|
|
25281
|
+
...this.#session?.mode !== "multi" || event.actorDisplayName === void 0 ? {} : {
|
|
23581
25282
|
actorDisplayName: event.actorDisplayName
|
|
23582
25283
|
}
|
|
23583
25284
|
});
|
|
@@ -23605,6 +25306,8 @@ var DshHostConnector = class {
|
|
|
23605
25306
|
}
|
|
23606
25307
|
async #captureLocalTurns(basedOnSequence) {
|
|
23607
25308
|
const state = this.#requireState();
|
|
25309
|
+
if (state.localOnlySessionIds?.includes(this.#host.sessionId))
|
|
25310
|
+
return;
|
|
23608
25311
|
if (state.activeRequest !== void 0 || state.outbox.length > 0)
|
|
23609
25312
|
return;
|
|
23610
25313
|
const current = this.#host.currentSequence();
|
|
@@ -23621,6 +25324,7 @@ var DshHostConnector = class {
|
|
|
23621
25324
|
input: {
|
|
23622
25325
|
localTurnId: turn.localTurnId,
|
|
23623
25326
|
runtimeId: runtime.id,
|
|
25327
|
+
contentRevision: state.contentRevision ?? 0,
|
|
23624
25328
|
basedOnSequence: basedOnSequence ?? state.serverCursor,
|
|
23625
25329
|
occurredAt: turn.occurredAt,
|
|
23626
25330
|
observedModel: this.#config.model,
|
|
@@ -23734,7 +25438,10 @@ var DshHostConnector = class {
|
|
|
23734
25438
|
}
|
|
23735
25439
|
}
|
|
23736
25440
|
});
|
|
23737
|
-
return operations
|
|
25441
|
+
return operations.map((operation) => ({
|
|
25442
|
+
...operation,
|
|
25443
|
+
input: { ...operation.input, contentRevision: this.#requireState().activeRequest?.contentRevision ?? 0 }
|
|
25444
|
+
}));
|
|
23738
25445
|
}
|
|
23739
25446
|
async #flushOutbox(includeLocalTurns = true) {
|
|
23740
25447
|
const state = this.#requireState();
|
|
@@ -23743,6 +25450,11 @@ var DshHostConnector = class {
|
|
|
23743
25450
|
const operation = state.outbox[0];
|
|
23744
25451
|
if (operation === void 0)
|
|
23745
25452
|
break;
|
|
25453
|
+
if ((operation.input.contentRevision ?? 0) !== (state.contentRevision ?? 0) || operation.kind === "local_turn" && state.localOnlySessionIds?.includes(this.#host.sessionId)) {
|
|
25454
|
+
state.outbox.shift();
|
|
25455
|
+
await this.#stateStore.save(state);
|
|
25456
|
+
continue;
|
|
25457
|
+
}
|
|
23746
25458
|
if (operation.kind === "local_turn" && !includeLocalTurns)
|
|
23747
25459
|
break;
|
|
23748
25460
|
if (operation.input.runtimeId !== runtime.id) {
|
|
@@ -24011,7 +25723,7 @@ function turnSettlement(events) {
|
|
|
24011
25723
|
return typeof reason?.kind === "string" ? reason.kind : void 0;
|
|
24012
25724
|
}
|
|
24013
25725
|
function digest2(value) {
|
|
24014
|
-
return
|
|
25726
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
24015
25727
|
}
|
|
24016
25728
|
function cloneExecutionProfiles(profiles) {
|
|
24017
25729
|
return profiles.map((profile) => ({
|
|
@@ -24077,6 +25789,8 @@ function captureCompletedLocalTurns(events, fromSequence, dshSessionId) {
|
|
|
24077
25789
|
return { turns, safeThroughSequence };
|
|
24078
25790
|
}
|
|
24079
25791
|
function hasCompletedDshLocalTurn(events, dshSessionId) {
|
|
25792
|
+
if (events.some((event) => event.type === "user/message" && /^gatherthread-retired:[0-9]+$/.test(String(asObject3(event.data)?.id)) && asObject3(asObject3(event.data)?.source)?.plugin === "gatherthread"))
|
|
25793
|
+
return false;
|
|
24080
25794
|
return captureCompletedLocalTurns(events, 0, dshSessionId).turns.length > 0;
|
|
24081
25795
|
}
|
|
24082
25796
|
function isDirectLocalUserMessage(event) {
|
|
@@ -24147,16 +25861,16 @@ function asObject3(value) {
|
|
|
24147
25861
|
}
|
|
24148
25862
|
|
|
24149
25863
|
// dist/src/dsh-compat.js
|
|
24150
|
-
import { createHash as
|
|
25864
|
+
import { createHash as createHash7, randomUUID as randomUUID5 } from "node:crypto";
|
|
24151
25865
|
var DSH_COMPATIBILITY = Object.freeze({
|
|
24152
|
-
tag: "dsh-v0.1.
|
|
24153
|
-
version: "0.1.
|
|
24154
|
-
commit: "
|
|
25866
|
+
tag: "dsh-v0.1.7-rc.2",
|
|
25867
|
+
version: "0.1.7-rc.2",
|
|
25868
|
+
commit: "477b4f420553e8a52c2fbccc464d7561b239c443",
|
|
24155
25869
|
profile: "headless"
|
|
24156
25870
|
});
|
|
24157
25871
|
var DSH_NPM_COMPATIBILITY = Object.freeze({
|
|
24158
25872
|
package: "@deepseek-ai/dsh",
|
|
24159
|
-
version: "0.1.
|
|
25873
|
+
version: "0.1.7-rc.2",
|
|
24160
25874
|
profile: "web",
|
|
24161
25875
|
persistenceProbe: "list"
|
|
24162
25876
|
});
|
|
@@ -24186,16 +25900,19 @@ function createDshHostFacade(options) {
|
|
|
24186
25900
|
const sessionTitle = options.sessionTitle === void 0 ? void 0 : requireService(context, "sessionTitle", ["rename"]);
|
|
24187
25901
|
const workspaceRegistry = options.workspaceTitle === void 0 ? void 0 : requireService(context, "workspaceRegistry", ["create"]);
|
|
24188
25902
|
const eventSubscribers = /* @__PURE__ */ new Set();
|
|
25903
|
+
const liveSubscribers = /* @__PURE__ */ new Set();
|
|
24189
25904
|
const statusSubscribers = /* @__PURE__ */ new Set();
|
|
24190
25905
|
const listenerDisposers = [];
|
|
24191
25906
|
const lifecycleAbort = new AbortController();
|
|
24192
25907
|
let handle;
|
|
24193
25908
|
let executionHandle;
|
|
24194
|
-
let
|
|
25909
|
+
let nativeSessionId = options.sessionId;
|
|
25910
|
+
let contentRevision = 0;
|
|
25911
|
+
let contextOperationPromise;
|
|
24195
25912
|
const executionBinding = JSON.stringify({ nativeSessionId: options.sessionId, ...options.contextBinding });
|
|
24196
|
-
|
|
24197
|
-
|
|
24198
|
-
|
|
25913
|
+
let executionSessionId = `gatherthread-execution-${createHash7("sha256").update(executionBinding).digest("hex").slice(0, 32)}`;
|
|
25914
|
+
let executionOwnerId = `gatherthread-execution-owner:${createHash7("sha256").update(executionBinding).digest("hex")}`;
|
|
25915
|
+
let executionOwnerText = JSON.stringify({
|
|
24199
25916
|
version: 1,
|
|
24200
25917
|
owner: "gatherthread",
|
|
24201
25918
|
native_session_id: options.sessionId,
|
|
@@ -24208,19 +25925,76 @@ function createDshHostFacade(options) {
|
|
|
24208
25925
|
let knownProjectionMessageIds;
|
|
24209
25926
|
let disposePromise;
|
|
24210
25927
|
let disposed = false;
|
|
25928
|
+
let livePrompt;
|
|
25929
|
+
const emitLive = (owner, event) => {
|
|
25930
|
+
if (disposed || livePrompt !== owner || owner.closed)
|
|
25931
|
+
return;
|
|
25932
|
+
for (const subscriber of liveSubscribers)
|
|
25933
|
+
subscriber(event);
|
|
25934
|
+
};
|
|
24211
25935
|
listenerDisposers.push(context.on("session/event", (...args) => {
|
|
24212
25936
|
const session = asSession(args[0]);
|
|
24213
25937
|
const event = asSessionEvent(args[1]);
|
|
24214
|
-
if (session === void 0 || session.id !==
|
|
25938
|
+
if (session === void 0 || session.id !== nativeSessionId && session.id !== executionHandle?.agent.session.id || event === void 0)
|
|
24215
25939
|
return;
|
|
24216
25940
|
for (const subscriber of eventSubscribers)
|
|
24217
25941
|
subscriber({ ...event, sourceSessionId: session.id });
|
|
25942
|
+
const owner = livePrompt;
|
|
25943
|
+
if (owner === void 0 || owner.closed || session !== owner.agent.session)
|
|
25944
|
+
return;
|
|
25945
|
+
const data = asObject4(event.data);
|
|
25946
|
+
if (event.type === "turn/start" && owner.turn === void 0 && Number.isSafeInteger(data?.turn)) {
|
|
25947
|
+
owner.turn = Number(data.turn);
|
|
25948
|
+
}
|
|
25949
|
+
if (owner.turn === void 0 || data?.turn !== void 0 && data.turn !== owner.turn)
|
|
25950
|
+
return;
|
|
25951
|
+
if (event.type === "step/start" && Number.isSafeInteger(data?.step))
|
|
25952
|
+
owner.step = Number(data.step);
|
|
25953
|
+
if (["assistant/message", "assistant/attempt", "tool/call", "tool/result", "todo/write"].includes(event.type)) {
|
|
25954
|
+
emitLive(owner, { kind: "session-event", sourceSessionId: session.id, event });
|
|
25955
|
+
}
|
|
25956
|
+
if (event.type === "turn/end")
|
|
25957
|
+
owner.closed = true;
|
|
25958
|
+
}, { global: true }));
|
|
25959
|
+
listenerDisposers.push(context.on("agent/assistant-stream", (...args) => {
|
|
25960
|
+
const owner = livePrompt;
|
|
25961
|
+
const payload = asObject4(args[0]);
|
|
25962
|
+
if (owner === void 0 || !owner.nativeFrames || owner.turn === void 0 || payload?.agent !== owner.agent)
|
|
25963
|
+
return;
|
|
25964
|
+
const frame = parseDshAssistantStreamFrame(payload.frame);
|
|
25965
|
+
if (frame === void 0 || frame.type === "start" && frame.turn !== owner.turn)
|
|
25966
|
+
return;
|
|
25967
|
+
emitLive(owner, { kind: "assistant-stream", sourceSessionId: owner.agent.session.id, frame });
|
|
25968
|
+
}, { global: true }));
|
|
25969
|
+
listenerDisposers.push(context.on("llm/stream", (...args) => {
|
|
25970
|
+
const request = asObject4(args[0]);
|
|
25971
|
+
const next = args[1];
|
|
25972
|
+
if (typeof next !== "function")
|
|
25973
|
+
return;
|
|
25974
|
+
const stream = next();
|
|
25975
|
+
const owner = livePrompt;
|
|
25976
|
+
if (owner === void 0 || owner.nativeFrames || owner.closed || owner.turn === void 0 || owner.step === void 0 || owner.isLoopRequest === void 0 || request === void 0 || !Object.isFrozen(args[0]) || request.sessionId !== owner.agent.session.id || request.provider !== owner.provider || request.model !== owner.model || typeof agents.get !== "function" || agents.get(owner.agent.session.id) !== owner.agent || !owner.isLoopRequest(args[0]))
|
|
25977
|
+
return stream;
|
|
25978
|
+
const attemptId = randomUUID5();
|
|
25979
|
+
const publish = (frame) => emitLive(owner, {
|
|
25980
|
+
kind: "assistant-stream",
|
|
25981
|
+
sourceSessionId: owner.agent.session.id,
|
|
25982
|
+
frame
|
|
25983
|
+
});
|
|
25984
|
+
return (async function* () {
|
|
25985
|
+
let index = 0;
|
|
25986
|
+
publish({ type: "start", attemptId, revision: ++owner.revision, turn: owner.turn, step: owner.step });
|
|
25987
|
+
for await (const chunk of stream) {
|
|
25988
|
+
publish({ type: "chunk", attemptId, revision: ++owner.revision, index: index++, time: Date.now(), chunk });
|
|
25989
|
+
yield chunk;
|
|
25990
|
+
}
|
|
25991
|
+
})();
|
|
24218
25992
|
}, { global: true }));
|
|
24219
25993
|
listenerDisposers.push(context.on("agent/status", (...args) => {
|
|
24220
25994
|
const payload = asObject4(args[0]);
|
|
24221
25995
|
const agent = payload ? asAgent(payload.agent) : void 0;
|
|
24222
25996
|
const status = payload?.status;
|
|
24223
|
-
if (agent === void 0 || agent.session.id !==
|
|
25997
|
+
if (agent === void 0 || agent.session.id !== nativeSessionId && agent.session.id !== executionHandle?.agent.session.id || status !== "running" && status !== "idle")
|
|
24224
25998
|
return;
|
|
24225
25999
|
for (const subscriber of statusSubscribers)
|
|
24226
26000
|
subscriber(status, agent.session.id);
|
|
@@ -24232,7 +26006,7 @@ function createDshHostFacade(options) {
|
|
|
24232
26006
|
await releaseHandle();
|
|
24233
26007
|
throw new Error("DSH host facade was disposed during open");
|
|
24234
26008
|
}
|
|
24235
|
-
if (acquired.agent.session.id !==
|
|
26009
|
+
if (acquired.agent.session.id !== nativeSessionId) {
|
|
24236
26010
|
await releaseHandle();
|
|
24237
26011
|
throw new Error("DSH Host returned an agent for an unexpected Session identity");
|
|
24238
26012
|
}
|
|
@@ -24259,13 +26033,13 @@ function createDshHostFacade(options) {
|
|
|
24259
26033
|
const parent = requireAgent(handle);
|
|
24260
26034
|
if (parent.status !== "idle")
|
|
24261
26035
|
throw new Error("Native DSH Agent must be idle before isolated context execution");
|
|
24262
|
-
if (parent.session.header?.cwd !== options.workspacePath || agents.get?.(
|
|
26036
|
+
if (parent.session.header?.cwd !== options.workspacePath || agents.get?.(nativeSessionId) !== parent) {
|
|
24263
26037
|
throw new Error("Native DSH workspace or live owner does not match the isolated context binding");
|
|
24264
26038
|
}
|
|
24265
26039
|
};
|
|
24266
26040
|
const assertExecutionOwnership = (agent) => {
|
|
24267
26041
|
const session = agent.session;
|
|
24268
|
-
if (session.id !== executionSessionId || session.header?.cwd !== options.workspacePath || session.header.parentSession !==
|
|
26042
|
+
if (session.id !== executionSessionId || session.header?.cwd !== options.workspacePath || session.header.parentSession !== nativeSessionId || session.header.origin !== "subagent") {
|
|
24269
26043
|
throw new Error("DSH isolated execution Session ownership does not match this native binding");
|
|
24270
26044
|
}
|
|
24271
26045
|
if (agents.get !== void 0 && agents.get(executionSessionId) !== agent) {
|
|
@@ -24286,6 +26060,118 @@ function createDshHostFacade(options) {
|
|
|
24286
26060
|
throw new Error("DSH isolated execution Session contains local manual input; refusing to replace or execute it");
|
|
24287
26061
|
}
|
|
24288
26062
|
};
|
|
26063
|
+
const recordedRevision = (session) => session.snapshotEvents(0).reduce((highest, value) => {
|
|
26064
|
+
const event = asObject4(value), data = asObject4(event?.data);
|
|
26065
|
+
const id = data?.id;
|
|
26066
|
+
if (event?.type !== "user/message" || typeof id !== "string" || !/^gatherthread-retired:[0-9]+$/.test(id) || asObject4(data?.source)?.kind !== "plugin" || asObject4(data?.source)?.plugin !== "gatherthread")
|
|
26067
|
+
return highest;
|
|
26068
|
+
const revision = Number(id.slice("gatherthread-retired:".length));
|
|
26069
|
+
return Number.isSafeInteger(revision) ? Math.max(highest, revision) : highest;
|
|
26070
|
+
}, 0);
|
|
26071
|
+
const resetCanonicalContext = async (revision) => {
|
|
26072
|
+
if (!Number.isSafeInteger(revision) || revision < 1 || revision < contentRevision)
|
|
26073
|
+
throw new Error("Stale DSH content revision");
|
|
26074
|
+
if (options.contextBinding === void 0 || Object.values(options.contextBinding).some((value) => !value) || typeof agents.get !== "function")
|
|
26075
|
+
throw new Error("DSH context reset requires an explicit owned GatherThread binding");
|
|
26076
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26077
|
+
await open7();
|
|
26078
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26079
|
+
if (promptActive || projectionActive)
|
|
26080
|
+
throw new Error("Wait for the active DSH turn before replacing its projection");
|
|
26081
|
+
const parent = requireAgent(handle);
|
|
26082
|
+
if (revision < recordedRevision(parent.session))
|
|
26083
|
+
throw new Error("Retired DSH revision cannot resume");
|
|
26084
|
+
assertExecutionParentIdle();
|
|
26085
|
+
const targetId = `gatherthread-projection-${createHash7("sha256").update(`${executionBinding}:${revision}`).digest("hex").slice(0, 32)}`;
|
|
26086
|
+
if (nativeSessionId === targetId && contentRevision === revision)
|
|
26087
|
+
return { sessionId: targetId };
|
|
26088
|
+
const ownerId = `gatherthread-projection-owner:${createHash7("sha256").update(`${executionBinding}:${revision}`).digest("hex")}`;
|
|
26089
|
+
const ownerText = JSON.stringify({ version: 1, binding: options.contextBinding, native_session_id: options.sessionId, content_revision: revision });
|
|
26090
|
+
projectionActive = true;
|
|
26091
|
+
let candidate;
|
|
26092
|
+
try {
|
|
26093
|
+
const retired = await createIsolatedMessage(`gatherthread-retired:${revision}`, "GatherThread retired this context. Continue here locally only.", options);
|
|
26094
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26095
|
+
if (recordedRevision(parent.session) < revision) {
|
|
26096
|
+
parent.session.append("user/message", retired, { surfaceOp: "append" });
|
|
26097
|
+
await sessions.flush(parent.session);
|
|
26098
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26099
|
+
}
|
|
26100
|
+
if (agents.get?.(targetId) !== void 0)
|
|
26101
|
+
throw new Error("DSH clean projection already has another live owner");
|
|
26102
|
+
const stored = await hasPersistedSession(persistence, targetId, lifecycleAbort.signal);
|
|
26103
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26104
|
+
const presets = asObject4(context.get("agentPresets"));
|
|
26105
|
+
const setup = (ctx) => {
|
|
26106
|
+
if (presets !== void 0) {
|
|
26107
|
+
if (typeof presets.composeFrom !== "function" || typeof presets.composedPreset !== "function")
|
|
26108
|
+
throw new Error("Native DSH tool inheritance unavailable");
|
|
26109
|
+
const expected = presets.composedPreset(parent.ctx);
|
|
26110
|
+
if (presets.composeFrom(ctx, parent.ctx) !== expected)
|
|
26111
|
+
throw new Error("DSH clean projection did not inherit native tools");
|
|
26112
|
+
}
|
|
26113
|
+
};
|
|
26114
|
+
candidate = await (stored ? agents.resume({
|
|
26115
|
+
resumeSessionId: targetId,
|
|
26116
|
+
agentOptions: { provider: options.provider, model: options.model },
|
|
26117
|
+
signal: lifecycleAbort.signal,
|
|
26118
|
+
setup
|
|
26119
|
+
}) : agents.create({
|
|
26120
|
+
sessionId: targetId,
|
|
26121
|
+
meta: { cwd: options.workspacePath, parentSession: options.sessionId, origin: "subagent" },
|
|
26122
|
+
agentOptions: { provider: options.provider, model: options.model },
|
|
26123
|
+
signal: lifecycleAbort.signal,
|
|
26124
|
+
setup
|
|
26125
|
+
}));
|
|
26126
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26127
|
+
const agent = candidate.agent;
|
|
26128
|
+
if (agent.session.id !== targetId || agent.session.header?.cwd !== options.workspacePath || agent.status !== "idle" || agent.session.header.parentSession !== options.sessionId || agent.session.header.origin !== "subagent" || agents.get?.(targetId) !== agent)
|
|
26129
|
+
throw new Error("DSH clean projection ownership is invalid");
|
|
26130
|
+
if (!stored) {
|
|
26131
|
+
if (agent.session.seq !== 0)
|
|
26132
|
+
throw new Error("DSH clean projection is not fresh");
|
|
26133
|
+
const owner = await createIsolatedMessage(ownerId, ownerText, options);
|
|
26134
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26135
|
+
agent.session.append("user/message", owner, { surfaceOp: "append" });
|
|
26136
|
+
}
|
|
26137
|
+
const events = agent.session.snapshotEvents(0).map(asObject4);
|
|
26138
|
+
if (!events.some((event) => {
|
|
26139
|
+
const data = asObject4(event?.data), source = asObject4(data?.source);
|
|
26140
|
+
return event?.type === "user/message" && data?.id === ownerId && source?.kind === "plugin" && source.plugin === "gatherthread" && Array.isArray(data?.content) && data.content.length === 1 && asObject4(data.content[0])?.text === ownerText;
|
|
26141
|
+
})) {
|
|
26142
|
+
throw new Error("DSH clean projection is missing its durable owner marker");
|
|
26143
|
+
}
|
|
26144
|
+
await sessions.flush(agent.session);
|
|
26145
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26146
|
+
if (sessionTitle && options.sessionTitle) {
|
|
26147
|
+
await sessionTitle.rename(agent.session, options.sessionTitle);
|
|
26148
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26149
|
+
}
|
|
26150
|
+
if (workspaceRegistry && options.workspaceTitle) {
|
|
26151
|
+
const workspace = await workspaceRegistry.create(options.workspacePath, options.workspaceTitle);
|
|
26152
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26153
|
+
await workspace.attachSession(targetId);
|
|
26154
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26155
|
+
}
|
|
26156
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26157
|
+
await releaseExecutionHandle();
|
|
26158
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26159
|
+
await releaseHandle();
|
|
26160
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26161
|
+
handle = candidate;
|
|
26162
|
+
candidate = void 0;
|
|
26163
|
+
nativeSessionId = targetId;
|
|
26164
|
+
contentRevision = revision;
|
|
26165
|
+
knownProjectionMessageIds = void 0;
|
|
26166
|
+
return { sessionId: targetId };
|
|
26167
|
+
} finally {
|
|
26168
|
+
try {
|
|
26169
|
+
await candidate?.dispose();
|
|
26170
|
+
} finally {
|
|
26171
|
+
projectionActive = false;
|
|
26172
|
+
}
|
|
26173
|
+
}
|
|
26174
|
+
};
|
|
24289
26175
|
const prepareContextExecution = async (input2) => {
|
|
24290
26176
|
if (disposed)
|
|
24291
26177
|
throw new Error("DSH host facade is disposed");
|
|
@@ -24302,10 +26188,34 @@ function createDshHostFacade(options) {
|
|
|
24302
26188
|
if (input2.selectedOnly && historyContext.items.length !== 0) {
|
|
24303
26189
|
throw new Error("Selected-only summary execution cannot include other shared history");
|
|
24304
26190
|
}
|
|
24305
|
-
await
|
|
26191
|
+
await open7();
|
|
24306
26192
|
if (disposed || promptActive || projectionActive)
|
|
24307
26193
|
throw new Error("DSH host facade permits only one active write");
|
|
24308
26194
|
const parent = requireAgent(handle);
|
|
26195
|
+
const revision = historyContext.content_revision ?? 0;
|
|
26196
|
+
if (revision < Math.max(contentRevision, recordedRevision(parent.session)))
|
|
26197
|
+
throw new Error("Stale retired DSH context revision");
|
|
26198
|
+
if (revision !== contentRevision || revision > 0 && !executionSessionId.endsWith(createHash7("sha256").update(`${executionBinding}:${revision}`).digest("hex").slice(0, 32))) {
|
|
26199
|
+
await releaseExecutionHandle();
|
|
26200
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26201
|
+
contentRevision = revision;
|
|
26202
|
+
const binding = `${executionBinding}:${revision}`;
|
|
26203
|
+
executionSessionId = `gatherthread-execution-${createHash7("sha256").update(binding).digest("hex").slice(0, 32)}`;
|
|
26204
|
+
executionOwnerId = `gatherthread-execution-owner:${createHash7("sha256").update(binding).digest("hex")}`;
|
|
26205
|
+
executionOwnerText = JSON.stringify({
|
|
26206
|
+
version: 1,
|
|
26207
|
+
owner: "gatherthread",
|
|
26208
|
+
native_session_id: options.sessionId,
|
|
26209
|
+
execution_session_id: executionSessionId,
|
|
26210
|
+
binding: options.contextBinding,
|
|
26211
|
+
content_revision: revision
|
|
26212
|
+
});
|
|
26213
|
+
const marker = await createIsolatedMessage(`gatherthread-retired:${revision}`, "GatherThread previous execution context is local only.", options);
|
|
26214
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26215
|
+
parent.session.append("user/message", marker, { surfaceOp: "append" });
|
|
26216
|
+
await sessions.flush(parent.session);
|
|
26217
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
26218
|
+
}
|
|
24309
26219
|
assertExecutionParentIdle();
|
|
24310
26220
|
const presets = asObject4(context.get("agentPresets"));
|
|
24311
26221
|
let parentPreset;
|
|
@@ -24314,7 +26224,8 @@ function createDshHostFacade(options) {
|
|
|
24314
26224
|
throw new Error("Pinned DSH public preset inheritance API is unavailable; refusing an isolated Agent without native tools");
|
|
24315
26225
|
}
|
|
24316
26226
|
parentPreset = presets.composedPreset(parent.ctx);
|
|
24317
|
-
|
|
26227
|
+
const roots = Array.isArray(presets.roots) ? presets.roots : void 0;
|
|
26228
|
+
if (parentPreset === void 0 && roots !== void 0 && roots.length > 0) {
|
|
24318
26229
|
throw new Error("Native DSH Agent has no composed preset; restore its tool composition before using summarized context");
|
|
24319
26230
|
}
|
|
24320
26231
|
}
|
|
@@ -24323,7 +26234,7 @@ function createDshHostFacade(options) {
|
|
|
24323
26234
|
throw new Error("DSH isolated Agent did not inherit its native Agent's exact tool composition");
|
|
24324
26235
|
}
|
|
24325
26236
|
};
|
|
24326
|
-
const markerId = `gatherthread-context:${
|
|
26237
|
+
const markerId = `gatherthread-context:${createHash7("sha256").update(JSON.stringify({
|
|
24327
26238
|
requestId: input2.requestId,
|
|
24328
26239
|
requestSequence: input2.requestSequence,
|
|
24329
26240
|
selectedOnly: input2.selectedOnly,
|
|
@@ -24348,7 +26259,7 @@ function createDshHostFacade(options) {
|
|
|
24348
26259
|
sessionId: executionSessionId,
|
|
24349
26260
|
meta: {
|
|
24350
26261
|
cwd: options.workspacePath,
|
|
24351
|
-
parentSession:
|
|
26262
|
+
parentSession: nativeSessionId,
|
|
24352
26263
|
origin: "subagent",
|
|
24353
26264
|
...parentPreset === void 0 ? {} : { agentPreset: parentPreset }
|
|
24354
26265
|
},
|
|
@@ -24362,7 +26273,7 @@ function createDshHostFacade(options) {
|
|
|
24362
26273
|
}
|
|
24363
26274
|
const agent2 = operationAgent(executionSessionId);
|
|
24364
26275
|
if (!stored) {
|
|
24365
|
-
if (agent2.session.id !== executionSessionId || agent2.session.seq !== 0 || agent2.session.header?.parentSession !==
|
|
26276
|
+
if (agent2.session.id !== executionSessionId || agent2.session.seq !== 0 || agent2.session.header?.parentSession !== nativeSessionId || agent2.session.header.origin !== "subagent" || agent2.session.header.cwd !== options.workspacePath || agent2.status !== "idle") {
|
|
24366
26277
|
throw new Error("DSH did not create a fresh, owned isolated execution Session");
|
|
24367
26278
|
}
|
|
24368
26279
|
const ownerMessage = await createIsolatedMessage(executionOwnerId, executionOwnerText, options);
|
|
@@ -24398,7 +26309,7 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24398
26309
|
const nodes = agent.session.surface?.nodes;
|
|
24399
26310
|
if (!Array.isArray(nodes) || nodes.some((seq) => !Number.isSafeInteger(seq) || seq < 0 || seq >= agent.session.seq) || new Set(nodes).size !== nodes.length)
|
|
24400
26311
|
throw new Error("Pinned DSH public Session surface API is unavailable or invalid");
|
|
24401
|
-
agent.session.append("user/message", message, nodes.length === 0 ? { surfaceOp: "append" } : { surfaceOp: { op: "replace",
|
|
26312
|
+
agent.session.append("user/message", message, nodes.length === 0 ? { surfaceOp: "append" } : { surfaceOp: { op: "replace", startSeq: nodes[0], endSeq: nodes.at(-1) }, sourceEventSeqs: [...nodes] });
|
|
24402
26313
|
}
|
|
24403
26314
|
await sessions.flush(agent.session);
|
|
24404
26315
|
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
@@ -24407,11 +26318,11 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24407
26318
|
projectionActive = false;
|
|
24408
26319
|
}
|
|
24409
26320
|
};
|
|
24410
|
-
const setupNativeAgent = async (agentContext) => {
|
|
26321
|
+
const setupNativeAgent = async (agentContext, agentValue) => {
|
|
24411
26322
|
const presets = asObject4(context.get("agentPresets"));
|
|
24412
26323
|
if (presets === void 0 || Array.isArray(presets.roots) && presets.roots.length === 0)
|
|
24413
26324
|
return;
|
|
24414
|
-
const agent = asAgent(
|
|
26325
|
+
const agent = asAgent(agentValue);
|
|
24415
26326
|
const projections = asObject4(context.get("sessionProjections"));
|
|
24416
26327
|
if (agent === void 0 || typeof presets.mount !== "function" || typeof projections?.stateOf !== "function") {
|
|
24417
26328
|
throw new Error("Pinned DSH public native preset composition API is unavailable");
|
|
@@ -24422,7 +26333,7 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24422
26333
|
}
|
|
24423
26334
|
await presets.mount(agentContext, recorded ?? options.agentPreset);
|
|
24424
26335
|
};
|
|
24425
|
-
const
|
|
26336
|
+
const open7 = () => {
|
|
24426
26337
|
if (disposed)
|
|
24427
26338
|
return Promise.reject(new Error("DSH host facade is disposed"));
|
|
24428
26339
|
if (openPromise !== void 0)
|
|
@@ -24514,7 +26425,8 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24514
26425
|
return parsed;
|
|
24515
26426
|
});
|
|
24516
26427
|
};
|
|
24517
|
-
const prompt = async (text, requestedSelection, requestedExecutionId) => {
|
|
26428
|
+
const prompt = async (text, requestedSelection, requestedExecutionId, signal) => {
|
|
26429
|
+
signal?.throwIfAborted();
|
|
24518
26430
|
if (disposed)
|
|
24519
26431
|
throw new Error("DSH host facade is disposed");
|
|
24520
26432
|
if (!text.trim())
|
|
@@ -24529,11 +26441,15 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24529
26441
|
promptActive = true;
|
|
24530
26442
|
let disposeSelection;
|
|
24531
26443
|
let selectionRef;
|
|
26444
|
+
let abortPrompt;
|
|
24532
26445
|
try {
|
|
24533
26446
|
if (requestedExecutionId === void 0)
|
|
24534
|
-
await
|
|
26447
|
+
await open7();
|
|
24535
26448
|
throwIfDisposed(disposed, lifecycleAbort.signal, "prompt");
|
|
24536
26449
|
const agent = operationAgent(requestedExecutionId);
|
|
26450
|
+
signal?.throwIfAborted();
|
|
26451
|
+
if (signal && typeof agent.cancel !== "function")
|
|
26452
|
+
throw new Error("DSH public cancel API is required for deletion-safe execution");
|
|
24537
26453
|
if (requestedExecutionId !== void 0) {
|
|
24538
26454
|
assertExecutionParentIdle();
|
|
24539
26455
|
assertExecutionOwnership(agent);
|
|
@@ -24551,7 +26467,7 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24551
26467
|
}
|
|
24552
26468
|
}
|
|
24553
26469
|
const fromSequence = agent.session.seq;
|
|
24554
|
-
const message = requestedExecutionId === void 0 ? await createUserMessage(text, options) : await createIsolatedMessage(`gatherthread-request:${
|
|
26470
|
+
const message = requestedExecutionId === void 0 ? await createUserMessage(text, options) : await createIsolatedMessage(`gatherthread-request:${createHash7("sha256").update(`${requestedExecutionId}:${fromSequence}:${text}`).digest("hex")}`, text, options);
|
|
24555
26471
|
throwIfDisposed(disposed, lifecycleAbort.signal, "prompt");
|
|
24556
26472
|
if (agent.status !== "idle" || agent.session.seq !== fromSequence)
|
|
24557
26473
|
throw new Error("DSH Agent changed while preparing a GatherThread prompt");
|
|
@@ -24559,8 +26475,41 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24559
26475
|
assertExecutionParentIdle();
|
|
24560
26476
|
assertExecutionOwnership(agent);
|
|
24561
26477
|
}
|
|
26478
|
+
if (liveSubscribers.size > 0) {
|
|
26479
|
+
let imported;
|
|
26480
|
+
try {
|
|
26481
|
+
imported = asObject4(await (options.moduleImporter ?? importDshModule)(USER_MESSAGE_MODULE));
|
|
26482
|
+
} catch {
|
|
26483
|
+
}
|
|
26484
|
+
throwIfDisposed(disposed, lifecycleAbort.signal, "prompt");
|
|
26485
|
+
if (agent.status !== "idle" || agent.session.seq !== fromSequence)
|
|
26486
|
+
throw new Error("DSH Agent changed while preparing live output");
|
|
26487
|
+
livePrompt = {
|
|
26488
|
+
agent,
|
|
26489
|
+
closed: false,
|
|
26490
|
+
revision: 0,
|
|
26491
|
+
nativeFrames: typeof imported?.expandAssistantStream === "function",
|
|
26492
|
+
...typeof imported?.isAgentLoopRequest === "function" ? { isLoopRequest: imported.isAgentLoopRequest } : {},
|
|
26493
|
+
provider: requestedSelection?.provider ?? options.provider,
|
|
26494
|
+
model: requestedSelection?.model ?? options.model
|
|
26495
|
+
};
|
|
26496
|
+
}
|
|
26497
|
+
signal?.throwIfAborted();
|
|
24562
26498
|
agent.followup(message);
|
|
26499
|
+
abortPrompt = () => {
|
|
26500
|
+
livePrompt = void 0;
|
|
26501
|
+
if (typeof agent.cancel === "function")
|
|
26502
|
+
agent.cancel({ kind: "user" });
|
|
26503
|
+
else if (requestedExecutionId !== void 0)
|
|
26504
|
+
void releaseExecutionHandle();
|
|
26505
|
+
else
|
|
26506
|
+
void releaseHandle();
|
|
26507
|
+
};
|
|
26508
|
+
signal?.addEventListener("abort", abortPrompt, { once: true });
|
|
26509
|
+
if (signal?.aborted)
|
|
26510
|
+
abortPrompt();
|
|
24563
26511
|
await agent.whenIdle();
|
|
26512
|
+
signal?.throwIfAborted();
|
|
24564
26513
|
throwIfDisposed(disposed, lifecycleAbort.signal, "prompt");
|
|
24565
26514
|
await sessions.flush(agent.session);
|
|
24566
26515
|
throwIfDisposed(disposed, lifecycleAbort.signal, "prompt");
|
|
@@ -24571,18 +26520,21 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24571
26520
|
events: snapshotFrom(fromSequence, requestedExecutionId)
|
|
24572
26521
|
};
|
|
24573
26522
|
} finally {
|
|
26523
|
+
if (abortPrompt)
|
|
26524
|
+
signal?.removeEventListener("abort", abortPrompt);
|
|
24574
26525
|
if (selectionRef !== void 0) {
|
|
24575
26526
|
selectionRef.current = void 0;
|
|
24576
26527
|
selectionRef.assembled = void 0;
|
|
24577
26528
|
}
|
|
24578
26529
|
disposeSelection?.();
|
|
26530
|
+
livePrompt = void 0;
|
|
24579
26531
|
promptActive = false;
|
|
24580
26532
|
}
|
|
24581
26533
|
};
|
|
24582
26534
|
const flush = async () => {
|
|
24583
26535
|
if (disposed)
|
|
24584
26536
|
throw new Error("DSH host facade is disposed");
|
|
24585
|
-
await
|
|
26537
|
+
await open7();
|
|
24586
26538
|
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
24587
26539
|
await sessions.flush(requireAgent(handle).session);
|
|
24588
26540
|
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
@@ -24596,7 +26548,7 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24596
26548
|
return;
|
|
24597
26549
|
projectionActive = true;
|
|
24598
26550
|
try {
|
|
24599
|
-
await
|
|
26551
|
+
await open7();
|
|
24600
26552
|
throwIfDisposed(disposed, lifecycleAbort.signal, "projection");
|
|
24601
26553
|
const agent = requireAgent(handle);
|
|
24602
26554
|
if (agent.status !== "idle") {
|
|
@@ -24639,7 +26591,7 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24639
26591
|
} catch {
|
|
24640
26592
|
}
|
|
24641
26593
|
try {
|
|
24642
|
-
await
|
|
26594
|
+
await contextOperationPromise;
|
|
24643
26595
|
} catch {
|
|
24644
26596
|
}
|
|
24645
26597
|
try {
|
|
@@ -24654,20 +26606,33 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24654
26606
|
return disposePromise;
|
|
24655
26607
|
};
|
|
24656
26608
|
return {
|
|
24657
|
-
sessionId
|
|
24658
|
-
|
|
26609
|
+
get sessionId() {
|
|
26610
|
+
return nativeSessionId;
|
|
26611
|
+
},
|
|
26612
|
+
...options.contextBinding === void 0 ? {} : { contextDeletionVersion: 1 },
|
|
26613
|
+
resetCanonicalContext(revision) {
|
|
26614
|
+
if (contextOperationPromise !== void 0)
|
|
26615
|
+
return Promise.reject(new Error("DSH host facade permits only one active context operation"));
|
|
26616
|
+
const operation = resetCanonicalContext(revision);
|
|
26617
|
+
contextOperationPromise = operation;
|
|
26618
|
+
return operation.finally(() => {
|
|
26619
|
+
if (contextOperationPromise === operation)
|
|
26620
|
+
contextOperationPromise = void 0;
|
|
26621
|
+
});
|
|
26622
|
+
},
|
|
26623
|
+
open: open7,
|
|
24659
26624
|
currentSequence(requestedExecutionId) {
|
|
24660
26625
|
return operationAgent(requestedExecutionId).session.seq;
|
|
24661
26626
|
},
|
|
24662
26627
|
snapshotFrom,
|
|
24663
26628
|
prepareContextExecution(input2) {
|
|
24664
|
-
if (
|
|
26629
|
+
if (contextOperationPromise !== void 0)
|
|
24665
26630
|
return Promise.reject(new Error("DSH host facade permits only one active context preparation"));
|
|
24666
26631
|
const operation = prepareContextExecution(input2);
|
|
24667
|
-
|
|
26632
|
+
contextOperationPromise = operation;
|
|
24668
26633
|
return operation.finally(() => {
|
|
24669
|
-
if (
|
|
24670
|
-
|
|
26634
|
+
if (contextOperationPromise === operation)
|
|
26635
|
+
contextOperationPromise = void 0;
|
|
24671
26636
|
});
|
|
24672
26637
|
},
|
|
24673
26638
|
projectCanonicalEvents,
|
|
@@ -24679,6 +26644,12 @@ ${JSON.stringify(historyContext.items)}`;
|
|
|
24679
26644
|
eventSubscribers.add(listener);
|
|
24680
26645
|
return () => eventSubscribers.delete(listener);
|
|
24681
26646
|
},
|
|
26647
|
+
onLiveOutput(listener) {
|
|
26648
|
+
if (disposed)
|
|
26649
|
+
throw new Error("DSH host facade is disposed");
|
|
26650
|
+
liveSubscribers.add(listener);
|
|
26651
|
+
return () => liveSubscribers.delete(listener);
|
|
26652
|
+
},
|
|
24682
26653
|
onStatus(listener) {
|
|
24683
26654
|
if (disposed)
|
|
24684
26655
|
throw new Error("DSH host facade is disposed");
|
|
@@ -24711,6 +26682,7 @@ async function registerDshNativeWorkspace(contextValue, workspacePath, workspace
|
|
|
24711
26682
|
const sessionQuery = requireService(context, "sessionQuery", ["readSession"]);
|
|
24712
26683
|
const sessionTitle = requireService(context, "sessionTitle", ["rename", "get"]);
|
|
24713
26684
|
const completed = /* @__PURE__ */ new Map();
|
|
26685
|
+
const retired = /* @__PURE__ */ new Set();
|
|
24714
26686
|
const observedSessionIds = new Set(workspace.sessionIds);
|
|
24715
26687
|
const runningSessionIds = /* @__PURE__ */ new Set();
|
|
24716
26688
|
const offActivity = context.on("agent/status", (...args) => {
|
|
@@ -24731,6 +26703,10 @@ async function registerDshNativeWorkspace(contextValue, workspacePath, workspace
|
|
|
24731
26703
|
return;
|
|
24732
26704
|
observedSessionIds.add(session.id);
|
|
24733
26705
|
const event = asSessionEvent(args[1]);
|
|
26706
|
+
if (event?.type === "user/message" && /^gatherthread-retired:[0-9]+$/.test(String(asObject4(event.data)?.id)) && asObject4(asObject4(event.data)?.source)?.plugin === "gatherthread") {
|
|
26707
|
+
retired.add(session.id);
|
|
26708
|
+
completed.delete(session.id);
|
|
26709
|
+
}
|
|
24734
26710
|
if (event?.type === "turn/start")
|
|
24735
26711
|
runningSessionIds.add(session.id);
|
|
24736
26712
|
if (event?.type === "turn/end")
|
|
@@ -24781,6 +26757,10 @@ async function registerDshNativeWorkspace(contextValue, workspacePath, workspace
|
|
|
24781
26757
|
for (const localSessionId of [...currentIds]) {
|
|
24782
26758
|
if (!isUserCreatedDshSessionId(localSessionId))
|
|
24783
26759
|
continue;
|
|
26760
|
+
if (retired.has(localSessionId)) {
|
|
26761
|
+
candidates.delete(localSessionId);
|
|
26762
|
+
continue;
|
|
26763
|
+
}
|
|
24784
26764
|
const cached2 = completed.get(localSessionId);
|
|
24785
26765
|
if (cached2 !== void 0)
|
|
24786
26766
|
continue;
|
|
@@ -24795,8 +26775,11 @@ async function registerDshNativeWorkspace(contextValue, workspacePath, workspace
|
|
|
24795
26775
|
throw new Error("Pinned DSH Session reader returned an invalid event");
|
|
24796
26776
|
return parsed;
|
|
24797
26777
|
});
|
|
24798
|
-
if (!hasCompletedDshLocalTurn(events, localSessionId))
|
|
26778
|
+
if (!hasCompletedDshLocalTurn(events, localSessionId)) {
|
|
26779
|
+
completed.delete(localSessionId);
|
|
26780
|
+
candidates.delete(localSessionId);
|
|
24799
26781
|
continue;
|
|
26782
|
+
}
|
|
24800
26783
|
const title = live === void 0 ? titleFromEvents(events) : normalizedLocalSessionTitle(sessionTitle.get(live)?.title) ?? titleFromEvents(events);
|
|
24801
26784
|
const candidate = { localSessionId, title };
|
|
24802
26785
|
completed.set(localSessionId, candidate);
|
|
@@ -24821,8 +26804,10 @@ async function registerDshNativeWorkspace(contextValue, workspacePath, workspace
|
|
|
24821
26804
|
return;
|
|
24822
26805
|
events.push(parsed);
|
|
24823
26806
|
}
|
|
24824
|
-
if (!hasCompletedDshLocalTurn(events, session.id))
|
|
26807
|
+
if (!hasCompletedDshLocalTurn(events, session.id)) {
|
|
26808
|
+
completed.delete(session.id);
|
|
24825
26809
|
return;
|
|
26810
|
+
}
|
|
24826
26811
|
completed.set(session.id, {
|
|
24827
26812
|
localSessionId: session.id,
|
|
24828
26813
|
title: normalizedLocalSessionTitle(sessionTitle.get(session)?.title) ?? titleFromEvents(events)
|
|
@@ -24903,8 +26888,11 @@ async function createProjectionMessage(projection, options) {
|
|
|
24903
26888
|
});
|
|
24904
26889
|
}
|
|
24905
26890
|
function formatProjectionText(projection) {
|
|
24906
|
-
if (projection.role === "user")
|
|
24907
|
-
|
|
26891
|
+
if (projection.role === "user") {
|
|
26892
|
+
const sender = projection.actorDisplayName?.replace(/[\p{Cc}\p{Cf}【】]+/gu, " ").replace(/\s+/gu, " ").trim();
|
|
26893
|
+
return sender ? `\u3010${sender} \xB7 GatherThread\u3011
|
|
26894
|
+
${projection.content}` : projection.content;
|
|
26895
|
+
}
|
|
24908
26896
|
const route = [projection.provider, projection.model].filter(Boolean).join(" / ");
|
|
24909
26897
|
const attribution = [projection.actorDisplayName, route].filter(Boolean).join(" \xB7 ");
|
|
24910
26898
|
return `[GatherThread remote Agent reply${attribution ? ` \xB7 ${attribution}` : ""}]
|
|
@@ -25069,9 +27057,9 @@ function asObject4(value) {
|
|
|
25069
27057
|
}
|
|
25070
27058
|
|
|
25071
27059
|
// dist/src/native-connection.js
|
|
25072
|
-
import { createHash as
|
|
27060
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
25073
27061
|
import { homedir as homedir3 } from "node:os";
|
|
25074
|
-
import
|
|
27062
|
+
import path7 from "node:path";
|
|
25075
27063
|
var DSH_NATIVE_CREDENTIAL_KEY = "gatherthread-dsh-host/default";
|
|
25076
27064
|
var DSH_NATIVE_GRANT_SCHEMA_VERSION = 2;
|
|
25077
27065
|
var DSH_NATIVE_PAIRING_CHANNEL = "/gatherthread";
|
|
@@ -25204,7 +27192,7 @@ function createNativeProjectConfig(options) {
|
|
|
25204
27192
|
projectId: options.binding.projectId,
|
|
25205
27193
|
projectName: options.binding.projectName,
|
|
25206
27194
|
deviceId: options.grant.deviceId,
|
|
25207
|
-
workspacePath:
|
|
27195
|
+
workspacePath: path7.resolve(options.workspacePath),
|
|
25208
27196
|
stateRoot,
|
|
25209
27197
|
provider: options.binding.provider,
|
|
25210
27198
|
model: options.binding.model,
|
|
@@ -25293,12 +27281,12 @@ function normalizeDshServerUrl(value) {
|
|
|
25293
27281
|
return { serverUrl, apiUrl: `${serverUrl}/v1` };
|
|
25294
27282
|
}
|
|
25295
27283
|
function nativeStateRoot(dshHome, serverUrl, projectId, workspacePath) {
|
|
25296
|
-
const digest3 =
|
|
25297
|
-
return
|
|
27284
|
+
const digest3 = createHash8("sha256").update(JSON.stringify([serverUrl, projectId, path7.resolve(workspacePath)])).digest("hex").slice(0, 32);
|
|
27285
|
+
return path7.join(path7.resolve(dshHome), "gatherthread", "state", digest3);
|
|
25298
27286
|
}
|
|
25299
27287
|
function resolveDshHome(env) {
|
|
25300
27288
|
const configured = env.DSH_HOME?.trim();
|
|
25301
|
-
return
|
|
27289
|
+
return path7.resolve(configured ? configured : path7.join(homedir3(), ".dsh"));
|
|
25302
27290
|
}
|
|
25303
27291
|
function parseBinding(value) {
|
|
25304
27292
|
const input2 = exactObject(value, /* @__PURE__ */ new Set([
|
|
@@ -25442,8 +27430,8 @@ function isLoopback2(hostname3) {
|
|
|
25442
27430
|
}
|
|
25443
27431
|
|
|
25444
27432
|
// dist/src/project-manager.js
|
|
25445
|
-
import
|
|
25446
|
-
import { createHash as
|
|
27433
|
+
import path8 from "node:path";
|
|
27434
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
25447
27435
|
|
|
25448
27436
|
// dist/src/execution-gate.js
|
|
25449
27437
|
var BoundedDshExecutionGate = class {
|
|
@@ -25722,6 +27710,27 @@ var DshProjectManager = class {
|
|
|
25722
27710
|
for (const error62 of permissions.errors)
|
|
25723
27711
|
this.#reportError(error62);
|
|
25724
27712
|
const eligibleSessions = [...permissions.eligibleSessions];
|
|
27713
|
+
const sessionUpdateErrors = [];
|
|
27714
|
+
for (const session of eligibleSessions) {
|
|
27715
|
+
const binding = this.#managed.get(session.id);
|
|
27716
|
+
if (binding?.phase === "active" && !binding.connector.stopped) {
|
|
27717
|
+
try {
|
|
27718
|
+
binding.connector.updateSessionSummary(session);
|
|
27719
|
+
} catch (error62) {
|
|
27720
|
+
this.#managed.delete(session.id);
|
|
27721
|
+
try {
|
|
27722
|
+
await binding.connector.stop();
|
|
27723
|
+
} catch (stopError) {
|
|
27724
|
+
this.#reportError(publicError2(stopError, "DSH Session connector cleanup failed"));
|
|
27725
|
+
}
|
|
27726
|
+
if (!this.#stopped)
|
|
27727
|
+
this.#recordFailure(session.id);
|
|
27728
|
+
const failure2 = publicError2(error62, `DSH Session ${session.id} mode refresh failed`);
|
|
27729
|
+
sessionUpdateErrors.push(failure2);
|
|
27730
|
+
this.#reportError(failure2);
|
|
27731
|
+
}
|
|
27732
|
+
}
|
|
27733
|
+
}
|
|
25725
27734
|
if (this.#discoverLocalSessions !== void 0) {
|
|
25726
27735
|
try {
|
|
25727
27736
|
const candidates2 = await this.#discoverLocalSessions();
|
|
@@ -25765,7 +27774,7 @@ var DshProjectManager = class {
|
|
|
25765
27774
|
status: "updated",
|
|
25766
27775
|
eligibleSessionIds: [...eligibleIds].sort(),
|
|
25767
27776
|
activeSessionIds: this.activeSessionIds(),
|
|
25768
|
-
errors: [...permissions.errors, ...attachErrors]
|
|
27777
|
+
errors: [...permissions.errors, ...sessionUpdateErrors, ...attachErrors]
|
|
25769
27778
|
};
|
|
25770
27779
|
this.#notifyStatus({
|
|
25771
27780
|
state: "connected",
|
|
@@ -25880,7 +27889,7 @@ function projectSessionConfig(config2, sessionId, nativeSessionId) {
|
|
|
25880
27889
|
sessionId,
|
|
25881
27890
|
deviceId: config2.deviceId,
|
|
25882
27891
|
workspacePath: config2.workspacePath,
|
|
25883
|
-
statePath:
|
|
27892
|
+
statePath: path8.join(config2.stateRoot, `${dshSessionId}.json`),
|
|
25884
27893
|
provider: config2.provider,
|
|
25885
27894
|
model: config2.model,
|
|
25886
27895
|
...config2.executionProfiles === void 0 ? {} : {
|
|
@@ -25898,7 +27907,7 @@ function projectSessionConfig(config2, sessionId, nativeSessionId) {
|
|
|
25898
27907
|
};
|
|
25899
27908
|
}
|
|
25900
27909
|
function nativeSessionCreationKey(projectId, localSessionId) {
|
|
25901
|
-
const identity =
|
|
27910
|
+
const identity = createHash9("sha256").update(`${projectId}\0${localSessionId}`).digest("hex");
|
|
25902
27911
|
return `dsh-native:${identity}`;
|
|
25903
27912
|
}
|
|
25904
27913
|
function retryDelay(config2, attempts) {
|
|
@@ -25928,31 +27937,31 @@ function publicError2(error62, fallback) {
|
|
|
25928
27937
|
}
|
|
25929
27938
|
|
|
25930
27939
|
// dist/src/state-store.js
|
|
25931
|
-
import { randomUUID as
|
|
25932
|
-
import { constants as
|
|
25933
|
-
import { lstat as
|
|
25934
|
-
import
|
|
27940
|
+
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
27941
|
+
import { constants as constants4 } from "node:fs";
|
|
27942
|
+
import { lstat as lstat7, mkdir as mkdir5, open as open6, rename as rename3, unlink as unlink4 } from "node:fs/promises";
|
|
27943
|
+
import path9 from "node:path";
|
|
25935
27944
|
var FileConnectorStateStore = class {
|
|
25936
27945
|
#path;
|
|
25937
27946
|
constructor(statePath) {
|
|
25938
|
-
if (!
|
|
27947
|
+
if (!path9.isAbsolute(statePath))
|
|
25939
27948
|
throw new Error("DSH connector state path must be absolute");
|
|
25940
|
-
this.#path =
|
|
27949
|
+
this.#path = path9.resolve(statePath);
|
|
25941
27950
|
}
|
|
25942
27951
|
async load() {
|
|
25943
|
-
const directory =
|
|
27952
|
+
const directory = path9.dirname(this.#path);
|
|
25944
27953
|
const ancestorsBefore = await secureDirectoryChain(directory);
|
|
25945
27954
|
let metadata;
|
|
25946
27955
|
try {
|
|
25947
|
-
metadata = await
|
|
27956
|
+
metadata = await lstat7(this.#path);
|
|
25948
27957
|
} catch (error62) {
|
|
25949
27958
|
if (isNodeError4(error62) && error62.code === "ENOENT")
|
|
25950
27959
|
return void 0;
|
|
25951
27960
|
throw error62;
|
|
25952
27961
|
}
|
|
25953
27962
|
assertPrivateRegularFile(metadata, "DSH connector state");
|
|
25954
|
-
const noFollow = "O_NOFOLLOW" in
|
|
25955
|
-
const handle = await
|
|
27963
|
+
const noFollow = "O_NOFOLLOW" in constants4 ? constants4.O_NOFOLLOW : 0;
|
|
27964
|
+
const handle = await open6(this.#path, constants4.O_RDONLY | noFollow);
|
|
25956
27965
|
let raw;
|
|
25957
27966
|
try {
|
|
25958
27967
|
const opened = await handle.stat();
|
|
@@ -25974,17 +27983,17 @@ var FileConnectorStateStore = class {
|
|
|
25974
27983
|
}
|
|
25975
27984
|
async save(state) {
|
|
25976
27985
|
const validated = validateConnectorState(structuredClone(state));
|
|
25977
|
-
const directory =
|
|
27986
|
+
const directory = path9.dirname(this.#path);
|
|
25978
27987
|
await secureDirectoryChain(directory);
|
|
25979
|
-
await
|
|
27988
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
25980
27989
|
const ancestorsBefore = await secureDirectoryChain(directory);
|
|
25981
|
-
const directoryBefore = await
|
|
27990
|
+
const directoryBefore = await lstat7(directory);
|
|
25982
27991
|
assertSecureDirectory(directoryBefore);
|
|
25983
27992
|
const targetBefore = await optionalLstat(this.#path);
|
|
25984
27993
|
if (targetBefore !== void 0)
|
|
25985
27994
|
assertPrivateRegularFile(targetBefore, "DSH connector state");
|
|
25986
|
-
const temporaryPath = `${this.#path}.tmp-${process.pid}-${
|
|
25987
|
-
const handle = await
|
|
27995
|
+
const temporaryPath = `${this.#path}.tmp-${process.pid}-${randomUUID6()}`;
|
|
27996
|
+
const handle = await open6(temporaryPath, "wx", 384);
|
|
25988
27997
|
try {
|
|
25989
27998
|
await handle.writeFile(`${JSON.stringify(validated, null, 2)}
|
|
25990
27999
|
`, { encoding: "utf8" });
|
|
@@ -25993,7 +28002,7 @@ var FileConnectorStateStore = class {
|
|
|
25993
28002
|
await handle.close();
|
|
25994
28003
|
}
|
|
25995
28004
|
try {
|
|
25996
|
-
const directoryAfter = await
|
|
28005
|
+
const directoryAfter = await lstat7(directory);
|
|
25997
28006
|
if (directoryAfter.isSymbolicLink() || directoryAfter.dev !== directoryBefore.dev || directoryAfter.ino !== directoryBefore.ino) {
|
|
25998
28007
|
throw new Error("DSH connector state directory changed during atomic save");
|
|
25999
28008
|
}
|
|
@@ -26004,9 +28013,9 @@ var FileConnectorStateStore = class {
|
|
|
26004
28013
|
}
|
|
26005
28014
|
await rename3(temporaryPath, this.#path);
|
|
26006
28015
|
assertSameDirectoryChain(ancestorsBefore, await secureDirectoryChain(directory));
|
|
26007
|
-
assertPrivateRegularFile(await
|
|
28016
|
+
assertPrivateRegularFile(await lstat7(this.#path), "DSH connector state");
|
|
26008
28017
|
} catch (error62) {
|
|
26009
|
-
await
|
|
28018
|
+
await unlink4(temporaryPath).catch(() => void 0);
|
|
26010
28019
|
throw error62;
|
|
26011
28020
|
}
|
|
26012
28021
|
}
|
|
@@ -26023,7 +28032,10 @@ function validateConnectorState(value) {
|
|
|
26023
28032
|
"publishedDshSequence",
|
|
26024
28033
|
...legacyUploadPolicy ? [] : ["automaticUpload"],
|
|
26025
28034
|
"activeRequest",
|
|
26026
|
-
"outbox"
|
|
28035
|
+
"outbox",
|
|
28036
|
+
"contentRevision",
|
|
28037
|
+
"projectionSessionId",
|
|
28038
|
+
"localOnlySessionIds"
|
|
26027
28039
|
]), "state");
|
|
26028
28040
|
if (state.version !== 1 && state.version !== 2 && state.version !== 3) {
|
|
26029
28041
|
throw new Error("Unsupported DSH connector state version");
|
|
@@ -26038,6 +28050,9 @@ function validateConnectorState(value) {
|
|
|
26038
28050
|
throw new Error("state.outbox must be an array");
|
|
26039
28051
|
const outbox = state.outbox.map(parseOutboxOperation);
|
|
26040
28052
|
const validated = {
|
|
28053
|
+
...state.contentRevision === void 0 ? {} : { contentRevision: nonNegativeInteger(state.contentRevision, "state.contentRevision") },
|
|
28054
|
+
...state.projectionSessionId === void 0 ? {} : { projectionSessionId: safeString(state.projectionSessionId, "state.projectionSessionId", 128) },
|
|
28055
|
+
...state.localOnlySessionIds === void 0 ? {} : { localOnlySessionIds: parseRetiredSessionIds(state.localOnlySessionIds) },
|
|
26041
28056
|
version: 3,
|
|
26042
28057
|
binding: {
|
|
26043
28058
|
projectId: safeString(binding.projectId, "state.binding.projectId", 128),
|
|
@@ -26056,8 +28071,9 @@ function validateConnectorState(value) {
|
|
|
26056
28071
|
}
|
|
26057
28072
|
function parseActiveRequest(value) {
|
|
26058
28073
|
const active = requiredObject4(value, "state.activeRequest");
|
|
26059
|
-
exactKeys(active, /* @__PURE__ */ new Set(["requestId", "requestSequence", "dshFromSequence", "dshToSequence", "promptDigest", "claimAttempt", "contextExecution"]), "state.activeRequest");
|
|
28074
|
+
exactKeys(active, /* @__PURE__ */ new Set(["requestId", "requestSequence", "dshFromSequence", "dshToSequence", "promptDigest", "claimAttempt", "contextExecution", "contentRevision"]), "state.activeRequest");
|
|
26060
28075
|
const parsed = {
|
|
28076
|
+
...active.contentRevision === void 0 ? {} : { contentRevision: nonNegativeInteger(active.contentRevision, "state.activeRequest.contentRevision") },
|
|
26061
28077
|
requestId: safeString(active.requestId, "state.activeRequest.requestId", 128),
|
|
26062
28078
|
requestSequence: positiveInteger(active.requestSequence, "state.activeRequest.requestSequence"),
|
|
26063
28079
|
dshFromSequence: nonNegativeInteger(active.dshFromSequence, "state.activeRequest.dshFromSequence"),
|
|
@@ -26133,7 +28149,8 @@ function parseLocalTurnInput(value, label) {
|
|
|
26133
28149
|
"observedReasoningEffort",
|
|
26134
28150
|
"requestPayload",
|
|
26135
28151
|
"responsePayload",
|
|
26136
|
-
"toolEvents"
|
|
28152
|
+
"toolEvents",
|
|
28153
|
+
"contentRevision"
|
|
26137
28154
|
]), label);
|
|
26138
28155
|
const occurredAt = safeString(input2.occurredAt, `${label}.occurredAt`, 64);
|
|
26139
28156
|
if (!Number.isFinite(Date.parse(occurredAt)))
|
|
@@ -26143,6 +28160,7 @@ function parseLocalTurnInput(value, label) {
|
|
|
26143
28160
|
}
|
|
26144
28161
|
return {
|
|
26145
28162
|
localTurnId: safeString(input2.localTurnId, `${label}.localTurnId`, 200),
|
|
28163
|
+
...input2.contentRevision === void 0 ? {} : { contentRevision: nonNegativeInteger(input2.contentRevision, `${label}.contentRevision`) },
|
|
26146
28164
|
runtimeId: safeString(input2.runtimeId, `${label}.runtimeId`, 128),
|
|
26147
28165
|
basedOnSequence: nonNegativeInteger(input2.basedOnSequence, `${label}.basedOnSequence`),
|
|
26148
28166
|
occurredAt,
|
|
@@ -26170,13 +28188,15 @@ function parseAppendInput(value, label) {
|
|
|
26170
28188
|
"runtimeId",
|
|
26171
28189
|
"claimAttempt",
|
|
26172
28190
|
"observedModel",
|
|
26173
|
-
"observedReasoningEffort"
|
|
28191
|
+
"observedReasoningEffort",
|
|
28192
|
+
"contentRevision"
|
|
26174
28193
|
]), label);
|
|
26175
28194
|
if (input2.type !== "tool_call" && input2.type !== "tool_result") {
|
|
26176
28195
|
throw new Error(`${label}.type must be tool_call or tool_result`);
|
|
26177
28196
|
}
|
|
26178
28197
|
return {
|
|
26179
28198
|
type: input2.type,
|
|
28199
|
+
...input2.contentRevision === void 0 ? {} : { contentRevision: nonNegativeInteger(input2.contentRevision, `${label}.contentRevision`) },
|
|
26180
28200
|
idempotencyKey: safeString(input2.idempotencyKey, `${label}.idempotencyKey`, 200),
|
|
26181
28201
|
payload: jsonValue(input2.payload, `${label}.payload`),
|
|
26182
28202
|
...input2.replyTo === void 0 ? {} : { replyTo: safeString(input2.replyTo, `${label}.replyTo`, 128) },
|
|
@@ -26193,9 +28213,10 @@ function parseAppendInput(value, label) {
|
|
|
26193
28213
|
}
|
|
26194
28214
|
function parseCompletionInput(value, label) {
|
|
26195
28215
|
const input2 = requiredObject4(value, label);
|
|
26196
|
-
exactKeys(input2, /* @__PURE__ */ new Set(["runtimeId", "claimAttempt", "idempotencyKey", "payload", "observedModel", "observedReasoningEffort"]), label);
|
|
28216
|
+
exactKeys(input2, /* @__PURE__ */ new Set(["runtimeId", "claimAttempt", "idempotencyKey", "payload", "observedModel", "observedReasoningEffort", "contentRevision"]), label);
|
|
26197
28217
|
return {
|
|
26198
28218
|
runtimeId: safeString(input2.runtimeId, `${label}.runtimeId`, 128),
|
|
28219
|
+
...input2.contentRevision === void 0 ? {} : { contentRevision: nonNegativeInteger(input2.contentRevision, `${label}.contentRevision`) },
|
|
26199
28220
|
...input2.claimAttempt === void 0 ? {} : {
|
|
26200
28221
|
claimAttempt: positiveInteger(input2.claimAttempt, `${label}.claimAttempt`)
|
|
26201
28222
|
},
|
|
@@ -26207,6 +28228,11 @@ function parseCompletionInput(value, label) {
|
|
|
26207
28228
|
}
|
|
26208
28229
|
};
|
|
26209
28230
|
}
|
|
28231
|
+
function parseRetiredSessionIds(value) {
|
|
28232
|
+
if (!Array.isArray(value) || value.length > 4096 || new Set(value).size !== value.length)
|
|
28233
|
+
throw new Error("Invalid DSH retired Session identities");
|
|
28234
|
+
return value.map((id, index) => safeString(id, `state.localOnlySessionIds[${index}]`, 128));
|
|
28235
|
+
}
|
|
26210
28236
|
function assertAlreadyRedacted(state) {
|
|
26211
28237
|
const encoded = JSON.stringify(state);
|
|
26212
28238
|
const redacted = JSON.stringify(redactValue(state));
|
|
@@ -26269,7 +28295,7 @@ function isNodeError4(error62) {
|
|
|
26269
28295
|
}
|
|
26270
28296
|
async function optionalLstat(target) {
|
|
26271
28297
|
try {
|
|
26272
|
-
return await
|
|
28298
|
+
return await lstat7(target);
|
|
26273
28299
|
} catch (error62) {
|
|
26274
28300
|
if (isNodeError4(error62) && error62.code === "ENOENT")
|
|
26275
28301
|
return void 0;
|
|
@@ -26526,7 +28552,10 @@ var RPC_ENDPOINTS = /* @__PURE__ */ new Set([
|
|
|
26526
28552
|
"sync/set-auto-upload",
|
|
26527
28553
|
"sync/upload",
|
|
26528
28554
|
"code/authorize",
|
|
26529
|
-
"code/action"
|
|
28555
|
+
"code/action",
|
|
28556
|
+
"github/authorize",
|
|
28557
|
+
"github/action",
|
|
28558
|
+
"github/connect"
|
|
26530
28559
|
]);
|
|
26531
28560
|
var MAX_CATALOG_PROVIDERS = 64;
|
|
26532
28561
|
var MAX_CATALOG_MODELS = 256;
|
|
@@ -26628,6 +28657,12 @@ var DshNativeHostController = class {
|
|
|
26628
28657
|
projectName: this.#projects.get(projectId)?.name ?? projectId,
|
|
26629
28658
|
...view
|
|
26630
28659
|
}];
|
|
28660
|
+
}).slice(0, 100),
|
|
28661
|
+
githubSync: [...this.#owners].flatMap(([projectId, owner]) => {
|
|
28662
|
+
if (this.#projects.get(projectId)?.role === "viewer")
|
|
28663
|
+
return [];
|
|
28664
|
+
const view = owner.githubSyncView?.();
|
|
28665
|
+
return view === void 0 ? [] : [{ projectId, projectName: this.#projects.get(projectId)?.name ?? projectId, ...view }];
|
|
26631
28666
|
}).slice(0, 100)
|
|
26632
28667
|
}
|
|
26633
28668
|
},
|
|
@@ -26889,6 +28924,40 @@ var DshNativeHostController = class {
|
|
|
26889
28924
|
}
|
|
26890
28925
|
return this.publicState();
|
|
26891
28926
|
}
|
|
28927
|
+
async githubSyncAction(inputValue, authorize = false) {
|
|
28928
|
+
this.#assertAvailable();
|
|
28929
|
+
const input2 = exactInput(inputValue, new Set(authorize ? ["projectId", "enabled", "repository", "base_branch", "revision"] : ["projectId", "action"]));
|
|
28930
|
+
const projectId = safeInputIdentifier(input2.projectId, "projectId");
|
|
28931
|
+
const owner = this.#owners.get(projectId);
|
|
28932
|
+
if (!owner || this.#projects.get(projectId)?.role === "viewer")
|
|
28933
|
+
throw new Error("This Project is not writable on this DSH device");
|
|
28934
|
+
if (authorize) {
|
|
28935
|
+
if (typeof input2.enabled !== "boolean" || !owner.authorizeGitHubSync)
|
|
28936
|
+
throw new Error("Invalid GitHub authorization");
|
|
28937
|
+
let expected;
|
|
28938
|
+
if (input2.enabled) {
|
|
28939
|
+
if (typeof input2.repository !== "string" || typeof input2.base_branch !== "string" || typeof input2.revision !== "string" || input2.repository.length > 160 || input2.base_branch.length > 255 || input2.revision.length > 128)
|
|
28940
|
+
throw new Error("Invalid GitHub target");
|
|
28941
|
+
expected = { repository: input2.repository, base_branch: input2.base_branch, revision: input2.revision };
|
|
28942
|
+
}
|
|
28943
|
+
await owner.authorizeGitHubSync(input2.enabled, expected);
|
|
28944
|
+
} else {
|
|
28945
|
+
if (typeof input2.action !== "string" || !GITHUB_CODE_ACTIONS.includes(input2.action) || !owner.executeGitHubSync)
|
|
28946
|
+
throw new Error("Invalid GitHub action");
|
|
28947
|
+
await owner.executeGitHubSync(input2.action);
|
|
28948
|
+
}
|
|
28949
|
+
return this.publicState();
|
|
28950
|
+
}
|
|
28951
|
+
async connectGitHub(inputValue) {
|
|
28952
|
+
this.#assertAvailable();
|
|
28953
|
+
const input2 = exactInput(inputValue, /* @__PURE__ */ new Set(["projectId"]));
|
|
28954
|
+
const projectId = safeInputIdentifier(input2.projectId, "projectId");
|
|
28955
|
+
if (!this.#owners.has(projectId) || this.#projects.get(projectId)?.role === "viewer") {
|
|
28956
|
+
throw new Error("This Project is not writable on this DSH device");
|
|
28957
|
+
}
|
|
28958
|
+
await connectGitHubAccount();
|
|
28959
|
+
return this.publicState();
|
|
28960
|
+
}
|
|
26892
28961
|
dispose() {
|
|
26893
28962
|
this.#disposePromise ??= (async () => {
|
|
26894
28963
|
this.#disposed = true;
|
|
@@ -26990,9 +29059,13 @@ var DshNativeHostController = class {
|
|
|
26990
29059
|
if (route === void 0)
|
|
26991
29060
|
return;
|
|
26992
29061
|
throwIfAborted(signal);
|
|
26993
|
-
const
|
|
29062
|
+
const catalogUpdate = await this.#refreshExecutionProfiles(route, signal);
|
|
26994
29063
|
throwIfAborted(signal);
|
|
26995
|
-
if (
|
|
29064
|
+
if (catalogUpdate === "route_unavailable") {
|
|
29065
|
+
await this.#abandonUnavailableRoute(grant);
|
|
29066
|
+
return;
|
|
29067
|
+
}
|
|
29068
|
+
if (catalogUpdate === "changed") {
|
|
26996
29069
|
for (const owner of this.#owners.values())
|
|
26997
29070
|
this.#pendingProfileOwners.add(owner);
|
|
26998
29071
|
}
|
|
@@ -27034,16 +29107,17 @@ var DshNativeHostController = class {
|
|
|
27034
29107
|
/**
|
|
27035
29108
|
* Re-read the harness catalog when the route changed or the entry went stale.
|
|
27036
29109
|
*
|
|
27037
|
-
* Returns whether the catalog differs from the one already advertised
|
|
27038
|
-
*
|
|
27039
|
-
*
|
|
27040
|
-
*
|
|
29110
|
+
* Returns whether the catalog differs from the one already advertised, or that
|
|
29111
|
+
* DSH no longer offers the model this connection runs. Discovery is cached per
|
|
29112
|
+
* route and also time-bounded: DSH publishes a model metadata catalog that can
|
|
29113
|
+
* gain models while a connection stays open, so a connect-time snapshot would
|
|
29114
|
+
* otherwise keep offering the old list for the life of the process.
|
|
27041
29115
|
*/
|
|
27042
29116
|
async #refreshExecutionProfiles(route, signal) {
|
|
27043
29117
|
const routeKey = `${route.provider}\0${route.model}`;
|
|
27044
29118
|
const discoveredAt = this.#executionProfilesDiscoveredAt;
|
|
27045
29119
|
if (this.#executionProfileRouteKey === routeKey && discoveredAt !== void 0 && this.#now() - discoveredAt < EXECUTION_PROFILE_REFRESH_INTERVAL_MS) {
|
|
27046
|
-
return
|
|
29120
|
+
return "unchanged";
|
|
27047
29121
|
}
|
|
27048
29122
|
const previous = this.#executionProfiles;
|
|
27049
29123
|
const remember = (profiles2) => {
|
|
@@ -27051,18 +29125,44 @@ var DshNativeHostController = class {
|
|
|
27051
29125
|
this.#executionProfiles = profiles2;
|
|
27052
29126
|
this.#executionProfileRouteKey = routeKey;
|
|
27053
29127
|
this.#executionProfilesDiscoveredAt = this.#now();
|
|
27054
|
-
return changed;
|
|
29128
|
+
return changed ? "changed" : "unchanged";
|
|
27055
29129
|
};
|
|
27056
29130
|
if (route.provider !== "deepseek-official")
|
|
27057
29131
|
return remember(void 0);
|
|
27058
29132
|
const discovered = this.#options.listExecutionProfiles === void 0 ? await listNativeExecutionProfiles(this.#options.context, route.provider, signal) : await this.#options.listExecutionProfiles(route.provider, signal);
|
|
27059
29133
|
throwIfAborted(signal);
|
|
27060
29134
|
const profiles = normalizeExecutionProfiles(discovered, route.provider);
|
|
27061
|
-
if (!profiles.some((profile) => profile.model === route.model))
|
|
27062
|
-
|
|
27063
|
-
}
|
|
29135
|
+
if (!profiles.some((profile) => profile.model === route.model))
|
|
29136
|
+
return "route_unavailable";
|
|
27064
29137
|
return remember(profiles);
|
|
27065
29138
|
}
|
|
29139
|
+
/**
|
|
29140
|
+
* Drop the route after DSH stops offering the model this connection runs.
|
|
29141
|
+
*
|
|
29142
|
+
* A harness catalog is the authority on what a runtime can run, so the route is
|
|
29143
|
+
* abandoned instead of being repointed at another model: registering a
|
|
29144
|
+
* replacement silently would run a model this device never selected. Clearing the
|
|
29145
|
+
* stored route returns the connection to the state it has before its first
|
|
29146
|
+
* selection, where the panel asks for an explicit provider and model against the
|
|
29147
|
+
* harness's current catalog. Owners and the cached declaration go with it, so
|
|
29148
|
+
* nothing keeps advertising the profiles and bindings built from the dropped route.
|
|
29149
|
+
*/
|
|
29150
|
+
async #abandonUnavailableRoute(grant) {
|
|
29151
|
+
await this.#stopOwners();
|
|
29152
|
+
this.#executionProfiles = void 0;
|
|
29153
|
+
this.#executionProfileRouteKey = void 0;
|
|
29154
|
+
this.#executionProfilesDiscoveredAt = void 0;
|
|
29155
|
+
this.#grant = await this.#credentials.save({
|
|
29156
|
+
schemaVersion: grant.schemaVersion,
|
|
29157
|
+
serverUrl: grant.serverUrl,
|
|
29158
|
+
apiUrl: grant.apiUrl,
|
|
29159
|
+
deviceId: grant.deviceId,
|
|
29160
|
+
deviceName: grant.deviceName,
|
|
29161
|
+
token: grant.token
|
|
29162
|
+
});
|
|
29163
|
+
this.#recoverableError = "route_unavailable";
|
|
29164
|
+
this.#options.status.setConnection("stopped");
|
|
29165
|
+
}
|
|
27066
29166
|
/**
|
|
27067
29167
|
* Republish the current catalog to every owner that has not confirmed it.
|
|
27068
29168
|
*
|
|
@@ -27192,7 +29292,7 @@ async function apply(context, rawConfig) {
|
|
|
27192
29292
|
workspacePath: config2.workspacePath,
|
|
27193
29293
|
...dshHome === void 0 ? {} : {
|
|
27194
29294
|
dshHome,
|
|
27195
|
-
projectWorkspaceHomeDirectory:
|
|
29295
|
+
projectWorkspaceHomeDirectory: path10.dirname(dshHome)
|
|
27196
29296
|
},
|
|
27197
29297
|
...config2.officialServerUrl === void 0 ? {} : { officialServerUrl: config2.officialServerUrl }
|
|
27198
29298
|
});
|
|
@@ -27250,6 +29350,12 @@ function registerNativeDshRpc(contextValue, controller) {
|
|
|
27250
29350
|
return rpcSuccess(await controller.codeSyncAction(payload, true));
|
|
27251
29351
|
case "code/action":
|
|
27252
29352
|
return rpcSuccess(await controller.codeSyncAction(payload));
|
|
29353
|
+
case "github/authorize":
|
|
29354
|
+
return rpcSuccess(await controller.githubSyncAction(payload, true));
|
|
29355
|
+
case "github/action":
|
|
29356
|
+
return rpcSuccess(await controller.githubSyncAction(payload));
|
|
29357
|
+
case "github/connect":
|
|
29358
|
+
return rpcSuccess(await controller.connectGitHub(payload));
|
|
27253
29359
|
default:
|
|
27254
29360
|
return rpcFailure("gatherthread/not-found", "GatherThread DSH action is unavailable");
|
|
27255
29361
|
}
|
|
@@ -27344,8 +29450,8 @@ async function createProductionOwner(options) {
|
|
|
27344
29450
|
throw error62;
|
|
27345
29451
|
}
|
|
27346
29452
|
const codeSync = new DshCodeSyncController({
|
|
27347
|
-
permissionPath:
|
|
27348
|
-
binding:
|
|
29453
|
+
permissionPath: path10.join(config2.stateRoot, `code-consent-${createHash10("sha256").update(actor.id).digest("hex")}.json`),
|
|
29454
|
+
binding: createHash10("sha256").update(JSON.stringify([
|
|
27349
29455
|
config2.apiUrl,
|
|
27350
29456
|
config2.projectId,
|
|
27351
29457
|
actor.id,
|
|
@@ -27360,7 +29466,7 @@ async function createProductionOwner(options) {
|
|
|
27360
29466
|
// A custom DSH home is an isolated profile, including its code-sync state.
|
|
27361
29467
|
// Default profiles keep the harness-neutral state shared with Codex.
|
|
27362
29468
|
...options.dshHome === void 0 ? {} : {
|
|
27363
|
-
stateRoot:
|
|
29469
|
+
stateRoot: path10.join(options.dshHome, "gatherthread-code-sync", createHash10("sha256").update(config2.workspacePath).digest("hex"))
|
|
27364
29470
|
}
|
|
27365
29471
|
}),
|
|
27366
29472
|
api: new HttpCollaborationClient({
|
|
@@ -27372,8 +29478,77 @@ async function createProductionOwner(options) {
|
|
|
27372
29478
|
isBusy: () => nativeWorkspace.isBusy()
|
|
27373
29479
|
});
|
|
27374
29480
|
await codeSync.start().catch(() => void 0);
|
|
29481
|
+
const githubSync = new DshGitHubCodeSyncController({
|
|
29482
|
+
permissionPath: path10.join(config2.stateRoot, `github-consent-${createHash10("sha256").update(actor.id).digest("hex")}.json`),
|
|
29483
|
+
binding: createHash10("sha256").update(JSON.stringify([
|
|
29484
|
+
"github",
|
|
29485
|
+
config2.apiUrl,
|
|
29486
|
+
config2.projectId,
|
|
29487
|
+
actor.id,
|
|
29488
|
+
config2.workspacePath
|
|
29489
|
+
])).digest("hex"),
|
|
29490
|
+
readMetadata: async () => {
|
|
29491
|
+
const response = await fetch(`${config2.apiUrl}/projects/${encodeURIComponent(config2.projectId)}/github`, {
|
|
29492
|
+
headers: { authorization: `Bearer ${options.grant.token}` },
|
|
29493
|
+
redirect: "error",
|
|
29494
|
+
signal: AbortSignal.any([ownerAbort.signal, AbortSignal.timeout(15e3)])
|
|
29495
|
+
});
|
|
29496
|
+
if (!response.ok || !response.body)
|
|
29497
|
+
throw Object.assign(new Error("GitHub metadata unavailable"), { code: "code_github_unavailable" });
|
|
29498
|
+
const reader = response.body.getReader();
|
|
29499
|
+
const chunks = [];
|
|
29500
|
+
let size = 0;
|
|
29501
|
+
try {
|
|
29502
|
+
for (; ; ) {
|
|
29503
|
+
const next = await reader.read();
|
|
29504
|
+
if (next.done)
|
|
29505
|
+
break;
|
|
29506
|
+
size += next.value.byteLength;
|
|
29507
|
+
if (size > 16 * 1024)
|
|
29508
|
+
throw new Error("GitHub metadata too large");
|
|
29509
|
+
chunks.push(next.value);
|
|
29510
|
+
}
|
|
29511
|
+
} finally {
|
|
29512
|
+
await reader.cancel().catch(() => void 0);
|
|
29513
|
+
}
|
|
29514
|
+
const envelope = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
29515
|
+
return GitHubProjectStatusSchema.parse(envelope.data);
|
|
29516
|
+
},
|
|
29517
|
+
createEngine: (target) => createGitHubCodeSync({
|
|
29518
|
+
apiUrl: config2.apiUrl,
|
|
29519
|
+
token: options.grant.token,
|
|
29520
|
+
projectId: config2.projectId,
|
|
29521
|
+
actorId: actor.id,
|
|
29522
|
+
workspacePath: config2.workspacePath,
|
|
29523
|
+
repository: target.repository,
|
|
29524
|
+
baseBranch: target.base_branch,
|
|
29525
|
+
revision: target.revision,
|
|
29526
|
+
...options.dshHome === void 0 ? {} : {
|
|
29527
|
+
stateRoot: path10.join(options.dshHome, "gatherthread-code-sync", createHash10("sha256").update(config2.workspacePath).digest("hex"))
|
|
29528
|
+
}
|
|
29529
|
+
}),
|
|
29530
|
+
api: new HttpCollaborationClient({ baseUrl: config2.apiUrl, bearerToken: options.grant.token, signal: ownerAbort.signal }),
|
|
29531
|
+
runtimes: () => new Map([...codeRuntimes].flatMap(([id, connector]) => connector.stopped || !connector.executionRuntimeId ? [] : [[id, connector.executionRuntimeId]])),
|
|
29532
|
+
isBusy: () => nativeWorkspace.isBusy()
|
|
29533
|
+
});
|
|
29534
|
+
await githubSync.start().catch(() => void 0);
|
|
29535
|
+
let localCodeTail = Promise.resolve();
|
|
29536
|
+
function scheduleCode(operation) {
|
|
29537
|
+
const task = localCodeTail.then(operation);
|
|
29538
|
+
localCodeTail = task.catch(() => void 0);
|
|
29539
|
+
return task;
|
|
29540
|
+
}
|
|
29541
|
+
let codePolling = false;
|
|
27375
29542
|
const codeTimer = setInterval(() => {
|
|
27376
|
-
|
|
29543
|
+
if (codePolling)
|
|
29544
|
+
return;
|
|
29545
|
+
codePolling = true;
|
|
29546
|
+
void scheduleCode(async () => {
|
|
29547
|
+
await codeSync.poll();
|
|
29548
|
+
await githubSync.poll();
|
|
29549
|
+
}).catch(() => void 0).finally(() => {
|
|
29550
|
+
codePolling = false;
|
|
29551
|
+
});
|
|
27377
29552
|
}, 5e3);
|
|
27378
29553
|
codeTimer.unref();
|
|
27379
29554
|
let stopPromise;
|
|
@@ -27383,14 +29558,19 @@ async function createProductionOwner(options) {
|
|
|
27383
29558
|
setLocalAutoUpload: (sessionId, enabled) => manager.setLocalAutoUpload(sessionId, enabled),
|
|
27384
29559
|
uploadLocalTurns: (sessionId) => manager.uploadLocalTurns(sessionId),
|
|
27385
29560
|
codeSyncView: () => codeSync.view(),
|
|
27386
|
-
authorizeCodeSync: (enabled) => codeSync.authorize(enabled),
|
|
27387
|
-
executeCodeSync: (action) => codeSync.execute(action),
|
|
29561
|
+
authorizeCodeSync: (enabled) => scheduleCode(() => codeSync.authorize(enabled)),
|
|
29562
|
+
executeCodeSync: (action) => scheduleCode(() => codeSync.execute(action)),
|
|
29563
|
+
githubSyncView: () => githubSync.view(),
|
|
29564
|
+
authorizeGitHubSync: (enabled, expected) => scheduleCode(() => githubSync.authorize(enabled, expected)),
|
|
29565
|
+
executeGitHubSync: (action) => scheduleCode(() => githubSync.execute(action)),
|
|
27388
29566
|
stop() {
|
|
27389
29567
|
stopPromise ??= (async () => {
|
|
27390
29568
|
options.signal.removeEventListener("abort", abortFromRoot);
|
|
27391
29569
|
clearInterval(codeTimer);
|
|
27392
29570
|
ownerAbort.abort(new Error("GatherThread native DSH manager stopped"));
|
|
29571
|
+
await localCodeTail;
|
|
27393
29572
|
await codeSync.stop();
|
|
29573
|
+
await githubSync.stop();
|
|
27394
29574
|
await manager.stop();
|
|
27395
29575
|
nativeWorkspace.dispose();
|
|
27396
29576
|
})();
|
|
@@ -27460,6 +29640,7 @@ function createNativeManagedConnector(options) {
|
|
|
27460
29640
|
await connector.start();
|
|
27461
29641
|
},
|
|
27462
29642
|
updateExecutionProfiles: (profiles) => connector.updateExecutionProfiles(profiles),
|
|
29643
|
+
updateSessionSummary: (session) => connector.updateSessionSummary(session),
|
|
27463
29644
|
localSyncStatus: () => connector.localSyncStatus(),
|
|
27464
29645
|
setLocalAutoUpload: (enabled) => connector.setLocalAutoUpload(enabled),
|
|
27465
29646
|
uploadLocalTurns: () => connector.uploadLocalTurns(),
|
|
@@ -27486,7 +29667,7 @@ async function resolveNativeProjectWorkspace(grant, binding, signal, homeDirecto
|
|
|
27486
29667
|
}
|
|
27487
29668
|
function nativeDshHomeFromEnvironment() {
|
|
27488
29669
|
const value = process.env.DSH_HOME?.trim();
|
|
27489
|
-
return value ?
|
|
29670
|
+
return value ? path10.resolve(value) : void 0;
|
|
27490
29671
|
}
|
|
27491
29672
|
async function listNativeLlmCatalog(contextValue, signal) {
|
|
27492
29673
|
const llm = requireLlm(contextValue);
|