@posthog/agent 2.3.385 → 2.3.387
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/claude/session/jsonl-hydration.d.ts +1 -0
- package/dist/agent.d.ts +1 -0
- package/dist/agent.js +21 -2
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.d.ts +39 -0
- package/dist/handoff-checkpoint.js +6679 -0
- package/dist/handoff-checkpoint.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/logger-RC7sPv0S.d.ts +24 -0
- package/dist/posthog-api.d.ts +1 -0
- package/dist/posthog-api.js +19 -2
- package/dist/posthog-api.js.map +1 -1
- package/dist/resume.d.ts +71 -0
- package/dist/resume.js +6838 -0
- package/dist/resume.js.map +1 -0
- package/dist/server/agent-server.d.ts +5 -18
- package/dist/server/agent-server.js +1373 -432
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1370 -429
- package/dist/server/bin.cjs.map +1 -1
- package/dist/server/schemas.d.ts +191 -0
- package/dist/server/schemas.js +108 -0
- package/dist/server/schemas.js.map +1 -0
- package/dist/tree-tracker.d.ts +68 -0
- package/dist/tree-tracker.js +6431 -0
- package/dist/tree-tracker.js.map +1 -0
- package/dist/types.d.ts +18 -1
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -1
- package/package.json +17 -1
- package/src/acp-extensions.ts +3 -0
- package/src/handoff-checkpoint.test.ts +183 -0
- package/src/handoff-checkpoint.ts +361 -0
- package/src/posthog-api.test.ts +29 -0
- package/src/posthog-api.ts +5 -1
- package/src/resume.ts +58 -1
- package/src/sagas/apply-snapshot-saga.ts +7 -0
- package/src/sagas/capture-tree-saga.ts +10 -3
- package/src/sagas/resume-saga.ts +32 -0
- package/src/sagas/test-fixtures.ts +46 -0
- package/src/server/agent-server.ts +76 -57
- package/src/server/schemas.ts +21 -2
- package/src/types.ts +24 -0
|
@@ -809,15 +809,15 @@ var require_src2 = __commonJS({
|
|
|
809
809
|
var fs_1 = __require("fs");
|
|
810
810
|
var debug_1 = __importDefault(require_src());
|
|
811
811
|
var log = debug_1.default("@kwsites/file-exists");
|
|
812
|
-
function check(
|
|
813
|
-
log(`checking %s`,
|
|
812
|
+
function check(path17, isFile2, isDirectory) {
|
|
813
|
+
log(`checking %s`, path17);
|
|
814
814
|
try {
|
|
815
|
-
const
|
|
816
|
-
if (
|
|
815
|
+
const stat4 = fs_1.statSync(path17);
|
|
816
|
+
if (stat4.isFile() && isFile2) {
|
|
817
817
|
log(`[OK] path represents a file`);
|
|
818
818
|
return true;
|
|
819
819
|
}
|
|
820
|
-
if (
|
|
820
|
+
if (stat4.isDirectory() && isDirectory) {
|
|
821
821
|
log(`[OK] path represents a directory`);
|
|
822
822
|
return true;
|
|
823
823
|
}
|
|
@@ -832,8 +832,8 @@ var require_src2 = __commonJS({
|
|
|
832
832
|
throw e;
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
|
-
function exists2(
|
|
836
|
-
return check(
|
|
835
|
+
function exists2(path17, type = exports2.READABLE) {
|
|
836
|
+
return check(path17, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
|
|
837
837
|
}
|
|
838
838
|
exports2.exists = exists2;
|
|
839
839
|
exports2.FILE = 1;
|
|
@@ -929,11 +929,11 @@ var require_tree_sitter = __commonJS({
|
|
|
929
929
|
throw toThrow;
|
|
930
930
|
};
|
|
931
931
|
var scriptDirectory = "";
|
|
932
|
-
function locateFile(
|
|
932
|
+
function locateFile(path17) {
|
|
933
933
|
if (Module["locateFile"]) {
|
|
934
|
-
return Module["locateFile"](
|
|
934
|
+
return Module["locateFile"](path17, scriptDirectory);
|
|
935
935
|
}
|
|
936
|
-
return scriptDirectory +
|
|
936
|
+
return scriptDirectory + path17;
|
|
937
937
|
}
|
|
938
938
|
var readAsync, readBinary;
|
|
939
939
|
if (ENVIRONMENT_IS_NODE) {
|
|
@@ -947,10 +947,10 @@ var require_tree_sitter = __commonJS({
|
|
|
947
947
|
};
|
|
948
948
|
readAsync = (filename, binary2 = true) => {
|
|
949
949
|
filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename);
|
|
950
|
-
return new Promise((
|
|
950
|
+
return new Promise((resolve7, reject) => {
|
|
951
951
|
fs.readFile(filename, binary2 ? void 0 : "utf8", (err2, data) => {
|
|
952
952
|
if (err2) reject(err2);
|
|
953
|
-
else
|
|
953
|
+
else resolve7(binary2 ? data.buffer : data);
|
|
954
954
|
});
|
|
955
955
|
});
|
|
956
956
|
};
|
|
@@ -991,13 +991,13 @@ var require_tree_sitter = __commonJS({
|
|
|
991
991
|
}
|
|
992
992
|
readAsync = (url) => {
|
|
993
993
|
if (isFileURI(url)) {
|
|
994
|
-
return new Promise((reject,
|
|
994
|
+
return new Promise((reject, resolve7) => {
|
|
995
995
|
var xhr = new XMLHttpRequest();
|
|
996
996
|
xhr.open("GET", url, true);
|
|
997
997
|
xhr.responseType = "arraybuffer";
|
|
998
998
|
xhr.onload = () => {
|
|
999
999
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
1000
|
-
|
|
1000
|
+
resolve7(xhr.response);
|
|
1001
1001
|
}
|
|
1002
1002
|
reject(xhr.status);
|
|
1003
1003
|
};
|
|
@@ -1957,8 +1957,8 @@ var require_tree_sitter = __commonJS({
|
|
|
1957
1957
|
}
|
|
1958
1958
|
var libFile = locateFile(libName2);
|
|
1959
1959
|
if (flags2.loadAsync) {
|
|
1960
|
-
return new Promise(function(
|
|
1961
|
-
asyncLoad(libFile,
|
|
1960
|
+
return new Promise(function(resolve7, reject) {
|
|
1961
|
+
asyncLoad(libFile, resolve7, reject);
|
|
1962
1962
|
});
|
|
1963
1963
|
}
|
|
1964
1964
|
if (!readBinary) {
|
|
@@ -3454,8 +3454,8 @@ var require_tree_sitter = __commonJS({
|
|
|
3454
3454
|
} else {
|
|
3455
3455
|
const url = input;
|
|
3456
3456
|
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
3457
|
-
const
|
|
3458
|
-
bytes = Promise.resolve(
|
|
3457
|
+
const fs14 = __require("fs");
|
|
3458
|
+
bytes = Promise.resolve(fs14.readFileSync(url));
|
|
3459
3459
|
} else {
|
|
3460
3460
|
bytes = fetch(url).then((response) => response.arrayBuffer().then((buffer) => {
|
|
3461
3461
|
if (response.ok) {
|
|
@@ -3784,8 +3784,8 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
3784
3784
|
});
|
|
3785
3785
|
|
|
3786
3786
|
// src/server/agent-server.ts
|
|
3787
|
-
import { mkdir as
|
|
3788
|
-
import { basename as basename2, join as
|
|
3787
|
+
import { mkdir as mkdir8, writeFile as writeFile6 } from "fs/promises";
|
|
3788
|
+
import { basename as basename2, join as join14 } from "path";
|
|
3789
3789
|
import { pathToFileURL } from "url";
|
|
3790
3790
|
import {
|
|
3791
3791
|
ClientSideConnection as ClientSideConnection2,
|
|
@@ -3835,8 +3835,8 @@ function pathspec(...paths) {
|
|
|
3835
3835
|
cache.set(key, paths);
|
|
3836
3836
|
return key;
|
|
3837
3837
|
}
|
|
3838
|
-
function isPathSpec(
|
|
3839
|
-
return
|
|
3838
|
+
function isPathSpec(path17) {
|
|
3839
|
+
return path17 instanceof String && cache.has(path17);
|
|
3840
3840
|
}
|
|
3841
3841
|
function toPaths(pathSpec) {
|
|
3842
3842
|
return cache.get(pathSpec) || [];
|
|
@@ -3925,8 +3925,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
3925
3925
|
function forEachLineWithContent(input, callback) {
|
|
3926
3926
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
3927
3927
|
}
|
|
3928
|
-
function folderExists(
|
|
3929
|
-
return (0, import_file_exists.exists)(
|
|
3928
|
+
function folderExists(path17) {
|
|
3929
|
+
return (0, import_file_exists.exists)(path17, import_file_exists.FOLDER);
|
|
3930
3930
|
}
|
|
3931
3931
|
function append(target, item) {
|
|
3932
3932
|
if (Array.isArray(target)) {
|
|
@@ -4330,8 +4330,8 @@ function checkIsRepoRootTask() {
|
|
|
4330
4330
|
commands,
|
|
4331
4331
|
format: "utf-8",
|
|
4332
4332
|
onError,
|
|
4333
|
-
parser(
|
|
4334
|
-
return /^\.(git)?$/.test(
|
|
4333
|
+
parser(path17) {
|
|
4334
|
+
return /^\.(git)?$/.test(path17.trim());
|
|
4335
4335
|
}
|
|
4336
4336
|
};
|
|
4337
4337
|
}
|
|
@@ -4765,11 +4765,11 @@ function parseGrep(grep) {
|
|
|
4765
4765
|
const paths = /* @__PURE__ */ new Set();
|
|
4766
4766
|
const results = {};
|
|
4767
4767
|
forEachLineWithContent(grep, (input) => {
|
|
4768
|
-
const [
|
|
4769
|
-
paths.add(
|
|
4770
|
-
(results[
|
|
4768
|
+
const [path17, line, preview] = input.split(NULL);
|
|
4769
|
+
paths.add(path17);
|
|
4770
|
+
(results[path17] = results[path17] || []).push({
|
|
4771
4771
|
line: asNumber(line),
|
|
4772
|
-
path:
|
|
4772
|
+
path: path17,
|
|
4773
4773
|
preview
|
|
4774
4774
|
});
|
|
4775
4775
|
});
|
|
@@ -5534,14 +5534,14 @@ var init_hash_object = __esm({
|
|
|
5534
5534
|
init_task();
|
|
5535
5535
|
}
|
|
5536
5536
|
});
|
|
5537
|
-
function parseInit(bare,
|
|
5537
|
+
function parseInit(bare, path17, text2) {
|
|
5538
5538
|
const response = String(text2).trim();
|
|
5539
5539
|
let result;
|
|
5540
5540
|
if (result = initResponseRegex.exec(response)) {
|
|
5541
|
-
return new InitSummary(bare,
|
|
5541
|
+
return new InitSummary(bare, path17, false, result[1]);
|
|
5542
5542
|
}
|
|
5543
5543
|
if (result = reInitResponseRegex.exec(response)) {
|
|
5544
|
-
return new InitSummary(bare,
|
|
5544
|
+
return new InitSummary(bare, path17, true, result[1]);
|
|
5545
5545
|
}
|
|
5546
5546
|
let gitDir = "";
|
|
5547
5547
|
const tokens = response.split(" ");
|
|
@@ -5552,7 +5552,7 @@ function parseInit(bare, path15, text2) {
|
|
|
5552
5552
|
break;
|
|
5553
5553
|
}
|
|
5554
5554
|
}
|
|
5555
|
-
return new InitSummary(bare,
|
|
5555
|
+
return new InitSummary(bare, path17, /^re/i.test(response), gitDir);
|
|
5556
5556
|
}
|
|
5557
5557
|
var InitSummary;
|
|
5558
5558
|
var initResponseRegex;
|
|
@@ -5561,9 +5561,9 @@ var init_InitSummary = __esm({
|
|
|
5561
5561
|
"src/lib/responses/InitSummary.ts"() {
|
|
5562
5562
|
"use strict";
|
|
5563
5563
|
InitSummary = class {
|
|
5564
|
-
constructor(bare,
|
|
5564
|
+
constructor(bare, path17, existing, gitDir) {
|
|
5565
5565
|
this.bare = bare;
|
|
5566
|
-
this.path =
|
|
5566
|
+
this.path = path17;
|
|
5567
5567
|
this.existing = existing;
|
|
5568
5568
|
this.gitDir = gitDir;
|
|
5569
5569
|
}
|
|
@@ -5575,7 +5575,7 @@ var init_InitSummary = __esm({
|
|
|
5575
5575
|
function hasBareCommand(command) {
|
|
5576
5576
|
return command.includes(bareCommand);
|
|
5577
5577
|
}
|
|
5578
|
-
function initTask(bare = false,
|
|
5578
|
+
function initTask(bare = false, path17, customArgs) {
|
|
5579
5579
|
const commands = ["init", ...customArgs];
|
|
5580
5580
|
if (bare && !hasBareCommand(commands)) {
|
|
5581
5581
|
commands.splice(1, 0, bareCommand);
|
|
@@ -5584,7 +5584,7 @@ function initTask(bare = false, path15, customArgs) {
|
|
|
5584
5584
|
commands,
|
|
5585
5585
|
format: "utf-8",
|
|
5586
5586
|
parser(text2) {
|
|
5587
|
-
return parseInit(commands.includes("--bare"),
|
|
5587
|
+
return parseInit(commands.includes("--bare"), path17, text2);
|
|
5588
5588
|
}
|
|
5589
5589
|
};
|
|
5590
5590
|
}
|
|
@@ -6400,12 +6400,12 @@ var init_FileStatusSummary = __esm({
|
|
|
6400
6400
|
"use strict";
|
|
6401
6401
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
6402
6402
|
FileStatusSummary = class {
|
|
6403
|
-
constructor(
|
|
6404
|
-
this.path =
|
|
6403
|
+
constructor(path17, index, working_dir) {
|
|
6404
|
+
this.path = path17;
|
|
6405
6405
|
this.index = index;
|
|
6406
6406
|
this.working_dir = working_dir;
|
|
6407
6407
|
if (index === "R" || working_dir === "R") {
|
|
6408
|
-
const detail = fromPathRegex.exec(
|
|
6408
|
+
const detail = fromPathRegex.exec(path17) || [null, path17, path17];
|
|
6409
6409
|
this.from = detail[2] || "";
|
|
6410
6410
|
this.path = detail[1] || "";
|
|
6411
6411
|
}
|
|
@@ -6436,14 +6436,14 @@ function splitLine(result, lineStr) {
|
|
|
6436
6436
|
default:
|
|
6437
6437
|
return;
|
|
6438
6438
|
}
|
|
6439
|
-
function data(index, workingDir,
|
|
6439
|
+
function data(index, workingDir, path17) {
|
|
6440
6440
|
const raw = `${index}${workingDir}`;
|
|
6441
6441
|
const handler = parsers6.get(raw);
|
|
6442
6442
|
if (handler) {
|
|
6443
|
-
handler(result,
|
|
6443
|
+
handler(result, path17);
|
|
6444
6444
|
}
|
|
6445
6445
|
if (raw !== "##" && raw !== "!!") {
|
|
6446
|
-
result.files.push(new FileStatusSummary(
|
|
6446
|
+
result.files.push(new FileStatusSummary(path17, index, workingDir));
|
|
6447
6447
|
}
|
|
6448
6448
|
}
|
|
6449
6449
|
}
|
|
@@ -6756,9 +6756,9 @@ var init_simple_git_api = __esm({
|
|
|
6756
6756
|
next
|
|
6757
6757
|
);
|
|
6758
6758
|
}
|
|
6759
|
-
hashObject(
|
|
6759
|
+
hashObject(path17, write) {
|
|
6760
6760
|
return this._runTask(
|
|
6761
|
-
hashObjectTask(
|
|
6761
|
+
hashObjectTask(path17, write === true),
|
|
6762
6762
|
trailingFunctionArgument(arguments)
|
|
6763
6763
|
);
|
|
6764
6764
|
}
|
|
@@ -7111,8 +7111,8 @@ var init_branch = __esm({
|
|
|
7111
7111
|
}
|
|
7112
7112
|
});
|
|
7113
7113
|
function toPath(input) {
|
|
7114
|
-
const
|
|
7115
|
-
return
|
|
7114
|
+
const path17 = input.trim().replace(/^["']|["']$/g, "");
|
|
7115
|
+
return path17 && normalize(path17);
|
|
7116
7116
|
}
|
|
7117
7117
|
var parseCheckIgnore;
|
|
7118
7118
|
var init_CheckIgnore = __esm({
|
|
@@ -7426,8 +7426,8 @@ __export(sub_module_exports, {
|
|
|
7426
7426
|
subModuleTask: () => subModuleTask,
|
|
7427
7427
|
updateSubModuleTask: () => updateSubModuleTask
|
|
7428
7428
|
});
|
|
7429
|
-
function addSubModuleTask(repo,
|
|
7430
|
-
return subModuleTask(["add", repo,
|
|
7429
|
+
function addSubModuleTask(repo, path17) {
|
|
7430
|
+
return subModuleTask(["add", repo, path17]);
|
|
7431
7431
|
}
|
|
7432
7432
|
function initSubModuleTask(customArgs) {
|
|
7433
7433
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -7757,8 +7757,8 @@ var require_git = __commonJS2({
|
|
|
7757
7757
|
}
|
|
7758
7758
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
7759
7759
|
};
|
|
7760
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
7761
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
7760
|
+
Git2.prototype.submoduleAdd = function(repo, path17, then) {
|
|
7761
|
+
return this._runTask(addSubModuleTask2(repo, path17), trailingFunctionArgument2(arguments));
|
|
7762
7762
|
};
|
|
7763
7763
|
Git2.prototype.submoduleUpdate = function(args2, then) {
|
|
7764
7764
|
return this._runTask(
|
|
@@ -8374,10 +8374,10 @@ async function getIndexLockPath(repoPath) {
|
|
|
8374
8374
|
async function getLockInfo(repoPath) {
|
|
8375
8375
|
const lockPath = await getIndexLockPath(repoPath);
|
|
8376
8376
|
try {
|
|
8377
|
-
const
|
|
8377
|
+
const stat4 = await fs2.stat(lockPath);
|
|
8378
8378
|
return {
|
|
8379
8379
|
path: lockPath,
|
|
8380
|
-
ageMs: Date.now() -
|
|
8380
|
+
ageMs: Date.now() - stat4.mtimeMs
|
|
8381
8381
|
};
|
|
8382
8382
|
} catch {
|
|
8383
8383
|
return null;
|
|
@@ -8412,10 +8412,10 @@ var AsyncReaderWriterLock = class {
|
|
|
8412
8412
|
this.readers++;
|
|
8413
8413
|
return;
|
|
8414
8414
|
}
|
|
8415
|
-
return new Promise((
|
|
8415
|
+
return new Promise((resolve7) => {
|
|
8416
8416
|
this.readQueue.push(() => {
|
|
8417
8417
|
this.readers++;
|
|
8418
|
-
|
|
8418
|
+
resolve7();
|
|
8419
8419
|
});
|
|
8420
8420
|
});
|
|
8421
8421
|
}
|
|
@@ -8429,11 +8429,11 @@ var AsyncReaderWriterLock = class {
|
|
|
8429
8429
|
return;
|
|
8430
8430
|
}
|
|
8431
8431
|
this.writerWaiting = true;
|
|
8432
|
-
return new Promise((
|
|
8432
|
+
return new Promise((resolve7) => {
|
|
8433
8433
|
this.writeQueue.push(() => {
|
|
8434
8434
|
this.writerWaiting = this.writeQueue.length > 0;
|
|
8435
8435
|
this.writer = true;
|
|
8436
|
-
|
|
8436
|
+
resolve7();
|
|
8437
8437
|
});
|
|
8438
8438
|
});
|
|
8439
8439
|
}
|
|
@@ -8605,7 +8605,7 @@ import { z as z4 } from "zod";
|
|
|
8605
8605
|
// package.json
|
|
8606
8606
|
var package_default = {
|
|
8607
8607
|
name: "@posthog/agent",
|
|
8608
|
-
version: "2.3.
|
|
8608
|
+
version: "2.3.387",
|
|
8609
8609
|
repository: "https://github.com/PostHog/code",
|
|
8610
8610
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8611
8611
|
exports: {
|
|
@@ -8661,9 +8661,25 @@ var package_default = {
|
|
|
8661
8661
|
types: "./dist/execution-mode.d.ts",
|
|
8662
8662
|
import: "./dist/execution-mode.js"
|
|
8663
8663
|
},
|
|
8664
|
+
"./resume": {
|
|
8665
|
+
types: "./dist/resume.d.ts",
|
|
8666
|
+
import: "./dist/resume.js"
|
|
8667
|
+
},
|
|
8668
|
+
"./handoff-checkpoint": {
|
|
8669
|
+
types: "./dist/handoff-checkpoint.d.ts",
|
|
8670
|
+
import: "./dist/handoff-checkpoint.js"
|
|
8671
|
+
},
|
|
8672
|
+
"./tree-tracker": {
|
|
8673
|
+
types: "./dist/tree-tracker.d.ts",
|
|
8674
|
+
import: "./dist/tree-tracker.js"
|
|
8675
|
+
},
|
|
8664
8676
|
"./server": {
|
|
8665
8677
|
types: "./dist/server/agent-server.d.ts",
|
|
8666
8678
|
import: "./dist/server/agent-server.js"
|
|
8679
|
+
},
|
|
8680
|
+
"./server/schemas": {
|
|
8681
|
+
types: "./dist/server/schemas.d.ts",
|
|
8682
|
+
import: "./dist/server/schemas.js"
|
|
8667
8683
|
}
|
|
8668
8684
|
},
|
|
8669
8685
|
bin: {
|
|
@@ -8754,6 +8770,8 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
8754
8770
|
SDK_SESSION: "_posthog/sdk_session",
|
|
8755
8771
|
/** Tree state snapshot captured (git tree hash + file archive) */
|
|
8756
8772
|
TREE_SNAPSHOT: "_posthog/tree_snapshot",
|
|
8773
|
+
/** Git checkpoint captured for handoff */
|
|
8774
|
+
GIT_CHECKPOINT: "_posthog/git_checkpoint",
|
|
8757
8775
|
/** Agent mode changed (interactive/background) */
|
|
8758
8776
|
MODE_CHANGE: "_posthog/mode_change",
|
|
8759
8777
|
/** Request to resume a session from previous state */
|
|
@@ -8860,17 +8878,17 @@ var Pushable = class {
|
|
|
8860
8878
|
resolvers = [];
|
|
8861
8879
|
done = false;
|
|
8862
8880
|
push(item) {
|
|
8863
|
-
const
|
|
8864
|
-
if (
|
|
8865
|
-
|
|
8881
|
+
const resolve7 = this.resolvers.shift();
|
|
8882
|
+
if (resolve7) {
|
|
8883
|
+
resolve7({ value: item, done: false });
|
|
8866
8884
|
} else {
|
|
8867
8885
|
this.queue.push(item);
|
|
8868
8886
|
}
|
|
8869
8887
|
}
|
|
8870
8888
|
end() {
|
|
8871
8889
|
this.done = true;
|
|
8872
|
-
for (const
|
|
8873
|
-
|
|
8890
|
+
for (const resolve7 of this.resolvers) {
|
|
8891
|
+
resolve7({ value: void 0, done: true });
|
|
8874
8892
|
}
|
|
8875
8893
|
this.resolvers = [];
|
|
8876
8894
|
}
|
|
@@ -8887,8 +8905,8 @@ var Pushable = class {
|
|
|
8887
8905
|
done: true
|
|
8888
8906
|
});
|
|
8889
8907
|
}
|
|
8890
|
-
return new Promise((
|
|
8891
|
-
this.resolvers.push(
|
|
8908
|
+
return new Promise((resolve7) => {
|
|
8909
|
+
this.resolvers.push(resolve7);
|
|
8892
8910
|
});
|
|
8893
8911
|
}
|
|
8894
8912
|
};
|
|
@@ -9002,20 +9020,20 @@ function nodeReadableToWebReadable(nodeStream) {
|
|
|
9002
9020
|
function nodeWritableToWebWritable(nodeStream) {
|
|
9003
9021
|
return new WritableStream2({
|
|
9004
9022
|
write(chunk) {
|
|
9005
|
-
return new Promise((
|
|
9023
|
+
return new Promise((resolve7, reject) => {
|
|
9006
9024
|
const ok = nodeStream.write(Buffer.from(chunk), (err2) => {
|
|
9007
9025
|
if (err2) reject(err2);
|
|
9008
9026
|
});
|
|
9009
9027
|
if (ok) {
|
|
9010
|
-
|
|
9028
|
+
resolve7();
|
|
9011
9029
|
} else {
|
|
9012
|
-
nodeStream.once("drain",
|
|
9030
|
+
nodeStream.once("drain", resolve7);
|
|
9013
9031
|
}
|
|
9014
9032
|
});
|
|
9015
9033
|
},
|
|
9016
9034
|
close() {
|
|
9017
|
-
return new Promise((
|
|
9018
|
-
nodeStream.end(
|
|
9035
|
+
return new Promise((resolve7) => {
|
|
9036
|
+
nodeStream.end(resolve7);
|
|
9019
9037
|
});
|
|
9020
9038
|
},
|
|
9021
9039
|
abort(reason) {
|
|
@@ -12962,9 +12980,9 @@ var PostHogEnricher = class {
|
|
|
12962
12980
|
}
|
|
12963
12981
|
let mtimeMs = 0;
|
|
12964
12982
|
try {
|
|
12965
|
-
const
|
|
12966
|
-
mtimeMs =
|
|
12967
|
-
if (
|
|
12983
|
+
const stat22 = await fs4.stat(absPath);
|
|
12984
|
+
mtimeMs = stat22.mtimeMs;
|
|
12985
|
+
if (stat22.size > MAX_WRAPPER_SOURCE_BYTES) {
|
|
12968
12986
|
return this.setWrapperCache(absPath, mtimeMs, []);
|
|
12969
12987
|
}
|
|
12970
12988
|
} catch {
|
|
@@ -13130,7 +13148,7 @@ async function buildWrapperContext(deps, content, langId, absPath) {
|
|
|
13130
13148
|
// src/utils/common.ts
|
|
13131
13149
|
async function withTimeout(operation, timeoutMs) {
|
|
13132
13150
|
const timeoutPromise = new Promise(
|
|
13133
|
-
(
|
|
13151
|
+
(resolve7) => setTimeout(() => resolve7({ result: "timeout" }), timeoutMs)
|
|
13134
13152
|
);
|
|
13135
13153
|
const operationPromise = operation.then((value) => ({
|
|
13136
13154
|
result: "success",
|
|
@@ -13428,8 +13446,8 @@ var ToolContentBuilder = class {
|
|
|
13428
13446
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
13429
13447
|
return this;
|
|
13430
13448
|
}
|
|
13431
|
-
diff(
|
|
13432
|
-
this.items.push({ type: "diff", path:
|
|
13449
|
+
diff(path17, oldText, newText) {
|
|
13450
|
+
this.items.push({ type: "diff", path: path17, oldText, newText });
|
|
13433
13451
|
return this;
|
|
13434
13452
|
}
|
|
13435
13453
|
build() {
|
|
@@ -13616,7 +13634,7 @@ function buildToolKey(serverName, toolName) {
|
|
|
13616
13634
|
return `mcp__${serverName}__${toolName}`;
|
|
13617
13635
|
}
|
|
13618
13636
|
function delay2(ms) {
|
|
13619
|
-
return new Promise((
|
|
13637
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
13620
13638
|
}
|
|
13621
13639
|
async function fetchMcpToolMetadata(q, logger = new Logger({ debug: false, prefix: "[McpToolMetadata]" })) {
|
|
13622
13640
|
let retries = 0;
|
|
@@ -15937,8 +15955,8 @@ var AsyncMutex = class {
|
|
|
15937
15955
|
this.locked = true;
|
|
15938
15956
|
return;
|
|
15939
15957
|
}
|
|
15940
|
-
return new Promise((
|
|
15941
|
-
this.queue.push(
|
|
15958
|
+
return new Promise((resolve7) => {
|
|
15959
|
+
this.queue.push(resolve7);
|
|
15942
15960
|
});
|
|
15943
15961
|
}
|
|
15944
15962
|
release() {
|
|
@@ -16465,8 +16483,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
16465
16483
|
if (this.session.promptRunning) {
|
|
16466
16484
|
this.session.input.push(userMessage);
|
|
16467
16485
|
const order = this.session.nextPendingOrder++;
|
|
16468
|
-
const cancelled = await new Promise((
|
|
16469
|
-
this.session.pendingMessages.set(promptUuid, { resolve:
|
|
16486
|
+
const cancelled = await new Promise((resolve7) => {
|
|
16487
|
+
this.session.pendingMessages.set(promptUuid, { resolve: resolve7, order });
|
|
16470
16488
|
});
|
|
16471
16489
|
if (cancelled) {
|
|
16472
16490
|
return { stopReason: "cancelled" };
|
|
@@ -17347,7 +17365,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
17347
17365
|
*/
|
|
17348
17366
|
deferBackgroundFetches(q) {
|
|
17349
17367
|
Promise.all([
|
|
17350
|
-
new Promise((
|
|
17368
|
+
new Promise((resolve7) => setTimeout(resolve7, 10)).then(
|
|
17351
17369
|
() => this.sendAvailableCommandsUpdate()
|
|
17352
17370
|
),
|
|
17353
17371
|
fetchMcpToolMetadata(q, this.logger).then(() => {
|
|
@@ -18315,245 +18333,19 @@ function createCodexConnection(config) {
|
|
|
18315
18333
|
};
|
|
18316
18334
|
}
|
|
18317
18335
|
|
|
18318
|
-
// src/
|
|
18336
|
+
// src/handoff-checkpoint.ts
|
|
18337
|
+
import { mkdir as mkdir4, readFile as readFile4, rm as rm4, writeFile as writeFile2 } from "fs/promises";
|
|
18338
|
+
import { join as join9 } from "path";
|
|
18339
|
+
|
|
18340
|
+
// ../git/dist/handoff.js
|
|
18341
|
+
import { spawn as spawn4 } from "child_process";
|
|
18342
|
+
import { copyFile, mkdir as mkdir3, readFile as readFile3, rm as rm3, stat as stat3 } from "fs/promises";
|
|
18343
|
+
import path13 from "path";
|
|
18344
|
+
|
|
18345
|
+
// ../git/dist/sagas/checkpoint.js
|
|
18319
18346
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
18320
18347
|
import * as fs10 from "fs/promises";
|
|
18321
|
-
import * as os6 from "os";
|
|
18322
18348
|
import * as path12 from "path";
|
|
18323
|
-
var CHARS_PER_TOKEN = 4;
|
|
18324
|
-
var DEFAULT_MAX_TOKENS = 15e4;
|
|
18325
|
-
function estimateTurnTokens(turn) {
|
|
18326
|
-
let chars = 0;
|
|
18327
|
-
for (const block of turn.content) {
|
|
18328
|
-
if ("text" in block && typeof block.text === "string") {
|
|
18329
|
-
chars += block.text.length;
|
|
18330
|
-
}
|
|
18331
|
-
}
|
|
18332
|
-
if (turn.toolCalls) {
|
|
18333
|
-
for (const tc of turn.toolCalls) {
|
|
18334
|
-
chars += JSON.stringify(tc.input ?? "").length;
|
|
18335
|
-
if (tc.result !== void 0) {
|
|
18336
|
-
chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
|
|
18337
|
-
}
|
|
18338
|
-
}
|
|
18339
|
-
}
|
|
18340
|
-
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
18341
|
-
}
|
|
18342
|
-
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
18343
|
-
let budget = maxTokens;
|
|
18344
|
-
let startIndex = turns.length;
|
|
18345
|
-
for (let i2 = turns.length - 1; i2 >= 0; i2--) {
|
|
18346
|
-
const cost = estimateTurnTokens(turns[i2]);
|
|
18347
|
-
if (cost > budget) break;
|
|
18348
|
-
budget -= cost;
|
|
18349
|
-
startIndex = i2;
|
|
18350
|
-
}
|
|
18351
|
-
while (startIndex < turns.length && turns[startIndex].role !== "user") {
|
|
18352
|
-
startIndex++;
|
|
18353
|
-
}
|
|
18354
|
-
return turns.slice(startIndex);
|
|
18355
|
-
}
|
|
18356
|
-
|
|
18357
|
-
// src/utils/gateway.ts
|
|
18358
|
-
function getGatewayBaseUrl(posthogHost) {
|
|
18359
|
-
const url = new URL(posthogHost);
|
|
18360
|
-
const hostname = url.hostname;
|
|
18361
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
18362
|
-
return `${url.protocol}//localhost:3308`;
|
|
18363
|
-
}
|
|
18364
|
-
if (hostname === "host.docker.internal") {
|
|
18365
|
-
return `${url.protocol}//host.docker.internal:3308`;
|
|
18366
|
-
}
|
|
18367
|
-
const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us";
|
|
18368
|
-
return `https://gateway.${region}.posthog.com`;
|
|
18369
|
-
}
|
|
18370
|
-
function getLlmGatewayUrl(posthogHost, product = "posthog_code") {
|
|
18371
|
-
return `${getGatewayBaseUrl(posthogHost)}/${product}`;
|
|
18372
|
-
}
|
|
18373
|
-
|
|
18374
|
-
// src/posthog-api.ts
|
|
18375
|
-
var DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${package_default.version}`;
|
|
18376
|
-
var PostHogAPIClient = class {
|
|
18377
|
-
config;
|
|
18378
|
-
constructor(config) {
|
|
18379
|
-
this.config = config;
|
|
18380
|
-
}
|
|
18381
|
-
get baseUrl() {
|
|
18382
|
-
const host = this.config.apiUrl.endsWith("/") ? this.config.apiUrl.slice(0, -1) : this.config.apiUrl;
|
|
18383
|
-
return host;
|
|
18384
|
-
}
|
|
18385
|
-
isAuthFailure(status) {
|
|
18386
|
-
return status === 401 || status === 403;
|
|
18387
|
-
}
|
|
18388
|
-
async resolveApiKey(forceRefresh = false) {
|
|
18389
|
-
if (forceRefresh && this.config.refreshApiKey) {
|
|
18390
|
-
return this.config.refreshApiKey();
|
|
18391
|
-
}
|
|
18392
|
-
return this.config.getApiKey();
|
|
18393
|
-
}
|
|
18394
|
-
async buildHeaders(options, forceRefresh = false) {
|
|
18395
|
-
const headers = new Headers(options.headers);
|
|
18396
|
-
headers.set(
|
|
18397
|
-
"Authorization",
|
|
18398
|
-
`Bearer ${await this.resolveApiKey(forceRefresh)}`
|
|
18399
|
-
);
|
|
18400
|
-
headers.set("Content-Type", "application/json");
|
|
18401
|
-
headers.set("User-Agent", this.config.userAgent ?? DEFAULT_USER_AGENT);
|
|
18402
|
-
return headers;
|
|
18403
|
-
}
|
|
18404
|
-
async performRequest(endpoint, options, forceRefresh = false) {
|
|
18405
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
18406
|
-
return fetch(url, {
|
|
18407
|
-
...options,
|
|
18408
|
-
headers: await this.buildHeaders(options, forceRefresh)
|
|
18409
|
-
});
|
|
18410
|
-
}
|
|
18411
|
-
async performRequestWithRetry(endpoint, options = {}) {
|
|
18412
|
-
let response = await this.performRequest(endpoint, options);
|
|
18413
|
-
if (!response.ok && this.isAuthFailure(response.status)) {
|
|
18414
|
-
response = await this.performRequest(endpoint, options, true);
|
|
18415
|
-
}
|
|
18416
|
-
return response;
|
|
18417
|
-
}
|
|
18418
|
-
async apiRequest(endpoint, options = {}) {
|
|
18419
|
-
const response = await this.performRequestWithRetry(endpoint, options);
|
|
18420
|
-
if (!response.ok) {
|
|
18421
|
-
let errorMessage;
|
|
18422
|
-
try {
|
|
18423
|
-
const errorResponse = await response.json();
|
|
18424
|
-
errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;
|
|
18425
|
-
} catch {
|
|
18426
|
-
errorMessage = `Failed request: [${response.status}] ${response.statusText}`;
|
|
18427
|
-
}
|
|
18428
|
-
throw new Error(errorMessage);
|
|
18429
|
-
}
|
|
18430
|
-
return response.json();
|
|
18431
|
-
}
|
|
18432
|
-
getTeamId() {
|
|
18433
|
-
return this.config.projectId;
|
|
18434
|
-
}
|
|
18435
|
-
async getApiKey(forceRefresh = false) {
|
|
18436
|
-
return this.resolveApiKey(forceRefresh);
|
|
18437
|
-
}
|
|
18438
|
-
getLlmGatewayUrl() {
|
|
18439
|
-
return getLlmGatewayUrl(this.baseUrl);
|
|
18440
|
-
}
|
|
18441
|
-
async getTask(taskId) {
|
|
18442
|
-
const teamId = this.getTeamId();
|
|
18443
|
-
return this.apiRequest(`/api/projects/${teamId}/tasks/${taskId}/`);
|
|
18444
|
-
}
|
|
18445
|
-
async getTaskRun(taskId, runId) {
|
|
18446
|
-
const teamId = this.getTeamId();
|
|
18447
|
-
return this.apiRequest(
|
|
18448
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`
|
|
18449
|
-
);
|
|
18450
|
-
}
|
|
18451
|
-
async updateTaskRun(taskId, runId, payload) {
|
|
18452
|
-
const teamId = this.getTeamId();
|
|
18453
|
-
return this.apiRequest(
|
|
18454
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,
|
|
18455
|
-
{
|
|
18456
|
-
method: "PATCH",
|
|
18457
|
-
body: JSON.stringify(payload)
|
|
18458
|
-
}
|
|
18459
|
-
);
|
|
18460
|
-
}
|
|
18461
|
-
async setTaskRunOutput(taskId, runId, output) {
|
|
18462
|
-
return this.apiRequest(
|
|
18463
|
-
`/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,
|
|
18464
|
-
{
|
|
18465
|
-
method: "PATCH",
|
|
18466
|
-
body: JSON.stringify(output)
|
|
18467
|
-
}
|
|
18468
|
-
);
|
|
18469
|
-
}
|
|
18470
|
-
async appendTaskRunLog(taskId, runId, entries) {
|
|
18471
|
-
const teamId = this.getTeamId();
|
|
18472
|
-
return this.apiRequest(
|
|
18473
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,
|
|
18474
|
-
{
|
|
18475
|
-
method: "POST",
|
|
18476
|
-
body: JSON.stringify({ entries })
|
|
18477
|
-
}
|
|
18478
|
-
);
|
|
18479
|
-
}
|
|
18480
|
-
async relayMessage(taskId, runId, text2) {
|
|
18481
|
-
const teamId = this.getTeamId();
|
|
18482
|
-
await this.apiRequest(
|
|
18483
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,
|
|
18484
|
-
{
|
|
18485
|
-
method: "POST",
|
|
18486
|
-
body: JSON.stringify({ text: text2 })
|
|
18487
|
-
}
|
|
18488
|
-
);
|
|
18489
|
-
}
|
|
18490
|
-
async uploadTaskArtifacts(taskId, runId, artifacts) {
|
|
18491
|
-
if (!artifacts.length) {
|
|
18492
|
-
return [];
|
|
18493
|
-
}
|
|
18494
|
-
const teamId = this.getTeamId();
|
|
18495
|
-
const response = await this.apiRequest(
|
|
18496
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
18497
|
-
{
|
|
18498
|
-
method: "POST",
|
|
18499
|
-
body: JSON.stringify({ artifacts })
|
|
18500
|
-
}
|
|
18501
|
-
);
|
|
18502
|
-
return response.artifacts ?? [];
|
|
18503
|
-
}
|
|
18504
|
-
/**
|
|
18505
|
-
* Download artifact content by storage path
|
|
18506
|
-
* Streams the file through the PostHog backend so the sandbox does not need
|
|
18507
|
-
* direct access to object storage.
|
|
18508
|
-
*/
|
|
18509
|
-
async downloadArtifact(taskId, runId, storagePath) {
|
|
18510
|
-
const teamId = this.getTeamId();
|
|
18511
|
-
try {
|
|
18512
|
-
const response = await this.performRequestWithRetry(
|
|
18513
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,
|
|
18514
|
-
{
|
|
18515
|
-
method: "POST",
|
|
18516
|
-
body: JSON.stringify({ storage_path: storagePath })
|
|
18517
|
-
}
|
|
18518
|
-
);
|
|
18519
|
-
if (!response.ok) {
|
|
18520
|
-
throw new Error(`Failed to download artifact: ${response.status}`);
|
|
18521
|
-
}
|
|
18522
|
-
return response.arrayBuffer();
|
|
18523
|
-
} catch {
|
|
18524
|
-
return null;
|
|
18525
|
-
}
|
|
18526
|
-
}
|
|
18527
|
-
/**
|
|
18528
|
-
* Fetch logs for a task run via the logs API endpoint
|
|
18529
|
-
* @param taskRun - The task run to fetch logs for
|
|
18530
|
-
* @returns Array of stored entries, or empty array if no logs available
|
|
18531
|
-
*/
|
|
18532
|
-
async fetchTaskRunLogs(taskRun) {
|
|
18533
|
-
const teamId = this.getTeamId();
|
|
18534
|
-
const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;
|
|
18535
|
-
try {
|
|
18536
|
-
const response = await this.performRequestWithRetry(endpoint);
|
|
18537
|
-
if (!response.ok) {
|
|
18538
|
-
if (response.status === 404) {
|
|
18539
|
-
return [];
|
|
18540
|
-
}
|
|
18541
|
-
throw new Error(
|
|
18542
|
-
`Failed to fetch logs: ${response.status} ${response.statusText}`
|
|
18543
|
-
);
|
|
18544
|
-
}
|
|
18545
|
-
const content = await response.text();
|
|
18546
|
-
if (!content.trim()) {
|
|
18547
|
-
return [];
|
|
18548
|
-
}
|
|
18549
|
-
return content.trim().split("\n").map((line) => JSON.parse(line));
|
|
18550
|
-
} catch (error) {
|
|
18551
|
-
throw new Error(
|
|
18552
|
-
`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`
|
|
18553
|
-
);
|
|
18554
|
-
}
|
|
18555
|
-
}
|
|
18556
|
-
};
|
|
18557
18349
|
|
|
18558
18350
|
// ../shared/dist/index.js
|
|
18559
18351
|
var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
|
|
@@ -18697,16 +18489,6 @@ var Saga = class {
|
|
|
18697
18489
|
}
|
|
18698
18490
|
};
|
|
18699
18491
|
|
|
18700
|
-
// src/sagas/apply-snapshot-saga.ts
|
|
18701
|
-
import { mkdir as mkdir4, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
18702
|
-
import { join as join10 } from "path";
|
|
18703
|
-
|
|
18704
|
-
// ../git/dist/sagas/tree.js
|
|
18705
|
-
import { existsSync as existsSync5 } from "fs";
|
|
18706
|
-
import * as fs11 from "fs/promises";
|
|
18707
|
-
import * as path13 from "path";
|
|
18708
|
-
import * as tar from "tar";
|
|
18709
|
-
|
|
18710
18492
|
// ../git/dist/git-saga.js
|
|
18711
18493
|
var GitSaga = class extends Saga {
|
|
18712
18494
|
_git = null;
|
|
@@ -18725,20 +18507,1082 @@ var GitSaga = class extends Saga {
|
|
|
18725
18507
|
}
|
|
18726
18508
|
};
|
|
18727
18509
|
|
|
18728
|
-
// ../git/dist/sagas/
|
|
18729
|
-
var
|
|
18730
|
-
|
|
18731
|
-
|
|
18510
|
+
// ../git/dist/sagas/checkpoint.js
|
|
18511
|
+
var CHECKPOINT_REF_PREFIX = "refs/posthog-code-checkpoint/";
|
|
18512
|
+
var CHECKPOINT_VERSION = "v1";
|
|
18513
|
+
var UNMERGED_INDEX_ERROR = "Cannot capture checkpoint with unresolved merge conflicts in the index";
|
|
18514
|
+
var GIT_BUSY_ERROR = "Cannot capture checkpoint while git operation is in progress";
|
|
18515
|
+
var CHECKPOINT_AUTHOR = {
|
|
18516
|
+
name: "PostHog Code",
|
|
18517
|
+
email: "posthog-code@local"
|
|
18518
|
+
};
|
|
18519
|
+
var CaptureCheckpointSaga = class extends GitSaga {
|
|
18520
|
+
sagaName = "CaptureCheckpointSaga";
|
|
18732
18521
|
async executeGitOperations(input) {
|
|
18733
|
-
const { baseDir
|
|
18734
|
-
const
|
|
18735
|
-
await this.
|
|
18736
|
-
|
|
18737
|
-
|
|
18522
|
+
const { baseDir } = input;
|
|
18523
|
+
const headInfo = await this.readOnlyStep("get_head_info", () => getHeadInfo(this.git));
|
|
18524
|
+
const busyState = await this.readOnlyStep("check_git_busy", () => getGitBusyState(this.git));
|
|
18525
|
+
if (busyState.busy) {
|
|
18526
|
+
throw new Error(`${GIT_BUSY_ERROR}: ${busyState.operation}`);
|
|
18527
|
+
}
|
|
18528
|
+
const hasUnmerged = await this.readOnlyStep("check_unmerged_index", () => hasUnmergedEntries(this.git));
|
|
18529
|
+
if (hasUnmerged) {
|
|
18530
|
+
throw new Error(UNMERGED_INDEX_ERROR);
|
|
18531
|
+
}
|
|
18532
|
+
const indexTree = await this.readOnlyStep("write_index_tree", () => this.git.raw(["write-tree"]));
|
|
18533
|
+
const worktreeTree = await this.readOnlyStep("write_worktree_tree", () => createWorktreeTree(this.git, baseDir, headInfo.head));
|
|
18534
|
+
const metaTree = await this.readOnlyStep("write_meta_tree", () => createMetaTree(this.git, baseDir, indexTree.trim(), worktreeTree.trim()));
|
|
18535
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18536
|
+
const message = formatCheckpointMessage({
|
|
18537
|
+
head: headInfo.head,
|
|
18538
|
+
branch: headInfo.branch,
|
|
18539
|
+
indexTree: indexTree.trim(),
|
|
18540
|
+
worktreeTree: worktreeTree.trim(),
|
|
18541
|
+
timestamp
|
|
18542
|
+
});
|
|
18543
|
+
const commitHash = await this.step({
|
|
18544
|
+
name: "create_checkpoint_commit",
|
|
18545
|
+
execute: async () => {
|
|
18546
|
+
const commitGit = this.git.env({
|
|
18547
|
+
...process.env,
|
|
18548
|
+
GIT_AUTHOR_NAME: CHECKPOINT_AUTHOR.name,
|
|
18549
|
+
GIT_AUTHOR_EMAIL: CHECKPOINT_AUTHOR.email,
|
|
18550
|
+
GIT_COMMITTER_NAME: CHECKPOINT_AUTHOR.name,
|
|
18551
|
+
GIT_COMMITTER_EMAIL: CHECKPOINT_AUTHOR.email
|
|
18552
|
+
});
|
|
18553
|
+
const rawCommit = await commitGit.raw([
|
|
18554
|
+
"commit-tree",
|
|
18555
|
+
metaTree.trim(),
|
|
18556
|
+
...headInfo.head ? ["-p", headInfo.head] : [],
|
|
18557
|
+
"-m",
|
|
18558
|
+
message
|
|
18559
|
+
]);
|
|
18560
|
+
return rawCommit.trim();
|
|
18561
|
+
},
|
|
18562
|
+
rollback: async () => {
|
|
18563
|
+
}
|
|
18564
|
+
});
|
|
18565
|
+
const checkpointId = input.checkpointId ?? randomUUID2();
|
|
18566
|
+
const refName = `${CHECKPOINT_REF_PREFIX}${checkpointId}`;
|
|
18567
|
+
const existingRef = await this.readOnlyStep("check_existing_ref", async () => {
|
|
18568
|
+
try {
|
|
18569
|
+
await this.git.revparse(["--verify", refName]);
|
|
18570
|
+
return true;
|
|
18571
|
+
} catch {
|
|
18572
|
+
return false;
|
|
18573
|
+
}
|
|
18574
|
+
});
|
|
18575
|
+
if (existingRef) {
|
|
18576
|
+
throw new Error(`Checkpoint ref already exists: ${refName}`);
|
|
18577
|
+
}
|
|
18578
|
+
await this.step({
|
|
18579
|
+
name: "update_checkpoint_ref",
|
|
18580
|
+
execute: () => this.git.raw(["update-ref", refName, commitHash]),
|
|
18581
|
+
rollback: async () => {
|
|
18582
|
+
await this.git.raw(["update-ref", "-d", refName]).catch(() => {
|
|
18583
|
+
});
|
|
18584
|
+
}
|
|
18585
|
+
});
|
|
18586
|
+
return {
|
|
18587
|
+
checkpointId,
|
|
18588
|
+
commit: commitHash,
|
|
18589
|
+
head: headInfo.head,
|
|
18590
|
+
branch: headInfo.branch,
|
|
18591
|
+
indexTree: indexTree.trim(),
|
|
18592
|
+
worktreeTree: worktreeTree.trim(),
|
|
18593
|
+
timestamp
|
|
18594
|
+
};
|
|
18595
|
+
}
|
|
18596
|
+
};
|
|
18597
|
+
async function getHeadInfo(git) {
|
|
18598
|
+
let head = null;
|
|
18599
|
+
let branch = null;
|
|
18600
|
+
try {
|
|
18601
|
+
head = (await git.revparse(["HEAD"]))?.trim() || null;
|
|
18602
|
+
} catch {
|
|
18603
|
+
head = null;
|
|
18604
|
+
}
|
|
18605
|
+
try {
|
|
18606
|
+
const rawBranch = await git.raw(["symbolic-ref", "--short", "HEAD"]);
|
|
18607
|
+
branch = rawBranch.trim() || null;
|
|
18608
|
+
} catch {
|
|
18609
|
+
branch = null;
|
|
18610
|
+
}
|
|
18611
|
+
return { head, branch };
|
|
18612
|
+
}
|
|
18613
|
+
async function hasUnmergedEntries(git) {
|
|
18614
|
+
const output = await git.raw(["ls-files", "--unmerged"]);
|
|
18615
|
+
return output.trim().length > 0;
|
|
18616
|
+
}
|
|
18617
|
+
async function getGitBusyState(git) {
|
|
18618
|
+
const toplevel = (await git.raw(["rev-parse", "--show-toplevel"])).trim();
|
|
18619
|
+
const resolveGitPath = async (gitPath) => {
|
|
18620
|
+
const relative = (await git.raw(["rev-parse", "--git-path", gitPath])).trim();
|
|
18621
|
+
return path12.isAbsolute(relative) ? relative : path12.resolve(toplevel, relative);
|
|
18622
|
+
};
|
|
18623
|
+
const pathExists = async (gitPath) => {
|
|
18624
|
+
const resolved = await resolveGitPath(gitPath);
|
|
18625
|
+
try {
|
|
18626
|
+
await fs10.access(resolved);
|
|
18627
|
+
return true;
|
|
18628
|
+
} catch {
|
|
18629
|
+
return false;
|
|
18630
|
+
}
|
|
18631
|
+
};
|
|
18632
|
+
const dirExists = async (gitPath) => {
|
|
18633
|
+
const resolved = await resolveGitPath(gitPath);
|
|
18634
|
+
try {
|
|
18635
|
+
const stat4 = await fs10.stat(resolved);
|
|
18636
|
+
return stat4.isDirectory();
|
|
18637
|
+
} catch {
|
|
18638
|
+
return false;
|
|
18639
|
+
}
|
|
18640
|
+
};
|
|
18641
|
+
if (await dirExists("rebase-merge") || await dirExists("rebase-apply")) {
|
|
18642
|
+
return { busy: true, operation: "rebase" };
|
|
18643
|
+
}
|
|
18644
|
+
if (await pathExists("MERGE_HEAD")) {
|
|
18645
|
+
return { busy: true, operation: "merge" };
|
|
18646
|
+
}
|
|
18647
|
+
if (await pathExists("CHERRY_PICK_HEAD")) {
|
|
18648
|
+
return { busy: true, operation: "cherry-pick" };
|
|
18649
|
+
}
|
|
18650
|
+
if (await pathExists("REVERT_HEAD")) {
|
|
18651
|
+
return { busy: true, operation: "revert" };
|
|
18652
|
+
}
|
|
18653
|
+
return { busy: false };
|
|
18654
|
+
}
|
|
18655
|
+
async function createWorktreeTree(git, baseDir, head) {
|
|
18656
|
+
const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-worktree");
|
|
18657
|
+
try {
|
|
18658
|
+
if (head) {
|
|
18659
|
+
await tempGit.raw(["read-tree", head]);
|
|
18660
|
+
} else {
|
|
18661
|
+
await tempGit.raw(["read-tree", "--empty"]);
|
|
18662
|
+
}
|
|
18663
|
+
await tempGit.raw(["add", "-A", "--", "."]);
|
|
18664
|
+
const treeHash = await tempGit.raw(["write-tree"]);
|
|
18665
|
+
return treeHash.trim();
|
|
18666
|
+
} finally {
|
|
18667
|
+
await fs10.rm(tempIndexPath, { force: true }).catch(() => {
|
|
18668
|
+
});
|
|
18669
|
+
}
|
|
18670
|
+
}
|
|
18671
|
+
async function createMetaTree(git, baseDir, indexTree, worktreeTree) {
|
|
18672
|
+
const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-meta");
|
|
18673
|
+
try {
|
|
18674
|
+
await tempGit.raw(["read-tree", "--empty"]);
|
|
18675
|
+
await tempGit.raw([
|
|
18676
|
+
"update-index",
|
|
18677
|
+
"--add",
|
|
18678
|
+
"--cacheinfo",
|
|
18679
|
+
"040000",
|
|
18680
|
+
indexTree,
|
|
18681
|
+
"index"
|
|
18682
|
+
]);
|
|
18683
|
+
await tempGit.raw([
|
|
18684
|
+
"update-index",
|
|
18685
|
+
"--add",
|
|
18686
|
+
"--cacheinfo",
|
|
18687
|
+
"040000",
|
|
18688
|
+
worktreeTree,
|
|
18689
|
+
"worktree"
|
|
18690
|
+
]);
|
|
18691
|
+
const metaTree = await tempGit.raw(["write-tree"]);
|
|
18692
|
+
return metaTree.trim();
|
|
18693
|
+
} finally {
|
|
18694
|
+
await fs10.rm(tempIndexPath, { force: true }).catch(() => {
|
|
18695
|
+
});
|
|
18696
|
+
}
|
|
18697
|
+
}
|
|
18698
|
+
function formatCheckpointMessage(meta) {
|
|
18699
|
+
return [
|
|
18700
|
+
`POSTHOG-CODE-CHECKPOINT ${CHECKPOINT_VERSION}`,
|
|
18701
|
+
`head=${meta.head ?? "null"}`,
|
|
18702
|
+
`branch=${meta.branch ?? "null"}`,
|
|
18703
|
+
`index=${meta.indexTree}`,
|
|
18704
|
+
`worktree=${meta.worktreeTree}`,
|
|
18705
|
+
`timestamp=${meta.timestamp}`
|
|
18706
|
+
].join("\n");
|
|
18707
|
+
}
|
|
18708
|
+
async function getGitCommonDir(git, baseDir) {
|
|
18709
|
+
const raw = await git.raw(["rev-parse", "--git-common-dir"]);
|
|
18710
|
+
const dir = raw.trim() || ".git";
|
|
18711
|
+
return path12.isAbsolute(dir) ? dir : path12.resolve(baseDir, dir);
|
|
18712
|
+
}
|
|
18713
|
+
async function createTempIndexGit(git, baseDir, label) {
|
|
18714
|
+
const tmpDir = path12.join(await getGitCommonDir(git, baseDir), "posthog-code-tmp");
|
|
18715
|
+
await fs10.mkdir(tmpDir, { recursive: true });
|
|
18716
|
+
const tempIndexPath = path12.join(tmpDir, `${label}-${Date.now()}-${randomUUID2()}`);
|
|
18717
|
+
const tempGit = createGitClient(baseDir).env({
|
|
18718
|
+
...process.env,
|
|
18719
|
+
GIT_INDEX_FILE: tempIndexPath
|
|
18720
|
+
});
|
|
18721
|
+
return { tempGit, tempIndexPath };
|
|
18722
|
+
}
|
|
18723
|
+
async function refExists(git, refName) {
|
|
18724
|
+
try {
|
|
18725
|
+
await git.revparse(["--verify", refName]);
|
|
18726
|
+
return true;
|
|
18727
|
+
} catch {
|
|
18728
|
+
return false;
|
|
18729
|
+
}
|
|
18730
|
+
}
|
|
18731
|
+
async function deleteCheckpoint(git, checkpointId) {
|
|
18732
|
+
const refName = `${CHECKPOINT_REF_PREFIX}${checkpointId}`;
|
|
18733
|
+
const exists2 = await refExists(git, refName);
|
|
18734
|
+
if (!exists2) {
|
|
18735
|
+
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
18736
|
+
}
|
|
18737
|
+
await git.raw(["update-ref", "-d", refName]);
|
|
18738
|
+
}
|
|
18739
|
+
|
|
18740
|
+
// ../git/dist/handoff.js
|
|
18741
|
+
var HANDOFF_HEAD_REF_PREFIX = "refs/posthog-code-handoff/head/";
|
|
18742
|
+
var CHECKPOINT_REF_PREFIX2 = "refs/posthog-code-checkpoint/";
|
|
18743
|
+
var GitHandoffTracker = class {
|
|
18744
|
+
repositoryPath;
|
|
18745
|
+
logger;
|
|
18746
|
+
constructor(config) {
|
|
18747
|
+
this.repositoryPath = config.repositoryPath;
|
|
18748
|
+
this.logger = config.logger;
|
|
18749
|
+
}
|
|
18750
|
+
async captureForHandoff(localGitState) {
|
|
18751
|
+
const captureSaga = new CaptureCheckpointSaga(this.logger);
|
|
18752
|
+
const result = await captureSaga.run({ baseDir: this.repositoryPath });
|
|
18753
|
+
if (!result.success) {
|
|
18754
|
+
throw new Error(`Failed to capture checkpoint at step '${result.failedStep}': ${result.error}`);
|
|
18755
|
+
}
|
|
18756
|
+
const checkpoint = result.data;
|
|
18757
|
+
const git = createGitClient(this.repositoryPath);
|
|
18758
|
+
const tempDir = await this.getTempDir(git);
|
|
18759
|
+
const checkpointRef = `${CHECKPOINT_REF_PREFIX2}${checkpoint.checkpointId}`;
|
|
18760
|
+
const shouldIncludeHead = !!checkpoint.head && checkpoint.head !== localGitState?.head;
|
|
18761
|
+
const headRef = shouldIncludeHead ? `${HANDOFF_HEAD_REF_PREFIX}${checkpoint.checkpointId}` : void 0;
|
|
18762
|
+
const packPrefix = path13.join(tempDir, checkpoint.checkpointId);
|
|
18763
|
+
try {
|
|
18764
|
+
const [headPack, indexFile, tracking] = await Promise.all([
|
|
18765
|
+
shouldIncludeHead && checkpoint.head ? this.captureHeadPack(packPrefix, checkpoint.head) : Promise.resolve(void 0),
|
|
18766
|
+
this.copyIndexFile(git, checkpoint.checkpointId),
|
|
18767
|
+
getTrackingMetadata(git, checkpoint.branch)
|
|
18768
|
+
]);
|
|
18769
|
+
return {
|
|
18770
|
+
checkpoint: {
|
|
18771
|
+
checkpointId: checkpoint.checkpointId,
|
|
18772
|
+
commit: checkpoint.commit,
|
|
18773
|
+
checkpointRef,
|
|
18774
|
+
headRef,
|
|
18775
|
+
head: checkpoint.head,
|
|
18776
|
+
branch: checkpoint.branch,
|
|
18777
|
+
indexTree: checkpoint.indexTree,
|
|
18778
|
+
worktreeTree: checkpoint.worktreeTree,
|
|
18779
|
+
timestamp: checkpoint.timestamp,
|
|
18780
|
+
upstreamRemote: tracking.upstreamRemote,
|
|
18781
|
+
upstreamMergeRef: tracking.upstreamMergeRef,
|
|
18782
|
+
remoteUrl: tracking.remoteUrl
|
|
18783
|
+
},
|
|
18784
|
+
headPack,
|
|
18785
|
+
indexFile,
|
|
18786
|
+
totalBytes: (headPack?.rawBytes ?? 0) + indexFile.rawBytes
|
|
18787
|
+
};
|
|
18788
|
+
} finally {
|
|
18789
|
+
await deleteCheckpoint(git, checkpoint.checkpointId).catch(() => {
|
|
18790
|
+
});
|
|
18791
|
+
}
|
|
18792
|
+
}
|
|
18793
|
+
async applyFromHandoff(input) {
|
|
18794
|
+
const { checkpoint, headPackPath, indexPath, localGitState, onDivergedBranch } = input;
|
|
18795
|
+
const git = createGitClient(this.repositoryPath);
|
|
18796
|
+
if (headPackPath) {
|
|
18797
|
+
await this.unpackPackFile(headPackPath);
|
|
18798
|
+
}
|
|
18799
|
+
if (checkpoint.branch && checkpoint.head) {
|
|
18800
|
+
const branchStatus2 = await this.resolveBranchRestoreStatus(git, checkpoint.branch, checkpoint.head, localGitState);
|
|
18801
|
+
const tracking = this.getPreferredTracking(localGitState, checkpoint);
|
|
18802
|
+
if (branchStatus2.kind === "diverged" && !await onDivergedBranch?.(branchStatus2.divergence)) {
|
|
18803
|
+
throw new Error(`Handoff aborted: local branch '${checkpoint.branch}' has diverged`);
|
|
18804
|
+
}
|
|
18805
|
+
await this.checkoutBranchAtHead(git, checkpoint.branch, checkpoint.head);
|
|
18806
|
+
if (this.shouldRestoreTracking(branchStatus2, localGitState, tracking)) {
|
|
18807
|
+
await this.ensureRemoteForTracking(git, tracking);
|
|
18808
|
+
await this.configureUpstream(git, checkpoint.branch, tracking);
|
|
18809
|
+
}
|
|
18810
|
+
} else if (checkpoint.head) {
|
|
18811
|
+
await git.checkout(checkpoint.head);
|
|
18812
|
+
}
|
|
18813
|
+
if (indexPath) {
|
|
18814
|
+
await this.restoreIndexFile(git, indexPath);
|
|
18815
|
+
}
|
|
18816
|
+
const packBytes = headPackPath ? await this.getFileSize(headPackPath) : 0;
|
|
18817
|
+
const indexBytes = indexPath ? await this.getFileSize(indexPath) : 0;
|
|
18818
|
+
return {
|
|
18819
|
+
packBytes,
|
|
18820
|
+
indexBytes,
|
|
18821
|
+
totalBytes: packBytes + indexBytes
|
|
18822
|
+
};
|
|
18823
|
+
}
|
|
18824
|
+
async captureHeadPack(packPrefix, headCommit) {
|
|
18825
|
+
const hash = await this.runGitWithInput(["pack-objects", packPrefix, "--revs"], `${headCommit}
|
|
18826
|
+
`);
|
|
18827
|
+
const packPath = `${packPrefix}-${hash.trim()}.pack`;
|
|
18828
|
+
const rawBytes = await this.getFileSize(packPath);
|
|
18829
|
+
await rm3(`${packPath}.idx`, { force: true }).catch(() => {
|
|
18830
|
+
});
|
|
18831
|
+
return { path: packPath, rawBytes };
|
|
18832
|
+
}
|
|
18833
|
+
async copyIndexFile(git, checkpointId) {
|
|
18834
|
+
const indexPath = await this.getGitPath(git, "index");
|
|
18835
|
+
const tempDir = await this.getTempDir(git);
|
|
18836
|
+
const copiedIndexPath = path13.join(tempDir, `${checkpointId}.index`);
|
|
18837
|
+
await copyFile(indexPath, copiedIndexPath);
|
|
18838
|
+
return {
|
|
18839
|
+
path: copiedIndexPath,
|
|
18840
|
+
rawBytes: await this.getFileSize(copiedIndexPath)
|
|
18841
|
+
};
|
|
18842
|
+
}
|
|
18843
|
+
async restoreIndexFile(git, indexPath) {
|
|
18844
|
+
const gitIndexPath = await this.getGitPath(git, "index");
|
|
18845
|
+
await copyFile(indexPath, gitIndexPath);
|
|
18846
|
+
}
|
|
18847
|
+
async unpackPackFile(packPath) {
|
|
18848
|
+
const content = await readFile3(packPath);
|
|
18849
|
+
await this.runGitWithBuffer(["unpack-objects", "-r"], content);
|
|
18850
|
+
}
|
|
18851
|
+
getPreferredTracking(localGitState, checkpoint) {
|
|
18852
|
+
const state = localGitState;
|
|
18853
|
+
if (state && hasTrackingConfig(state)) {
|
|
18854
|
+
return {
|
|
18855
|
+
upstreamRemote: state.upstreamRemote ?? null,
|
|
18856
|
+
upstreamMergeRef: state.upstreamMergeRef ?? null,
|
|
18857
|
+
remoteUrl: state.upstreamRemote && state.upstreamRemote === checkpoint.upstreamRemote ? checkpoint.remoteUrl : null
|
|
18858
|
+
};
|
|
18859
|
+
}
|
|
18860
|
+
return {
|
|
18861
|
+
upstreamRemote: checkpoint.upstreamRemote,
|
|
18862
|
+
upstreamMergeRef: checkpoint.upstreamMergeRef,
|
|
18863
|
+
remoteUrl: checkpoint.remoteUrl
|
|
18864
|
+
};
|
|
18865
|
+
}
|
|
18866
|
+
shouldRestoreTracking(branchStatus2, localGitState, tracking) {
|
|
18867
|
+
return branchStatus2.kind === "missing" || !hasTrackingConfig(localGitState) && (tracking.upstreamRemote !== null || tracking.upstreamMergeRef !== null);
|
|
18868
|
+
}
|
|
18869
|
+
async ensureRemoteForTracking(git, tracking) {
|
|
18870
|
+
if (!tracking.upstreamRemote || !tracking.remoteUrl)
|
|
18871
|
+
return;
|
|
18872
|
+
const remotes = await git.getRemotes(true);
|
|
18873
|
+
const existing = remotes.find((remote) => remote.name === tracking.upstreamRemote);
|
|
18874
|
+
if (!existing) {
|
|
18875
|
+
await git.addRemote(tracking.upstreamRemote, tracking.remoteUrl);
|
|
18876
|
+
}
|
|
18877
|
+
}
|
|
18878
|
+
async configureUpstream(git, branch, tracking) {
|
|
18879
|
+
if (tracking.upstreamRemote) {
|
|
18880
|
+
await git.raw([
|
|
18881
|
+
"config",
|
|
18882
|
+
`branch.${branch}.remote`,
|
|
18883
|
+
tracking.upstreamRemote
|
|
18884
|
+
]);
|
|
18885
|
+
}
|
|
18886
|
+
if (tracking.upstreamMergeRef) {
|
|
18887
|
+
await git.raw([
|
|
18888
|
+
"config",
|
|
18889
|
+
`branch.${branch}.merge`,
|
|
18890
|
+
tracking.upstreamMergeRef
|
|
18891
|
+
]);
|
|
18892
|
+
}
|
|
18893
|
+
}
|
|
18894
|
+
async resolveBranchRestoreStatus(git, branch, cloudHead, localGitState) {
|
|
18895
|
+
const branchRef = `refs/heads/${branch}`;
|
|
18896
|
+
const branchExists = await this.refExists(git, branchRef);
|
|
18897
|
+
if (!branchExists) {
|
|
18898
|
+
return { kind: "missing" };
|
|
18899
|
+
}
|
|
18900
|
+
const currentBranchHead = (await git.revparse([branchRef])).trim();
|
|
18901
|
+
const candidateHeads = [
|
|
18902
|
+
currentBranchHead,
|
|
18903
|
+
...localGitState?.branch === branch && localGitState.head ? [localGitState.head] : []
|
|
18904
|
+
].filter((value, index, array) => array.indexOf(value) === index);
|
|
18905
|
+
if (candidateHeads.every((head) => head === cloudHead)) {
|
|
18906
|
+
return { kind: "match" };
|
|
18907
|
+
}
|
|
18908
|
+
const nonAncestorHead = await this.findNonAncestorHead(git, candidateHeads, cloudHead);
|
|
18909
|
+
if (!nonAncestorHead) {
|
|
18910
|
+
return { kind: "fast_forward" };
|
|
18911
|
+
}
|
|
18912
|
+
return {
|
|
18913
|
+
kind: "diverged",
|
|
18914
|
+
divergence: {
|
|
18915
|
+
branch,
|
|
18916
|
+
localHead: nonAncestorHead,
|
|
18917
|
+
cloudHead
|
|
18918
|
+
}
|
|
18919
|
+
};
|
|
18920
|
+
}
|
|
18921
|
+
async findNonAncestorHead(_git, heads, cloudHead) {
|
|
18922
|
+
for (const head of heads) {
|
|
18923
|
+
if (head === cloudHead) {
|
|
18924
|
+
continue;
|
|
18925
|
+
}
|
|
18926
|
+
if (!await this.isAncestor(head, cloudHead)) {
|
|
18927
|
+
return head;
|
|
18928
|
+
}
|
|
18929
|
+
}
|
|
18930
|
+
return null;
|
|
18931
|
+
}
|
|
18932
|
+
async checkoutBranchAtHead(git, branch, head) {
|
|
18933
|
+
const currentBranch = await getCurrentBranchName(git);
|
|
18934
|
+
if (currentBranch === branch) {
|
|
18935
|
+
await git.reset(["--hard", head]);
|
|
18936
|
+
return;
|
|
18937
|
+
}
|
|
18938
|
+
const branchRef = `refs/heads/${branch}`;
|
|
18939
|
+
if (await this.refExists(git, branchRef)) {
|
|
18940
|
+
await git.branch(["-f", branch, head]);
|
|
18941
|
+
await git.checkout(branch);
|
|
18942
|
+
return;
|
|
18943
|
+
}
|
|
18944
|
+
await git.checkout(["-b", branch, head]);
|
|
18945
|
+
}
|
|
18946
|
+
async refExists(git, ref) {
|
|
18947
|
+
try {
|
|
18948
|
+
await git.revparse(["--verify", ref]);
|
|
18949
|
+
return true;
|
|
18950
|
+
} catch {
|
|
18951
|
+
return false;
|
|
18952
|
+
}
|
|
18953
|
+
}
|
|
18954
|
+
async isAncestor(ancestor, descendant) {
|
|
18955
|
+
const exitCode = await this.runGitProcessAllowingFailure([
|
|
18956
|
+
"merge-base",
|
|
18957
|
+
"--is-ancestor",
|
|
18958
|
+
ancestor,
|
|
18959
|
+
descendant
|
|
18960
|
+
]);
|
|
18961
|
+
return exitCode === 0;
|
|
18962
|
+
}
|
|
18963
|
+
async getTempDir(git) {
|
|
18964
|
+
const raw = await git.raw(["rev-parse", "--git-common-dir"]);
|
|
18965
|
+
const commonDir = raw.trim() || ".git";
|
|
18966
|
+
const resolved = path13.isAbsolute(commonDir) ? commonDir : path13.resolve(this.repositoryPath, commonDir);
|
|
18967
|
+
const tempDir = path13.join(resolved, "posthog-code-tmp");
|
|
18968
|
+
await mkdir3(tempDir, { recursive: true });
|
|
18969
|
+
return tempDir;
|
|
18970
|
+
}
|
|
18971
|
+
async getGitPath(git, gitPath) {
|
|
18972
|
+
const raw = await git.raw(["rev-parse", "--git-path", gitPath]);
|
|
18973
|
+
const resolved = raw.trim();
|
|
18974
|
+
return path13.isAbsolute(resolved) ? resolved : path13.resolve(this.repositoryPath, resolved);
|
|
18975
|
+
}
|
|
18976
|
+
async getFileSize(filePath) {
|
|
18977
|
+
return (await stat3(filePath)).size;
|
|
18978
|
+
}
|
|
18979
|
+
async runGitWithInput(args2, input) {
|
|
18980
|
+
const { stdout } = await this.runGitProcess(args2, input);
|
|
18981
|
+
return stdout;
|
|
18982
|
+
}
|
|
18983
|
+
async runGitWithBuffer(args2, input) {
|
|
18984
|
+
await this.runGitProcess(args2, input);
|
|
18985
|
+
}
|
|
18986
|
+
async runGitProcessAllowingFailure(args2) {
|
|
18987
|
+
return new Promise((resolve7, reject) => {
|
|
18988
|
+
const child = spawn4("git", args2, {
|
|
18989
|
+
cwd: this.repositoryPath,
|
|
18990
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
18991
|
+
});
|
|
18992
|
+
let stderr = "";
|
|
18993
|
+
child.stderr.on("data", (chunk) => {
|
|
18994
|
+
stderr += chunk.toString();
|
|
18995
|
+
});
|
|
18996
|
+
child.on("error", reject);
|
|
18997
|
+
child.on("close", (code) => {
|
|
18998
|
+
if (code === null) {
|
|
18999
|
+
reject(new Error(`git ${args2.join(" ")} exited unexpectedly`));
|
|
19000
|
+
return;
|
|
19001
|
+
}
|
|
19002
|
+
if (code > 1) {
|
|
19003
|
+
reject(new Error(stderr || `git ${args2.join(" ")} failed with code ${code}`));
|
|
19004
|
+
return;
|
|
19005
|
+
}
|
|
19006
|
+
resolve7(code);
|
|
19007
|
+
});
|
|
19008
|
+
});
|
|
19009
|
+
}
|
|
19010
|
+
runGitProcess(args2, input) {
|
|
19011
|
+
return new Promise((resolve7, reject) => {
|
|
19012
|
+
const child = spawn4("git", args2, {
|
|
19013
|
+
cwd: this.repositoryPath,
|
|
19014
|
+
stdio: "pipe"
|
|
19015
|
+
});
|
|
19016
|
+
let stdout = "";
|
|
19017
|
+
let stderr = "";
|
|
19018
|
+
child.stdout.on("data", (chunk) => {
|
|
19019
|
+
stdout += chunk.toString();
|
|
19020
|
+
});
|
|
19021
|
+
child.stderr.on("data", (chunk) => {
|
|
19022
|
+
stderr += chunk.toString();
|
|
19023
|
+
});
|
|
19024
|
+
child.on("error", reject);
|
|
19025
|
+
child.on("close", (code) => {
|
|
19026
|
+
if (code === 0) {
|
|
19027
|
+
resolve7({ stdout, stderr });
|
|
19028
|
+
return;
|
|
19029
|
+
}
|
|
19030
|
+
reject(new Error(stderr || `git ${args2.join(" ")} failed with code ${code}`));
|
|
19031
|
+
});
|
|
19032
|
+
child.stdin.end(input);
|
|
19033
|
+
});
|
|
19034
|
+
}
|
|
19035
|
+
};
|
|
19036
|
+
async function getCurrentBranchName(git) {
|
|
19037
|
+
try {
|
|
19038
|
+
const raw = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
19039
|
+
const branch = raw.trim();
|
|
19040
|
+
return branch === "HEAD" ? null : branch;
|
|
19041
|
+
} catch {
|
|
19042
|
+
return null;
|
|
19043
|
+
}
|
|
19044
|
+
}
|
|
19045
|
+
async function getTrackingMetadata(git, branch) {
|
|
19046
|
+
if (!branch) {
|
|
19047
|
+
return {
|
|
19048
|
+
upstreamRemote: null,
|
|
19049
|
+
upstreamMergeRef: null,
|
|
19050
|
+
remoteUrl: null
|
|
19051
|
+
};
|
|
19052
|
+
}
|
|
19053
|
+
const upstreamRemote = await getGitConfigValue(git, `branch.${branch}.remote`);
|
|
19054
|
+
const upstreamMergeRef = await getGitConfigValue(git, `branch.${branch}.merge`);
|
|
19055
|
+
const remoteUrl = upstreamRemote ? await getRemoteUrl(git, upstreamRemote) : null;
|
|
19056
|
+
return { upstreamRemote, upstreamMergeRef, remoteUrl };
|
|
19057
|
+
}
|
|
19058
|
+
async function getGitConfigValue(git, key) {
|
|
19059
|
+
try {
|
|
19060
|
+
const value = await git.raw(["config", "--get", key]);
|
|
19061
|
+
return value.trim() || null;
|
|
19062
|
+
} catch {
|
|
19063
|
+
return null;
|
|
19064
|
+
}
|
|
19065
|
+
}
|
|
19066
|
+
async function getRemoteUrl(git, remote) {
|
|
19067
|
+
try {
|
|
19068
|
+
const value = await git.remote(["get-url", remote]);
|
|
19069
|
+
return typeof value === "string" ? value.trim() || null : null;
|
|
19070
|
+
} catch {
|
|
19071
|
+
return null;
|
|
19072
|
+
}
|
|
19073
|
+
}
|
|
19074
|
+
function hasTrackingConfig(localGitState) {
|
|
19075
|
+
return !!(localGitState?.upstreamRemote || localGitState?.upstreamMergeRef);
|
|
19076
|
+
}
|
|
19077
|
+
|
|
19078
|
+
// src/handoff-checkpoint.ts
|
|
19079
|
+
var HandoffCheckpointTracker = class {
|
|
19080
|
+
repositoryPath;
|
|
19081
|
+
taskId;
|
|
19082
|
+
runId;
|
|
19083
|
+
apiClient;
|
|
19084
|
+
logger;
|
|
19085
|
+
constructor(config) {
|
|
19086
|
+
this.repositoryPath = config.repositoryPath;
|
|
19087
|
+
this.taskId = config.taskId;
|
|
19088
|
+
this.runId = config.runId;
|
|
19089
|
+
this.apiClient = config.apiClient;
|
|
19090
|
+
this.logger = config.logger || new Logger({ debug: false, prefix: "[HandoffCheckpointTracker]" });
|
|
19091
|
+
}
|
|
19092
|
+
async captureForHandoff(localGitState) {
|
|
19093
|
+
if (!this.apiClient) {
|
|
19094
|
+
throw new Error(
|
|
19095
|
+
"Cannot capture handoff checkpoint: API client not configured"
|
|
19096
|
+
);
|
|
19097
|
+
}
|
|
19098
|
+
const gitTracker = this.createGitTracker();
|
|
19099
|
+
const capture = await gitTracker.captureForHandoff(localGitState);
|
|
19100
|
+
try {
|
|
19101
|
+
const uploads = await this.uploadArtifacts([
|
|
19102
|
+
{
|
|
19103
|
+
key: "pack",
|
|
19104
|
+
filePath: capture.headPack?.path,
|
|
19105
|
+
name: `handoff/${capture.checkpoint.checkpointId}.pack`,
|
|
19106
|
+
contentType: "application/x-git-packed-objects"
|
|
19107
|
+
},
|
|
19108
|
+
{
|
|
19109
|
+
key: "index",
|
|
19110
|
+
filePath: capture.indexFile.path,
|
|
19111
|
+
name: `handoff/${capture.checkpoint.checkpointId}.index`,
|
|
19112
|
+
contentType: "application/octet-stream"
|
|
19113
|
+
}
|
|
19114
|
+
]);
|
|
19115
|
+
this.logCaptureMetrics(capture.checkpoint, uploads);
|
|
19116
|
+
return {
|
|
19117
|
+
...capture.checkpoint,
|
|
19118
|
+
artifactPath: uploads.pack?.storagePath,
|
|
19119
|
+
indexArtifactPath: uploads.index?.storagePath
|
|
19120
|
+
};
|
|
19121
|
+
} finally {
|
|
19122
|
+
await this.removeIfPresent(capture.headPack?.path);
|
|
19123
|
+
await this.removeIfPresent(capture.indexFile.path);
|
|
19124
|
+
}
|
|
19125
|
+
}
|
|
19126
|
+
async applyFromHandoff(checkpoint, options) {
|
|
19127
|
+
if (!this.apiClient) {
|
|
19128
|
+
throw new Error(
|
|
19129
|
+
"Cannot apply handoff checkpoint: API client not configured"
|
|
19130
|
+
);
|
|
19131
|
+
}
|
|
19132
|
+
const gitTracker = this.createGitTracker();
|
|
19133
|
+
const tmpDir = join9(this.repositoryPath, ".posthog", "tmp");
|
|
19134
|
+
await mkdir4(tmpDir, { recursive: true });
|
|
19135
|
+
const packPath = join9(tmpDir, `${checkpoint.checkpointId}.pack`);
|
|
19136
|
+
const indexPath = join9(tmpDir, `${checkpoint.checkpointId}.index`);
|
|
19137
|
+
try {
|
|
19138
|
+
const downloads = await this.downloadArtifacts([
|
|
19139
|
+
{
|
|
19140
|
+
key: "pack",
|
|
19141
|
+
storagePath: checkpoint.artifactPath,
|
|
19142
|
+
filePath: packPath,
|
|
19143
|
+
label: "handoff pack"
|
|
19144
|
+
},
|
|
19145
|
+
{
|
|
19146
|
+
key: "index",
|
|
19147
|
+
storagePath: checkpoint.indexArtifactPath,
|
|
19148
|
+
filePath: indexPath,
|
|
19149
|
+
label: "handoff index"
|
|
19150
|
+
}
|
|
19151
|
+
]);
|
|
19152
|
+
const applyResult = await gitTracker.applyFromHandoff({
|
|
19153
|
+
checkpoint: this.toGitCheckpoint(checkpoint),
|
|
19154
|
+
headPackPath: downloads.pack?.filePath,
|
|
19155
|
+
indexPath: downloads.index?.filePath,
|
|
19156
|
+
localGitState: options?.localGitState,
|
|
19157
|
+
onDivergedBranch: options?.onDivergedBranch
|
|
19158
|
+
});
|
|
19159
|
+
this.logApplyMetrics(checkpoint, downloads, applyResult.totalBytes);
|
|
19160
|
+
} finally {
|
|
19161
|
+
await this.removeIfPresent(packPath);
|
|
19162
|
+
await this.removeIfPresent(indexPath);
|
|
19163
|
+
}
|
|
19164
|
+
}
|
|
19165
|
+
toGitCheckpoint(checkpoint) {
|
|
19166
|
+
return {
|
|
19167
|
+
checkpointId: checkpoint.checkpointId,
|
|
19168
|
+
commit: checkpoint.commit,
|
|
19169
|
+
checkpointRef: checkpoint.checkpointRef,
|
|
19170
|
+
headRef: checkpoint.headRef,
|
|
19171
|
+
head: checkpoint.head,
|
|
19172
|
+
branch: checkpoint.branch,
|
|
19173
|
+
indexTree: checkpoint.indexTree,
|
|
19174
|
+
worktreeTree: checkpoint.worktreeTree,
|
|
19175
|
+
timestamp: checkpoint.timestamp,
|
|
19176
|
+
upstreamRemote: checkpoint.upstreamRemote ?? null,
|
|
19177
|
+
upstreamMergeRef: checkpoint.upstreamMergeRef ?? null,
|
|
19178
|
+
remoteUrl: checkpoint.remoteUrl ?? null
|
|
19179
|
+
};
|
|
19180
|
+
}
|
|
19181
|
+
async uploadArtifactFile(filePath, name2, contentType) {
|
|
19182
|
+
if (!this.apiClient) {
|
|
19183
|
+
return { rawBytes: 0, wireBytes: 0 };
|
|
19184
|
+
}
|
|
19185
|
+
const content = await readFile4(filePath);
|
|
19186
|
+
const base64Content = content.toString("base64");
|
|
19187
|
+
const artifacts = await this.apiClient.uploadTaskArtifacts(
|
|
19188
|
+
this.taskId,
|
|
19189
|
+
this.runId,
|
|
19190
|
+
[
|
|
19191
|
+
{
|
|
19192
|
+
name: name2,
|
|
19193
|
+
type: "artifact",
|
|
19194
|
+
content: base64Content,
|
|
19195
|
+
content_type: contentType
|
|
19196
|
+
}
|
|
19197
|
+
]
|
|
19198
|
+
);
|
|
19199
|
+
return {
|
|
19200
|
+
storagePath: artifacts.at(-1)?.storage_path,
|
|
19201
|
+
rawBytes: content.byteLength,
|
|
19202
|
+
wireBytes: Buffer.byteLength(base64Content, "utf-8")
|
|
19203
|
+
};
|
|
19204
|
+
}
|
|
19205
|
+
async uploadArtifacts(specs) {
|
|
19206
|
+
const uploads = await Promise.all(
|
|
19207
|
+
specs.map(async (spec) => {
|
|
19208
|
+
if (!spec.filePath) {
|
|
19209
|
+
return [spec.key, void 0];
|
|
19210
|
+
}
|
|
19211
|
+
return [
|
|
19212
|
+
spec.key,
|
|
19213
|
+
await this.uploadArtifactFile(
|
|
19214
|
+
spec.filePath,
|
|
19215
|
+
spec.name,
|
|
19216
|
+
spec.contentType
|
|
19217
|
+
)
|
|
19218
|
+
];
|
|
19219
|
+
})
|
|
19220
|
+
);
|
|
19221
|
+
return Object.fromEntries(uploads);
|
|
19222
|
+
}
|
|
19223
|
+
async downloadArtifactToFile(artifactPath, filePath, label) {
|
|
19224
|
+
if (!this.apiClient) {
|
|
19225
|
+
throw new Error(`Cannot download ${label}: API client not configured`);
|
|
19226
|
+
}
|
|
19227
|
+
const arrayBuffer = await this.apiClient.downloadArtifact(
|
|
19228
|
+
this.taskId,
|
|
19229
|
+
this.runId,
|
|
19230
|
+
artifactPath
|
|
19231
|
+
);
|
|
19232
|
+
if (!arrayBuffer) {
|
|
19233
|
+
throw new Error(`Failed to download ${label}`);
|
|
19234
|
+
}
|
|
19235
|
+
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
19236
|
+
const binaryContent = Buffer.from(base64Content, "base64");
|
|
19237
|
+
await writeFile2(filePath, binaryContent);
|
|
19238
|
+
return {
|
|
19239
|
+
filePath,
|
|
19240
|
+
rawBytes: binaryContent.byteLength,
|
|
19241
|
+
wireBytes: arrayBuffer.byteLength
|
|
19242
|
+
};
|
|
19243
|
+
}
|
|
19244
|
+
async downloadArtifacts(specs) {
|
|
19245
|
+
const downloads = await Promise.all(
|
|
19246
|
+
specs.map(async (spec) => {
|
|
19247
|
+
if (!spec.storagePath) {
|
|
19248
|
+
return [spec.key, void 0];
|
|
19249
|
+
}
|
|
19250
|
+
return [
|
|
19251
|
+
spec.key,
|
|
19252
|
+
await this.downloadArtifactToFile(
|
|
19253
|
+
spec.storagePath,
|
|
19254
|
+
spec.filePath,
|
|
19255
|
+
spec.label
|
|
19256
|
+
)
|
|
19257
|
+
];
|
|
19258
|
+
})
|
|
19259
|
+
);
|
|
19260
|
+
return Object.fromEntries(downloads);
|
|
19261
|
+
}
|
|
19262
|
+
createGitTracker() {
|
|
19263
|
+
return new GitHandoffTracker({
|
|
19264
|
+
repositoryPath: this.repositoryPath,
|
|
19265
|
+
logger: this.logger
|
|
19266
|
+
});
|
|
19267
|
+
}
|
|
19268
|
+
logCaptureMetrics(checkpoint, uploads) {
|
|
19269
|
+
this.logger.info("Captured handoff checkpoint", {
|
|
19270
|
+
checkpointId: checkpoint.checkpointId,
|
|
19271
|
+
branch: checkpoint.branch,
|
|
19272
|
+
head: checkpoint.head,
|
|
19273
|
+
artifactPath: uploads.pack?.storagePath,
|
|
19274
|
+
indexArtifactPath: uploads.index?.storagePath,
|
|
19275
|
+
...this.buildMetricPayload(uploads)
|
|
19276
|
+
});
|
|
19277
|
+
}
|
|
19278
|
+
logApplyMetrics(checkpoint, downloads, totalBytes) {
|
|
19279
|
+
this.logger.info("Applied handoff checkpoint", {
|
|
19280
|
+
checkpointId: checkpoint.checkpointId,
|
|
19281
|
+
commit: checkpoint.commit,
|
|
19282
|
+
branch: checkpoint.branch,
|
|
19283
|
+
head: checkpoint.head,
|
|
19284
|
+
packBytes: downloads.pack?.rawBytes ?? 0,
|
|
19285
|
+
packWireBytes: downloads.pack?.wireBytes ?? 0,
|
|
19286
|
+
indexBytes: downloads.index?.rawBytes ?? 0,
|
|
19287
|
+
indexWireBytes: downloads.index?.wireBytes ?? 0,
|
|
19288
|
+
totalBytes,
|
|
19289
|
+
totalWireBytes: this.sumWireBytes(downloads.pack, downloads.index)
|
|
19290
|
+
});
|
|
19291
|
+
}
|
|
19292
|
+
buildMetricPayload(metrics) {
|
|
19293
|
+
return {
|
|
19294
|
+
packBytes: metrics.pack?.rawBytes ?? 0,
|
|
19295
|
+
packWireBytes: metrics.pack?.wireBytes ?? 0,
|
|
19296
|
+
indexBytes: metrics.index?.rawBytes ?? 0,
|
|
19297
|
+
indexWireBytes: metrics.index?.wireBytes ?? 0,
|
|
19298
|
+
totalBytes: this.sumRawBytes(metrics.pack, metrics.index),
|
|
19299
|
+
totalWireBytes: this.sumWireBytes(metrics.pack, metrics.index)
|
|
19300
|
+
};
|
|
19301
|
+
}
|
|
19302
|
+
sumRawBytes(...artifacts) {
|
|
19303
|
+
return artifacts.reduce(
|
|
19304
|
+
(total, artifact) => total + (artifact?.rawBytes ?? 0),
|
|
19305
|
+
0
|
|
19306
|
+
);
|
|
19307
|
+
}
|
|
19308
|
+
sumWireBytes(...artifacts) {
|
|
19309
|
+
return artifacts.reduce(
|
|
19310
|
+
(total, artifact) => total + (artifact?.wireBytes ?? 0),
|
|
19311
|
+
0
|
|
19312
|
+
);
|
|
19313
|
+
}
|
|
19314
|
+
async removeIfPresent(filePath) {
|
|
19315
|
+
if (!filePath) {
|
|
19316
|
+
return;
|
|
19317
|
+
}
|
|
19318
|
+
await rm4(filePath, { force: true }).catch(() => {
|
|
19319
|
+
});
|
|
19320
|
+
}
|
|
19321
|
+
};
|
|
19322
|
+
|
|
19323
|
+
// src/utils/gateway.ts
|
|
19324
|
+
function getGatewayBaseUrl(posthogHost) {
|
|
19325
|
+
const url = new URL(posthogHost);
|
|
19326
|
+
const hostname = url.hostname;
|
|
19327
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
19328
|
+
return `${url.protocol}//localhost:3308`;
|
|
19329
|
+
}
|
|
19330
|
+
if (hostname === "host.docker.internal") {
|
|
19331
|
+
return `${url.protocol}//host.docker.internal:3308`;
|
|
19332
|
+
}
|
|
19333
|
+
const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us";
|
|
19334
|
+
return `https://gateway.${region}.posthog.com`;
|
|
19335
|
+
}
|
|
19336
|
+
function getLlmGatewayUrl(posthogHost, product = "posthog_code") {
|
|
19337
|
+
return `${getGatewayBaseUrl(posthogHost)}/${product}`;
|
|
19338
|
+
}
|
|
19339
|
+
|
|
19340
|
+
// src/posthog-api.ts
|
|
19341
|
+
var DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${package_default.version}`;
|
|
19342
|
+
var PostHogAPIClient = class {
|
|
19343
|
+
config;
|
|
19344
|
+
constructor(config) {
|
|
19345
|
+
this.config = config;
|
|
19346
|
+
}
|
|
19347
|
+
get baseUrl() {
|
|
19348
|
+
const host = this.config.apiUrl.endsWith("/") ? this.config.apiUrl.slice(0, -1) : this.config.apiUrl;
|
|
19349
|
+
return host;
|
|
19350
|
+
}
|
|
19351
|
+
isAuthFailure(status) {
|
|
19352
|
+
return status === 401 || status === 403;
|
|
19353
|
+
}
|
|
19354
|
+
async resolveApiKey(forceRefresh = false) {
|
|
19355
|
+
if (forceRefresh && this.config.refreshApiKey) {
|
|
19356
|
+
return this.config.refreshApiKey();
|
|
19357
|
+
}
|
|
19358
|
+
return this.config.getApiKey();
|
|
19359
|
+
}
|
|
19360
|
+
async buildHeaders(options, forceRefresh = false) {
|
|
19361
|
+
const headers = new Headers(options.headers);
|
|
19362
|
+
headers.set(
|
|
19363
|
+
"Authorization",
|
|
19364
|
+
`Bearer ${await this.resolveApiKey(forceRefresh)}`
|
|
19365
|
+
);
|
|
19366
|
+
headers.set("Content-Type", "application/json");
|
|
19367
|
+
headers.set("User-Agent", this.config.userAgent ?? DEFAULT_USER_AGENT);
|
|
19368
|
+
return headers;
|
|
19369
|
+
}
|
|
19370
|
+
async performRequest(endpoint, options, forceRefresh = false) {
|
|
19371
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
19372
|
+
return fetch(url, {
|
|
19373
|
+
...options,
|
|
19374
|
+
headers: await this.buildHeaders(options, forceRefresh)
|
|
19375
|
+
});
|
|
19376
|
+
}
|
|
19377
|
+
async performRequestWithRetry(endpoint, options = {}) {
|
|
19378
|
+
let response = await this.performRequest(endpoint, options);
|
|
19379
|
+
if (!response.ok && this.isAuthFailure(response.status)) {
|
|
19380
|
+
response = await this.performRequest(endpoint, options, true);
|
|
19381
|
+
}
|
|
19382
|
+
return response;
|
|
19383
|
+
}
|
|
19384
|
+
async apiRequest(endpoint, options = {}) {
|
|
19385
|
+
const response = await this.performRequestWithRetry(endpoint, options);
|
|
19386
|
+
if (!response.ok) {
|
|
19387
|
+
let errorMessage;
|
|
19388
|
+
try {
|
|
19389
|
+
const errorResponse = await response.json();
|
|
19390
|
+
errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;
|
|
19391
|
+
} catch {
|
|
19392
|
+
errorMessage = `Failed request: [${response.status}] ${response.statusText}`;
|
|
19393
|
+
}
|
|
19394
|
+
throw new Error(errorMessage);
|
|
19395
|
+
}
|
|
19396
|
+
return response.json();
|
|
19397
|
+
}
|
|
19398
|
+
getTeamId() {
|
|
19399
|
+
return this.config.projectId;
|
|
19400
|
+
}
|
|
19401
|
+
async getApiKey(forceRefresh = false) {
|
|
19402
|
+
return this.resolveApiKey(forceRefresh);
|
|
19403
|
+
}
|
|
19404
|
+
getLlmGatewayUrl() {
|
|
19405
|
+
return getLlmGatewayUrl(this.baseUrl);
|
|
19406
|
+
}
|
|
19407
|
+
async getTask(taskId) {
|
|
19408
|
+
const teamId = this.getTeamId();
|
|
19409
|
+
return this.apiRequest(`/api/projects/${teamId}/tasks/${taskId}/`);
|
|
19410
|
+
}
|
|
19411
|
+
async getTaskRun(taskId, runId) {
|
|
19412
|
+
const teamId = this.getTeamId();
|
|
19413
|
+
return this.apiRequest(
|
|
19414
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`
|
|
19415
|
+
);
|
|
19416
|
+
}
|
|
19417
|
+
async updateTaskRun(taskId, runId, payload) {
|
|
19418
|
+
const teamId = this.getTeamId();
|
|
19419
|
+
return this.apiRequest(
|
|
19420
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,
|
|
19421
|
+
{
|
|
19422
|
+
method: "PATCH",
|
|
19423
|
+
body: JSON.stringify(payload)
|
|
19424
|
+
}
|
|
19425
|
+
);
|
|
19426
|
+
}
|
|
19427
|
+
async setTaskRunOutput(taskId, runId, output) {
|
|
19428
|
+
return this.apiRequest(
|
|
19429
|
+
`/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,
|
|
19430
|
+
{
|
|
19431
|
+
method: "PATCH",
|
|
19432
|
+
body: JSON.stringify(output)
|
|
19433
|
+
}
|
|
19434
|
+
);
|
|
19435
|
+
}
|
|
19436
|
+
async appendTaskRunLog(taskId, runId, entries) {
|
|
19437
|
+
const teamId = this.getTeamId();
|
|
19438
|
+
return this.apiRequest(
|
|
19439
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,
|
|
19440
|
+
{
|
|
19441
|
+
method: "POST",
|
|
19442
|
+
body: JSON.stringify({ entries })
|
|
19443
|
+
}
|
|
19444
|
+
);
|
|
19445
|
+
}
|
|
19446
|
+
async relayMessage(taskId, runId, text2) {
|
|
19447
|
+
const teamId = this.getTeamId();
|
|
19448
|
+
await this.apiRequest(
|
|
19449
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,
|
|
19450
|
+
{
|
|
19451
|
+
method: "POST",
|
|
19452
|
+
body: JSON.stringify({ text: text2 })
|
|
19453
|
+
}
|
|
19454
|
+
);
|
|
19455
|
+
}
|
|
19456
|
+
async uploadTaskArtifacts(taskId, runId, artifacts) {
|
|
19457
|
+
if (!artifacts.length) {
|
|
19458
|
+
return [];
|
|
19459
|
+
}
|
|
19460
|
+
const teamId = this.getTeamId();
|
|
19461
|
+
const response = await this.apiRequest(
|
|
19462
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
19463
|
+
{
|
|
19464
|
+
method: "POST",
|
|
19465
|
+
body: JSON.stringify({ artifacts })
|
|
19466
|
+
}
|
|
19467
|
+
);
|
|
19468
|
+
const manifest = response.artifacts ?? [];
|
|
19469
|
+
return manifest.slice(-artifacts.length);
|
|
19470
|
+
}
|
|
19471
|
+
/**
|
|
19472
|
+
* Download artifact content by storage path
|
|
19473
|
+
* Streams the file through the PostHog backend so the sandbox does not need
|
|
19474
|
+
* direct access to object storage.
|
|
19475
|
+
*/
|
|
19476
|
+
async downloadArtifact(taskId, runId, storagePath) {
|
|
19477
|
+
const teamId = this.getTeamId();
|
|
19478
|
+
try {
|
|
19479
|
+
const response = await this.performRequestWithRetry(
|
|
19480
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,
|
|
19481
|
+
{
|
|
19482
|
+
method: "POST",
|
|
19483
|
+
body: JSON.stringify({ storage_path: storagePath })
|
|
19484
|
+
}
|
|
19485
|
+
);
|
|
19486
|
+
if (!response.ok) {
|
|
19487
|
+
throw new Error(`Failed to download artifact: ${response.status}`);
|
|
19488
|
+
}
|
|
19489
|
+
return response.arrayBuffer();
|
|
19490
|
+
} catch {
|
|
19491
|
+
return null;
|
|
19492
|
+
}
|
|
19493
|
+
}
|
|
19494
|
+
/**
|
|
19495
|
+
* Fetch logs for a task run via the logs API endpoint
|
|
19496
|
+
* @param taskRun - The task run to fetch logs for
|
|
19497
|
+
* @returns Array of stored entries, or empty array if no logs available
|
|
19498
|
+
*/
|
|
19499
|
+
async fetchTaskRunLogs(taskRun) {
|
|
19500
|
+
const teamId = this.getTeamId();
|
|
19501
|
+
const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;
|
|
19502
|
+
try {
|
|
19503
|
+
const response = await this.performRequestWithRetry(endpoint);
|
|
19504
|
+
if (!response.ok) {
|
|
19505
|
+
if (response.status === 404) {
|
|
19506
|
+
return [];
|
|
19507
|
+
}
|
|
19508
|
+
throw new Error(
|
|
19509
|
+
`Failed to fetch logs: ${response.status} ${response.statusText}`
|
|
19510
|
+
);
|
|
19511
|
+
}
|
|
19512
|
+
const content = await response.text();
|
|
19513
|
+
if (!content.trim()) {
|
|
19514
|
+
return [];
|
|
19515
|
+
}
|
|
19516
|
+
return content.trim().split("\n").map((line) => JSON.parse(line));
|
|
19517
|
+
} catch (error) {
|
|
19518
|
+
throw new Error(
|
|
19519
|
+
`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`
|
|
19520
|
+
);
|
|
19521
|
+
}
|
|
19522
|
+
}
|
|
19523
|
+
};
|
|
19524
|
+
|
|
19525
|
+
// src/adapters/claude/session/jsonl-hydration.ts
|
|
19526
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
19527
|
+
import * as fs11 from "fs/promises";
|
|
19528
|
+
import * as os6 from "os";
|
|
19529
|
+
import * as path14 from "path";
|
|
19530
|
+
var CHARS_PER_TOKEN = 4;
|
|
19531
|
+
var DEFAULT_MAX_TOKENS = 15e4;
|
|
19532
|
+
function estimateTurnTokens(turn) {
|
|
19533
|
+
let chars = 0;
|
|
19534
|
+
for (const block of turn.content) {
|
|
19535
|
+
if ("text" in block && typeof block.text === "string") {
|
|
19536
|
+
chars += block.text.length;
|
|
19537
|
+
}
|
|
19538
|
+
}
|
|
19539
|
+
if (turn.toolCalls) {
|
|
19540
|
+
for (const tc of turn.toolCalls) {
|
|
19541
|
+
chars += JSON.stringify(tc.input ?? "").length;
|
|
19542
|
+
if (tc.result !== void 0) {
|
|
19543
|
+
chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
|
|
19544
|
+
}
|
|
19545
|
+
}
|
|
19546
|
+
}
|
|
19547
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
19548
|
+
}
|
|
19549
|
+
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
19550
|
+
let budget = maxTokens;
|
|
19551
|
+
let startIndex = turns.length;
|
|
19552
|
+
for (let i2 = turns.length - 1; i2 >= 0; i2--) {
|
|
19553
|
+
const cost = estimateTurnTokens(turns[i2]);
|
|
19554
|
+
if (cost > budget) break;
|
|
19555
|
+
budget -= cost;
|
|
19556
|
+
startIndex = i2;
|
|
19557
|
+
}
|
|
19558
|
+
while (startIndex < turns.length && turns[startIndex].role !== "user") {
|
|
19559
|
+
startIndex++;
|
|
19560
|
+
}
|
|
19561
|
+
return turns.slice(startIndex);
|
|
19562
|
+
}
|
|
19563
|
+
|
|
19564
|
+
// src/sagas/apply-snapshot-saga.ts
|
|
19565
|
+
import { mkdir as mkdir7, rm as rm6, writeFile as writeFile5 } from "fs/promises";
|
|
19566
|
+
import { join as join12 } from "path";
|
|
19567
|
+
|
|
19568
|
+
// ../git/dist/sagas/tree.js
|
|
19569
|
+
import { existsSync as existsSync5 } from "fs";
|
|
19570
|
+
import * as fs12 from "fs/promises";
|
|
19571
|
+
import * as path15 from "path";
|
|
19572
|
+
import * as tar from "tar";
|
|
19573
|
+
var CaptureTreeSaga = class extends GitSaga {
|
|
19574
|
+
sagaName = "CaptureTreeSaga";
|
|
19575
|
+
tempIndexPath = null;
|
|
19576
|
+
async executeGitOperations(input) {
|
|
19577
|
+
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
19578
|
+
const tmpDir = path15.join(baseDir, ".git", "posthog-code-tmp");
|
|
19579
|
+
await this.step({
|
|
19580
|
+
name: "create_tmp_dir",
|
|
19581
|
+
execute: () => fs12.mkdir(tmpDir, { recursive: true }),
|
|
18738
19582
|
rollback: async () => {
|
|
18739
19583
|
}
|
|
18740
19584
|
});
|
|
18741
|
-
this.tempIndexPath =
|
|
19585
|
+
this.tempIndexPath = path15.join(tmpDir, `index-${Date.now()}`);
|
|
18742
19586
|
const tempIndexGit = this.git.env({
|
|
18743
19587
|
...process.env,
|
|
18744
19588
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -18748,7 +19592,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18748
19592
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
18749
19593
|
rollback: async () => {
|
|
18750
19594
|
if (this.tempIndexPath) {
|
|
18751
|
-
await
|
|
19595
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18752
19596
|
});
|
|
18753
19597
|
}
|
|
18754
19598
|
}
|
|
@@ -18757,7 +19601,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18757
19601
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
18758
19602
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
18759
19603
|
this.log.debug("No changes since last capture", { treeHash });
|
|
18760
|
-
await
|
|
19604
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18761
19605
|
});
|
|
18762
19606
|
return { snapshot: null, changed: false };
|
|
18763
19607
|
}
|
|
@@ -18769,7 +19613,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18769
19613
|
}
|
|
18770
19614
|
});
|
|
18771
19615
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
18772
|
-
await
|
|
19616
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18773
19617
|
});
|
|
18774
19618
|
const snapshot = {
|
|
18775
19619
|
treeHash,
|
|
@@ -18793,15 +19637,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18793
19637
|
if (filesToArchive.length === 0) {
|
|
18794
19638
|
return void 0;
|
|
18795
19639
|
}
|
|
18796
|
-
const existingFiles = filesToArchive.filter((f) => existsSync5(
|
|
19640
|
+
const existingFiles = filesToArchive.filter((f) => existsSync5(path15.join(baseDir, f)));
|
|
18797
19641
|
if (existingFiles.length === 0) {
|
|
18798
19642
|
return void 0;
|
|
18799
19643
|
}
|
|
18800
19644
|
await this.step({
|
|
18801
19645
|
name: "create_archive",
|
|
18802
19646
|
execute: async () => {
|
|
18803
|
-
const archiveDir =
|
|
18804
|
-
await
|
|
19647
|
+
const archiveDir = path15.dirname(archivePath);
|
|
19648
|
+
await fs12.mkdir(archiveDir, { recursive: true });
|
|
18805
19649
|
await tar.create({
|
|
18806
19650
|
gzip: true,
|
|
18807
19651
|
file: archivePath,
|
|
@@ -18809,7 +19653,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18809
19653
|
}, existingFiles);
|
|
18810
19654
|
},
|
|
18811
19655
|
rollback: async () => {
|
|
18812
|
-
await
|
|
19656
|
+
await fs12.rm(archivePath, { force: true }).catch(() => {
|
|
18813
19657
|
});
|
|
18814
19658
|
}
|
|
18815
19659
|
});
|
|
@@ -18909,9 +19753,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18909
19753
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
18910
19754
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
18911
19755
|
for (const filePath of filesToExtract) {
|
|
18912
|
-
const fullPath =
|
|
19756
|
+
const fullPath = path15.join(baseDir, filePath);
|
|
18913
19757
|
try {
|
|
18914
|
-
const content = await
|
|
19758
|
+
const content = await fs12.readFile(fullPath);
|
|
18915
19759
|
this.fileBackups.set(filePath, content);
|
|
18916
19760
|
} catch {
|
|
18917
19761
|
}
|
|
@@ -18928,16 +19772,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18928
19772
|
},
|
|
18929
19773
|
rollback: async () => {
|
|
18930
19774
|
for (const filePath of this.extractedFiles) {
|
|
18931
|
-
const fullPath =
|
|
19775
|
+
const fullPath = path15.join(baseDir, filePath);
|
|
18932
19776
|
const backup = this.fileBackups.get(filePath);
|
|
18933
19777
|
if (backup) {
|
|
18934
|
-
const dir =
|
|
18935
|
-
await
|
|
19778
|
+
const dir = path15.dirname(fullPath);
|
|
19779
|
+
await fs12.mkdir(dir, { recursive: true }).catch(() => {
|
|
18936
19780
|
});
|
|
18937
|
-
await
|
|
19781
|
+
await fs12.writeFile(fullPath, backup).catch(() => {
|
|
18938
19782
|
});
|
|
18939
19783
|
} else {
|
|
18940
|
-
await
|
|
19784
|
+
await fs12.rm(fullPath, { force: true }).catch(() => {
|
|
18941
19785
|
});
|
|
18942
19786
|
}
|
|
18943
19787
|
}
|
|
@@ -18945,10 +19789,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18945
19789
|
});
|
|
18946
19790
|
}
|
|
18947
19791
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
18948
|
-
const fullPath =
|
|
19792
|
+
const fullPath = path15.join(baseDir, change.path);
|
|
18949
19793
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
18950
19794
|
try {
|
|
18951
|
-
return await
|
|
19795
|
+
return await fs12.readFile(fullPath);
|
|
18952
19796
|
} catch {
|
|
18953
19797
|
return null;
|
|
18954
19798
|
}
|
|
@@ -18956,15 +19800,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18956
19800
|
await this.step({
|
|
18957
19801
|
name: `delete_${change.path}`,
|
|
18958
19802
|
execute: async () => {
|
|
18959
|
-
await
|
|
19803
|
+
await fs12.rm(fullPath, { force: true });
|
|
18960
19804
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
18961
19805
|
},
|
|
18962
19806
|
rollback: async () => {
|
|
18963
19807
|
if (backupContent) {
|
|
18964
|
-
const dir =
|
|
18965
|
-
await
|
|
19808
|
+
const dir = path15.dirname(fullPath);
|
|
19809
|
+
await fs12.mkdir(dir, { recursive: true }).catch(() => {
|
|
18966
19810
|
});
|
|
18967
|
-
await
|
|
19811
|
+
await fs12.writeFile(fullPath, backupContent).catch(() => {
|
|
18968
19812
|
});
|
|
18969
19813
|
}
|
|
18970
19814
|
}
|
|
@@ -18987,18 +19831,18 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
18987
19831
|
archivePath = null;
|
|
18988
19832
|
async execute(input) {
|
|
18989
19833
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
18990
|
-
const tmpDir =
|
|
19834
|
+
const tmpDir = join12(repositoryPath, ".posthog", "tmp");
|
|
18991
19835
|
if (!snapshot.archiveUrl) {
|
|
18992
19836
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
18993
19837
|
}
|
|
18994
19838
|
const archiveUrl = snapshot.archiveUrl;
|
|
18995
19839
|
await this.step({
|
|
18996
19840
|
name: "create_tmp_dir",
|
|
18997
|
-
execute: () =>
|
|
19841
|
+
execute: () => mkdir7(tmpDir, { recursive: true }),
|
|
18998
19842
|
rollback: async () => {
|
|
18999
19843
|
}
|
|
19000
19844
|
});
|
|
19001
|
-
const archivePath =
|
|
19845
|
+
const archivePath = join12(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
19002
19846
|
this.archivePath = archivePath;
|
|
19003
19847
|
await this.step({
|
|
19004
19848
|
name: "download_archive",
|
|
@@ -19013,11 +19857,18 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19013
19857
|
}
|
|
19014
19858
|
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
19015
19859
|
const binaryContent = Buffer.from(base64Content, "base64");
|
|
19016
|
-
await
|
|
19860
|
+
await writeFile5(archivePath, binaryContent);
|
|
19861
|
+
this.log.info("Tree archive downloaded", {
|
|
19862
|
+
treeHash: snapshot.treeHash,
|
|
19863
|
+
snapshotBytes: binaryContent.byteLength,
|
|
19864
|
+
snapshotWireBytes: arrayBuffer.byteLength,
|
|
19865
|
+
totalBytes: binaryContent.byteLength,
|
|
19866
|
+
totalWireBytes: arrayBuffer.byteLength
|
|
19867
|
+
});
|
|
19017
19868
|
},
|
|
19018
19869
|
rollback: async () => {
|
|
19019
19870
|
if (this.archivePath) {
|
|
19020
|
-
await
|
|
19871
|
+
await rm6(this.archivePath, { force: true }).catch(() => {
|
|
19021
19872
|
});
|
|
19022
19873
|
}
|
|
19023
19874
|
}
|
|
@@ -19033,7 +19884,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19033
19884
|
if (!applyResult.success) {
|
|
19034
19885
|
throw new Error(`Failed to apply tree: ${applyResult.error}`);
|
|
19035
19886
|
}
|
|
19036
|
-
await
|
|
19887
|
+
await rm6(this.archivePath, { force: true }).catch(() => {
|
|
19037
19888
|
});
|
|
19038
19889
|
this.log.info("Tree snapshot applied", {
|
|
19039
19890
|
treeHash: snapshot.treeHash,
|
|
@@ -19046,8 +19897,8 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19046
19897
|
|
|
19047
19898
|
// src/sagas/capture-tree-saga.ts
|
|
19048
19899
|
import { existsSync as existsSync6 } from "fs";
|
|
19049
|
-
import { readFile as
|
|
19050
|
-
import { join as
|
|
19900
|
+
import { readFile as readFile6, rm as rm7 } from "fs/promises";
|
|
19901
|
+
import { join as join13 } from "path";
|
|
19051
19902
|
var CaptureTreeSaga2 = class extends Saga {
|
|
19052
19903
|
sagaName = "CaptureTreeSaga";
|
|
19053
19904
|
async execute(input) {
|
|
@@ -19059,14 +19910,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19059
19910
|
taskId,
|
|
19060
19911
|
runId
|
|
19061
19912
|
} = input;
|
|
19062
|
-
const tmpDir =
|
|
19063
|
-
if (existsSync6(
|
|
19913
|
+
const tmpDir = join13(repositoryPath, ".posthog", "tmp");
|
|
19914
|
+
if (existsSync6(join13(repositoryPath, ".gitmodules"))) {
|
|
19064
19915
|
this.log.warn(
|
|
19065
19916
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
19066
19917
|
);
|
|
19067
19918
|
}
|
|
19068
19919
|
const shouldArchive = !!apiClient;
|
|
19069
|
-
const archivePath = shouldArchive ?
|
|
19920
|
+
const archivePath = shouldArchive ? join13(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
19070
19921
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
19071
19922
|
const captureResult = await gitCaptureSaga.run({
|
|
19072
19923
|
baseDir: repositoryPath,
|
|
@@ -19096,7 +19947,7 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19096
19947
|
runId
|
|
19097
19948
|
);
|
|
19098
19949
|
} finally {
|
|
19099
|
-
await
|
|
19950
|
+
await rm7(createdArchivePath, { force: true }).catch(() => {
|
|
19100
19951
|
});
|
|
19101
19952
|
}
|
|
19102
19953
|
}
|
|
@@ -19120,8 +19971,10 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19120
19971
|
const archiveUrl = await this.step({
|
|
19121
19972
|
name: "upload_archive",
|
|
19122
19973
|
execute: async () => {
|
|
19123
|
-
const archiveContent = await
|
|
19974
|
+
const archiveContent = await readFile6(archivePath);
|
|
19124
19975
|
const base64Content = archiveContent.toString("base64");
|
|
19976
|
+
const snapshotBytes = archiveContent.byteLength;
|
|
19977
|
+
const snapshotWireBytes = Buffer.byteLength(base64Content, "utf-8");
|
|
19125
19978
|
const artifacts = await apiClient.uploadTaskArtifacts(taskId, runId, [
|
|
19126
19979
|
{
|
|
19127
19980
|
name: `trees/${treeHash}.tar.gz`,
|
|
@@ -19130,17 +19983,22 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19130
19983
|
content_type: "application/gzip"
|
|
19131
19984
|
}
|
|
19132
19985
|
]);
|
|
19133
|
-
|
|
19986
|
+
const uploadedArtifact = artifacts[0];
|
|
19987
|
+
if (uploadedArtifact?.storage_path) {
|
|
19134
19988
|
this.log.info("Tree archive uploaded", {
|
|
19135
|
-
storagePath:
|
|
19136
|
-
treeHash
|
|
19989
|
+
storagePath: uploadedArtifact.storage_path,
|
|
19990
|
+
treeHash,
|
|
19991
|
+
snapshotBytes,
|
|
19992
|
+
snapshotWireBytes,
|
|
19993
|
+
totalBytes: snapshotBytes,
|
|
19994
|
+
totalWireBytes: snapshotWireBytes
|
|
19137
19995
|
});
|
|
19138
|
-
return
|
|
19996
|
+
return uploadedArtifact.storage_path;
|
|
19139
19997
|
}
|
|
19140
19998
|
return void 0;
|
|
19141
19999
|
},
|
|
19142
20000
|
rollback: async () => {
|
|
19143
|
-
await
|
|
20001
|
+
await rm7(archivePath, { force: true }).catch(() => {
|
|
19144
20002
|
});
|
|
19145
20003
|
}
|
|
19146
20004
|
});
|
|
@@ -19268,6 +20126,10 @@ var ResumeSaga = class extends Saga {
|
|
|
19268
20126
|
"find_snapshot",
|
|
19269
20127
|
() => Promise.resolve(this.findLatestTreeSnapshot(entries))
|
|
19270
20128
|
);
|
|
20129
|
+
const latestGitCheckpoint = await this.readOnlyStep(
|
|
20130
|
+
"find_git_checkpoint",
|
|
20131
|
+
() => Promise.resolve(this.findLatestGitCheckpoint(entries))
|
|
20132
|
+
);
|
|
19271
20133
|
let snapshotApplied = false;
|
|
19272
20134
|
if (latestSnapshot?.archiveUrl && repositoryPath) {
|
|
19273
20135
|
this.log.info("Found tree snapshot", {
|
|
@@ -19340,6 +20202,7 @@ var ResumeSaga = class extends Saga {
|
|
|
19340
20202
|
return {
|
|
19341
20203
|
conversation,
|
|
19342
20204
|
latestSnapshot,
|
|
20205
|
+
latestGitCheckpoint,
|
|
19343
20206
|
snapshotApplied,
|
|
19344
20207
|
interrupted: latestSnapshot?.interrupted ?? false,
|
|
19345
20208
|
lastDevice,
|
|
@@ -19350,6 +20213,7 @@ var ResumeSaga = class extends Saga {
|
|
|
19350
20213
|
return {
|
|
19351
20214
|
conversation: [],
|
|
19352
20215
|
latestSnapshot: null,
|
|
20216
|
+
latestGitCheckpoint: null,
|
|
19353
20217
|
snapshotApplied: false,
|
|
19354
20218
|
interrupted: false,
|
|
19355
20219
|
logEntryCount: 0
|
|
@@ -19370,6 +20234,20 @@ var ResumeSaga = class extends Saga {
|
|
|
19370
20234
|
}
|
|
19371
20235
|
return null;
|
|
19372
20236
|
}
|
|
20237
|
+
findLatestGitCheckpoint(entries) {
|
|
20238
|
+
const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;
|
|
20239
|
+
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
20240
|
+
const entry = entries[i2];
|
|
20241
|
+
const method = entry.notification?.method;
|
|
20242
|
+
if (method === sdkPrefixedMethod || method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT) {
|
|
20243
|
+
const params = entry.notification?.params;
|
|
20244
|
+
if (params?.checkpointId && params?.checkpointRef) {
|
|
20245
|
+
return params;
|
|
20246
|
+
}
|
|
20247
|
+
}
|
|
20248
|
+
}
|
|
20249
|
+
return null;
|
|
20250
|
+
}
|
|
19373
20251
|
findLastDeviceInfo(entries) {
|
|
19374
20252
|
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
19375
20253
|
const entry = entries[i2];
|
|
@@ -19518,17 +20396,49 @@ async function resumeFromLog(config) {
|
|
|
19518
20396
|
return {
|
|
19519
20397
|
conversation: result.data.conversation,
|
|
19520
20398
|
latestSnapshot: result.data.latestSnapshot,
|
|
20399
|
+
latestGitCheckpoint: result.data.latestGitCheckpoint,
|
|
19521
20400
|
snapshotApplied: result.data.snapshotApplied,
|
|
19522
20401
|
interrupted: result.data.interrupted,
|
|
19523
20402
|
lastDevice: result.data.lastDevice,
|
|
19524
20403
|
logEntryCount: result.data.logEntryCount
|
|
19525
20404
|
};
|
|
19526
20405
|
}
|
|
20406
|
+
var RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
20407
|
+
var TOOL_RESULT_MAX_CHARS = 2e3;
|
|
20408
|
+
function formatConversationForResume(conversation) {
|
|
20409
|
+
const selected = selectRecentTurns(conversation, RESUME_HISTORY_TOKEN_BUDGET);
|
|
20410
|
+
const parts2 = [];
|
|
20411
|
+
if (selected.length < conversation.length) {
|
|
20412
|
+
parts2.push(
|
|
20413
|
+
`*(${conversation.length - selected.length} earlier turns omitted)*`
|
|
20414
|
+
);
|
|
20415
|
+
}
|
|
20416
|
+
for (const turn of selected) {
|
|
20417
|
+
const role = turn.role === "user" ? "User" : "Assistant";
|
|
20418
|
+
const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
|
|
20419
|
+
if (textParts.length > 0) {
|
|
20420
|
+
parts2.push(`**${role}**: ${textParts.join("\n")}`);
|
|
20421
|
+
}
|
|
20422
|
+
if (turn.toolCalls?.length) {
|
|
20423
|
+
const toolSummary = turn.toolCalls.map((tc) => {
|
|
20424
|
+
let resultStr = "";
|
|
20425
|
+
if (tc.result !== void 0) {
|
|
20426
|
+
const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
|
|
20427
|
+
resultStr = raw.length > TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
|
|
20428
|
+
}
|
|
20429
|
+
return ` - ${tc.toolName}${resultStr}`;
|
|
20430
|
+
}).join("\n");
|
|
20431
|
+
parts2.push(`**${role} (tools)**:
|
|
20432
|
+
${toolSummary}`);
|
|
20433
|
+
}
|
|
20434
|
+
}
|
|
20435
|
+
return parts2.join("\n\n");
|
|
20436
|
+
}
|
|
19527
20437
|
|
|
19528
20438
|
// src/session-log-writer.ts
|
|
19529
|
-
import
|
|
20439
|
+
import fs13 from "fs";
|
|
19530
20440
|
import fsp from "fs/promises";
|
|
19531
|
-
import
|
|
20441
|
+
import path16 from "path";
|
|
19532
20442
|
var SessionLogWriter = class _SessionLogWriter {
|
|
19533
20443
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
19534
20444
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -19564,13 +20474,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19564
20474
|
this.sessions.set(sessionId, { context, currentTurnMessages: [] });
|
|
19565
20475
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
19566
20476
|
if (this.localCachePath) {
|
|
19567
|
-
const sessionDir =
|
|
20477
|
+
const sessionDir = path16.join(
|
|
19568
20478
|
this.localCachePath,
|
|
19569
20479
|
"sessions",
|
|
19570
20480
|
context.runId
|
|
19571
20481
|
);
|
|
19572
20482
|
try {
|
|
19573
|
-
|
|
20483
|
+
fs13.mkdirSync(sessionDir, { recursive: true });
|
|
19574
20484
|
} catch (error) {
|
|
19575
20485
|
this.logger.warn("Failed to create local cache directory", {
|
|
19576
20486
|
sessionDir,
|
|
@@ -19822,14 +20732,14 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19822
20732
|
if (!this.localCachePath) return;
|
|
19823
20733
|
const session = this.sessions.get(sessionId);
|
|
19824
20734
|
if (!session) return;
|
|
19825
|
-
const logPath =
|
|
20735
|
+
const logPath = path16.join(
|
|
19826
20736
|
this.localCachePath,
|
|
19827
20737
|
"sessions",
|
|
19828
20738
|
session.context.runId,
|
|
19829
20739
|
"logs.ndjson"
|
|
19830
20740
|
);
|
|
19831
20741
|
try {
|
|
19832
|
-
|
|
20742
|
+
fs13.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
19833
20743
|
`);
|
|
19834
20744
|
} catch (error) {
|
|
19835
20745
|
this.logger.warn("Failed to write to local cache", {
|
|
@@ -19841,13 +20751,13 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19841
20751
|
}
|
|
19842
20752
|
}
|
|
19843
20753
|
static async cleanupOldSessions(localCachePath) {
|
|
19844
|
-
const sessionsDir =
|
|
20754
|
+
const sessionsDir = path16.join(localCachePath, "sessions");
|
|
19845
20755
|
let deleted = 0;
|
|
19846
20756
|
try {
|
|
19847
20757
|
const entries = await fsp.readdir(sessionsDir);
|
|
19848
20758
|
const now = Date.now();
|
|
19849
20759
|
for (const entry of entries) {
|
|
19850
|
-
const entryPath =
|
|
20760
|
+
const entryPath = path16.join(sessionsDir, entry);
|
|
19851
20761
|
try {
|
|
19852
20762
|
const stats = await fsp.stat(entryPath);
|
|
19853
20763
|
if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {
|
|
@@ -19929,6 +20839,14 @@ var httpHeaderSchema = z3.object({
|
|
|
19929
20839
|
name: z3.string(),
|
|
19930
20840
|
value: z3.string()
|
|
19931
20841
|
});
|
|
20842
|
+
var nullishString = z3.string().nullish().transform((value) => value ?? null);
|
|
20843
|
+
var handoffLocalGitStateSchema = z3.object({
|
|
20844
|
+
head: nullishString,
|
|
20845
|
+
branch: nullishString,
|
|
20846
|
+
upstreamHead: nullishString,
|
|
20847
|
+
upstreamRemote: nullishString,
|
|
20848
|
+
upstreamMergeRef: nullishString
|
|
20849
|
+
});
|
|
19932
20850
|
var remoteMcpServerSchema = z3.object({
|
|
19933
20851
|
type: z3.enum(["http", "sse"]),
|
|
19934
20852
|
name: z3.string().min(1, "MCP server name is required"),
|
|
@@ -19980,13 +20898,16 @@ var setConfigOptionParamsSchema = z3.object({
|
|
|
19980
20898
|
var refreshSessionParamsSchema = z3.object({
|
|
19981
20899
|
mcpServers: mcpServersSchema
|
|
19982
20900
|
});
|
|
20901
|
+
var closeParamsSchema = z3.object({
|
|
20902
|
+
localGitState: handoffLocalGitStateSchema.optional()
|
|
20903
|
+
}).optional();
|
|
19983
20904
|
var commandParamsSchemas = {
|
|
19984
20905
|
user_message: userMessageParamsSchema,
|
|
19985
20906
|
"posthog/user_message": userMessageParamsSchema,
|
|
19986
20907
|
cancel: z3.object({}).optional(),
|
|
19987
20908
|
"posthog/cancel": z3.object({}).optional(),
|
|
19988
|
-
close:
|
|
19989
|
-
"posthog/close":
|
|
20909
|
+
close: closeParamsSchema,
|
|
20910
|
+
"posthog/close": closeParamsSchema,
|
|
19990
20911
|
permission_response: permissionResponseParamsSchema,
|
|
19991
20912
|
"posthog/permission_response": permissionResponseParamsSchema,
|
|
19992
20913
|
set_config_option: setConfigOptionParamsSchema,
|
|
@@ -20109,7 +21030,7 @@ function getTaskRunStateString(taskRun, key) {
|
|
|
20109
21030
|
const value = state[key];
|
|
20110
21031
|
return typeof value === "string" ? value : null;
|
|
20111
21032
|
}
|
|
20112
|
-
var AgentServer = class
|
|
21033
|
+
var AgentServer = class {
|
|
20113
21034
|
config;
|
|
20114
21035
|
logger;
|
|
20115
21036
|
server = null;
|
|
@@ -20308,7 +21229,7 @@ var AgentServer = class _AgentServer {
|
|
|
20308
21229
|
return app;
|
|
20309
21230
|
}
|
|
20310
21231
|
async start() {
|
|
20311
|
-
await new Promise((
|
|
21232
|
+
await new Promise((resolve7) => {
|
|
20312
21233
|
this.server = serve(
|
|
20313
21234
|
{
|
|
20314
21235
|
fetch: this.app.fetch,
|
|
@@ -20318,7 +21239,7 @@ var AgentServer = class _AgentServer {
|
|
|
20318
21239
|
this.logger.debug(
|
|
20319
21240
|
`HTTP server listening on port ${this.config.port}`
|
|
20320
21241
|
);
|
|
20321
|
-
|
|
21242
|
+
resolve7();
|
|
20322
21243
|
}
|
|
20323
21244
|
);
|
|
20324
21245
|
});
|
|
@@ -20472,6 +21393,10 @@ var AgentServer = class _AgentServer {
|
|
|
20472
21393
|
case POSTHOG_NOTIFICATIONS.CLOSE:
|
|
20473
21394
|
case "close": {
|
|
20474
21395
|
this.logger.debug("Close requested");
|
|
21396
|
+
const localGitState = this.extractHandoffLocalGitState(params);
|
|
21397
|
+
if (localGitState && this.session) {
|
|
21398
|
+
this.session.pendingHandoffGitState = localGitState;
|
|
21399
|
+
}
|
|
20475
21400
|
await this.cleanupSession();
|
|
20476
21401
|
return { closed: true };
|
|
20477
21402
|
}
|
|
@@ -20698,7 +21623,8 @@ var AgentServer = class _AgentServer {
|
|
|
20698
21623
|
deviceInfo,
|
|
20699
21624
|
logWriter,
|
|
20700
21625
|
permissionMode: initialPermissionMode,
|
|
20701
|
-
hasDesktopConnected: sseController !== null
|
|
21626
|
+
hasDesktopConnected: sseController !== null,
|
|
21627
|
+
pendingHandoffGitState: void 0
|
|
20702
21628
|
};
|
|
20703
21629
|
this.logger = new Logger({
|
|
20704
21630
|
debug: true,
|
|
@@ -20834,7 +21760,7 @@ var AgentServer = class _AgentServer {
|
|
|
20834
21760
|
async sendResumeMessage(payload, taskRun) {
|
|
20835
21761
|
if (!this.session || !this.resumeState) return;
|
|
20836
21762
|
try {
|
|
20837
|
-
const conversationSummary =
|
|
21763
|
+
const conversationSummary = formatConversationForResume(
|
|
20838
21764
|
this.resumeState.conversation
|
|
20839
21765
|
);
|
|
20840
21766
|
const pendingUserPrompt = await this.getPendingUserPrompt(taskRun);
|
|
@@ -20905,40 +21831,6 @@ Continue from where you left off. The user is waiting for your response.`
|
|
|
20905
21831
|
await this.classifyAndSignalFailure(payload, "resume", error);
|
|
20906
21832
|
}
|
|
20907
21833
|
}
|
|
20908
|
-
static RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
20909
|
-
static TOOL_RESULT_MAX_CHARS = 2e3;
|
|
20910
|
-
formatConversationForResume(conversation) {
|
|
20911
|
-
const selected = selectRecentTurns(
|
|
20912
|
-
conversation,
|
|
20913
|
-
_AgentServer.RESUME_HISTORY_TOKEN_BUDGET
|
|
20914
|
-
);
|
|
20915
|
-
const parts2 = [];
|
|
20916
|
-
if (selected.length < conversation.length) {
|
|
20917
|
-
parts2.push(
|
|
20918
|
-
`*(${conversation.length - selected.length} earlier turns omitted)*`
|
|
20919
|
-
);
|
|
20920
|
-
}
|
|
20921
|
-
for (const turn of selected) {
|
|
20922
|
-
const role = turn.role === "user" ? "User" : "Assistant";
|
|
20923
|
-
const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
|
|
20924
|
-
if (textParts.length > 0) {
|
|
20925
|
-
parts2.push(`**${role}**: ${textParts.join("\n")}`);
|
|
20926
|
-
}
|
|
20927
|
-
if (turn.toolCalls?.length) {
|
|
20928
|
-
const toolSummary = turn.toolCalls.map((tc) => {
|
|
20929
|
-
let resultStr = "";
|
|
20930
|
-
if (tc.result !== void 0) {
|
|
20931
|
-
const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
|
|
20932
|
-
resultStr = raw.length > _AgentServer.TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, _AgentServer.TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
|
|
20933
|
-
}
|
|
20934
|
-
return ` - ${tc.toolName}${resultStr}`;
|
|
20935
|
-
}).join("\n");
|
|
20936
|
-
parts2.push(`**${role} (tools)**:
|
|
20937
|
-
${toolSummary}`);
|
|
20938
|
-
}
|
|
20939
|
-
}
|
|
20940
|
-
return parts2.join("\n\n");
|
|
20941
|
-
}
|
|
20942
21834
|
getInitialPromptOverride(taskRun) {
|
|
20943
21835
|
const state = taskRun.state;
|
|
20944
21836
|
const override = state?.initial_prompt_override;
|
|
@@ -21057,16 +21949,16 @@ ${toolSummary}`);
|
|
|
21057
21949
|
throw new Error(`Failed to download artifact ${artifact.name}`);
|
|
21058
21950
|
}
|
|
21059
21951
|
const safeName = this.getSafeArtifactName(artifact.name);
|
|
21060
|
-
const artifactDir =
|
|
21952
|
+
const artifactDir = join14(
|
|
21061
21953
|
this.config.repositoryPath ?? "/tmp/workspace",
|
|
21062
21954
|
".posthog",
|
|
21063
21955
|
"attachments",
|
|
21064
21956
|
runId,
|
|
21065
21957
|
artifact.id ?? safeName
|
|
21066
21958
|
);
|
|
21067
|
-
await
|
|
21068
|
-
const artifactPath =
|
|
21069
|
-
await
|
|
21959
|
+
await mkdir8(artifactDir, { recursive: true });
|
|
21960
|
+
const artifactPath = join14(artifactDir, safeName);
|
|
21961
|
+
await writeFile6(artifactPath, Buffer.from(data));
|
|
21070
21962
|
return resourceLink(pathToFileURL(artifactPath).toString(), artifact.name, {
|
|
21071
21963
|
...artifact.content_type ? { mimeType: artifact.content_type } : {},
|
|
21072
21964
|
...typeof artifact.size === "number" ? { size: artifact.size } : {}
|
|
@@ -21565,6 +22457,11 @@ ${attributionInstructions}
|
|
|
21565
22457
|
async cleanupSession() {
|
|
21566
22458
|
if (!this.session) return;
|
|
21567
22459
|
this.logger.debug("Cleaning up session");
|
|
22460
|
+
try {
|
|
22461
|
+
await this.captureHandoffCheckpoint();
|
|
22462
|
+
} catch (error) {
|
|
22463
|
+
this.logger.error("Failed to capture handoff checkpoint", error);
|
|
22464
|
+
}
|
|
21568
22465
|
try {
|
|
21569
22466
|
await this.captureTreeState();
|
|
21570
22467
|
} catch (error) {
|
|
@@ -21624,6 +22521,50 @@ ${attributionInstructions}
|
|
|
21624
22521
|
this.logger.error("Failed to capture tree state", error);
|
|
21625
22522
|
}
|
|
21626
22523
|
}
|
|
22524
|
+
async captureHandoffCheckpoint() {
|
|
22525
|
+
if (!this.session?.treeTracker || !this.session.pendingHandoffGitState) {
|
|
22526
|
+
return;
|
|
22527
|
+
}
|
|
22528
|
+
if (!this.posthogAPI) {
|
|
22529
|
+
this.logger.warn(
|
|
22530
|
+
"Skipping handoff checkpoint capture: PostHog API client is not configured"
|
|
22531
|
+
);
|
|
22532
|
+
return;
|
|
22533
|
+
}
|
|
22534
|
+
const tracker = new HandoffCheckpointTracker({
|
|
22535
|
+
repositoryPath: this.config.repositoryPath ?? "/tmp/workspace",
|
|
22536
|
+
taskId: this.session.payload.task_id,
|
|
22537
|
+
runId: this.session.payload.run_id,
|
|
22538
|
+
apiClient: this.posthogAPI,
|
|
22539
|
+
logger: this.logger.child("HandoffCheckpoint")
|
|
22540
|
+
});
|
|
22541
|
+
const checkpoint = await tracker.captureForHandoff(
|
|
22542
|
+
this.session.pendingHandoffGitState
|
|
22543
|
+
);
|
|
22544
|
+
if (!checkpoint) return;
|
|
22545
|
+
const checkpointWithDevice = {
|
|
22546
|
+
...checkpoint,
|
|
22547
|
+
device: this.session.deviceInfo
|
|
22548
|
+
};
|
|
22549
|
+
const notification = {
|
|
22550
|
+
jsonrpc: "2.0",
|
|
22551
|
+
method: POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT,
|
|
22552
|
+
params: checkpointWithDevice
|
|
22553
|
+
};
|
|
22554
|
+
this.broadcastEvent({
|
|
22555
|
+
type: "notification",
|
|
22556
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22557
|
+
notification
|
|
22558
|
+
});
|
|
22559
|
+
this.session.logWriter.appendRawLine(
|
|
22560
|
+
this.session.payload.run_id,
|
|
22561
|
+
JSON.stringify(notification)
|
|
22562
|
+
);
|
|
22563
|
+
}
|
|
22564
|
+
extractHandoffLocalGitState(params) {
|
|
22565
|
+
const result = handoffLocalGitStateSchema.safeParse(params.localGitState);
|
|
22566
|
+
return result.success ? result.data : null;
|
|
22567
|
+
}
|
|
21627
22568
|
broadcastTurnComplete(stopReason) {
|
|
21628
22569
|
if (!this.session) return;
|
|
21629
22570
|
this.broadcastEvent({
|
|
@@ -21677,8 +22618,8 @@ ${attributionInstructions}
|
|
|
21677
22618
|
options: params.options,
|
|
21678
22619
|
toolCall: params.toolCall
|
|
21679
22620
|
});
|
|
21680
|
-
return new Promise((
|
|
21681
|
-
this.pendingPermissions.set(requestId, { resolve:
|
|
22621
|
+
return new Promise((resolve7) => {
|
|
22622
|
+
this.pendingPermissions.set(requestId, { resolve: resolve7 });
|
|
21682
22623
|
});
|
|
21683
22624
|
}
|
|
21684
22625
|
resolvePermission(requestId, optionId, customInput, answers) {
|