@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
package/dist/server/bin.cjs
CHANGED
|
@@ -805,15 +805,15 @@ var require_src2 = __commonJS({
|
|
|
805
805
|
var fs_1 = require("fs");
|
|
806
806
|
var debug_1 = __importDefault(require_src());
|
|
807
807
|
var log = debug_1.default("@kwsites/file-exists");
|
|
808
|
-
function check(
|
|
809
|
-
log(`checking %s`,
|
|
808
|
+
function check(path17, isFile2, isDirectory) {
|
|
809
|
+
log(`checking %s`, path17);
|
|
810
810
|
try {
|
|
811
|
-
const
|
|
812
|
-
if (
|
|
811
|
+
const stat4 = fs_1.statSync(path17);
|
|
812
|
+
if (stat4.isFile() && isFile2) {
|
|
813
813
|
log(`[OK] path represents a file`);
|
|
814
814
|
return true;
|
|
815
815
|
}
|
|
816
|
-
if (
|
|
816
|
+
if (stat4.isDirectory() && isDirectory) {
|
|
817
817
|
log(`[OK] path represents a directory`);
|
|
818
818
|
return true;
|
|
819
819
|
}
|
|
@@ -828,8 +828,8 @@ var require_src2 = __commonJS({
|
|
|
828
828
|
throw e;
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
|
-
function exists2(
|
|
832
|
-
return check(
|
|
831
|
+
function exists2(path17, type = exports2.READABLE) {
|
|
832
|
+
return check(path17, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
|
|
833
833
|
}
|
|
834
834
|
exports2.exists = exists2;
|
|
835
835
|
exports2.FILE = 1;
|
|
@@ -925,11 +925,11 @@ var require_tree_sitter = __commonJS({
|
|
|
925
925
|
throw toThrow;
|
|
926
926
|
};
|
|
927
927
|
var scriptDirectory = "";
|
|
928
|
-
function locateFile(
|
|
928
|
+
function locateFile(path17) {
|
|
929
929
|
if (Module["locateFile"]) {
|
|
930
|
-
return Module["locateFile"](
|
|
930
|
+
return Module["locateFile"](path17, scriptDirectory);
|
|
931
931
|
}
|
|
932
|
-
return scriptDirectory +
|
|
932
|
+
return scriptDirectory + path17;
|
|
933
933
|
}
|
|
934
934
|
var readAsync, readBinary;
|
|
935
935
|
if (ENVIRONMENT_IS_NODE) {
|
|
@@ -943,10 +943,10 @@ var require_tree_sitter = __commonJS({
|
|
|
943
943
|
};
|
|
944
944
|
readAsync = (filename, binary2 = true) => {
|
|
945
945
|
filename = isFileURI(filename) ? new URL(filename) : nodePath.normalize(filename);
|
|
946
|
-
return new Promise((
|
|
946
|
+
return new Promise((resolve7, reject) => {
|
|
947
947
|
fs.readFile(filename, binary2 ? void 0 : "utf8", (err2, data) => {
|
|
948
948
|
if (err2) reject(err2);
|
|
949
|
-
else
|
|
949
|
+
else resolve7(binary2 ? data.buffer : data);
|
|
950
950
|
});
|
|
951
951
|
});
|
|
952
952
|
};
|
|
@@ -987,13 +987,13 @@ var require_tree_sitter = __commonJS({
|
|
|
987
987
|
}
|
|
988
988
|
readAsync = (url) => {
|
|
989
989
|
if (isFileURI(url)) {
|
|
990
|
-
return new Promise((reject,
|
|
990
|
+
return new Promise((reject, resolve7) => {
|
|
991
991
|
var xhr = new XMLHttpRequest();
|
|
992
992
|
xhr.open("GET", url, true);
|
|
993
993
|
xhr.responseType = "arraybuffer";
|
|
994
994
|
xhr.onload = () => {
|
|
995
995
|
if (xhr.status == 200 || xhr.status == 0 && xhr.response) {
|
|
996
|
-
|
|
996
|
+
resolve7(xhr.response);
|
|
997
997
|
}
|
|
998
998
|
reject(xhr.status);
|
|
999
999
|
};
|
|
@@ -1953,8 +1953,8 @@ var require_tree_sitter = __commonJS({
|
|
|
1953
1953
|
}
|
|
1954
1954
|
var libFile = locateFile(libName2);
|
|
1955
1955
|
if (flags2.loadAsync) {
|
|
1956
|
-
return new Promise(function(
|
|
1957
|
-
asyncLoad(libFile,
|
|
1956
|
+
return new Promise(function(resolve7, reject) {
|
|
1957
|
+
asyncLoad(libFile, resolve7, reject);
|
|
1958
1958
|
});
|
|
1959
1959
|
}
|
|
1960
1960
|
if (!readBinary) {
|
|
@@ -3450,8 +3450,8 @@ var require_tree_sitter = __commonJS({
|
|
|
3450
3450
|
} else {
|
|
3451
3451
|
const url = input;
|
|
3452
3452
|
if (typeof process !== "undefined" && process.versions && process.versions.node) {
|
|
3453
|
-
const
|
|
3454
|
-
bytes = Promise.resolve(
|
|
3453
|
+
const fs14 = require("fs");
|
|
3454
|
+
bytes = Promise.resolve(fs14.readFileSync(url));
|
|
3455
3455
|
} else {
|
|
3456
3456
|
bytes = fetch(url).then((response) => response.arrayBuffer().then((buffer) => {
|
|
3457
3457
|
if (response.ok) {
|
|
@@ -3912,8 +3912,8 @@ function isSupportedReasoningEffort(adapter, modelId, value) {
|
|
|
3912
3912
|
}
|
|
3913
3913
|
|
|
3914
3914
|
// src/server/agent-server.ts
|
|
3915
|
-
var
|
|
3916
|
-
var
|
|
3915
|
+
var import_promises7 = require("fs/promises");
|
|
3916
|
+
var import_node_path10 = require("path");
|
|
3917
3917
|
var import_node_url2 = require("url");
|
|
3918
3918
|
var import_sdk5 = require("@agentclientprotocol/sdk");
|
|
3919
3919
|
var import_node_server = require("@hono/node-server");
|
|
@@ -3959,8 +3959,8 @@ function pathspec(...paths) {
|
|
|
3959
3959
|
cache.set(key, paths);
|
|
3960
3960
|
return key;
|
|
3961
3961
|
}
|
|
3962
|
-
function isPathSpec(
|
|
3963
|
-
return
|
|
3962
|
+
function isPathSpec(path17) {
|
|
3963
|
+
return path17 instanceof String && cache.has(path17);
|
|
3964
3964
|
}
|
|
3965
3965
|
function toPaths(pathSpec) {
|
|
3966
3966
|
return cache.get(pathSpec) || [];
|
|
@@ -4049,8 +4049,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4049
4049
|
function forEachLineWithContent(input, callback) {
|
|
4050
4050
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4051
4051
|
}
|
|
4052
|
-
function folderExists(
|
|
4053
|
-
return (0, import_file_exists.exists)(
|
|
4052
|
+
function folderExists(path17) {
|
|
4053
|
+
return (0, import_file_exists.exists)(path17, import_file_exists.FOLDER);
|
|
4054
4054
|
}
|
|
4055
4055
|
function append(target, item) {
|
|
4056
4056
|
if (Array.isArray(target)) {
|
|
@@ -4454,8 +4454,8 @@ function checkIsRepoRootTask() {
|
|
|
4454
4454
|
commands,
|
|
4455
4455
|
format: "utf-8",
|
|
4456
4456
|
onError,
|
|
4457
|
-
parser(
|
|
4458
|
-
return /^\.(git)?$/.test(
|
|
4457
|
+
parser(path17) {
|
|
4458
|
+
return /^\.(git)?$/.test(path17.trim());
|
|
4459
4459
|
}
|
|
4460
4460
|
};
|
|
4461
4461
|
}
|
|
@@ -4889,11 +4889,11 @@ function parseGrep(grep) {
|
|
|
4889
4889
|
const paths = /* @__PURE__ */ new Set();
|
|
4890
4890
|
const results = {};
|
|
4891
4891
|
forEachLineWithContent(grep, (input) => {
|
|
4892
|
-
const [
|
|
4893
|
-
paths.add(
|
|
4894
|
-
(results[
|
|
4892
|
+
const [path17, line, preview] = input.split(NULL);
|
|
4893
|
+
paths.add(path17);
|
|
4894
|
+
(results[path17] = results[path17] || []).push({
|
|
4895
4895
|
line: asNumber(line),
|
|
4896
|
-
path:
|
|
4896
|
+
path: path17,
|
|
4897
4897
|
preview
|
|
4898
4898
|
});
|
|
4899
4899
|
});
|
|
@@ -5658,14 +5658,14 @@ var init_hash_object = __esm({
|
|
|
5658
5658
|
init_task();
|
|
5659
5659
|
}
|
|
5660
5660
|
});
|
|
5661
|
-
function parseInit(bare,
|
|
5661
|
+
function parseInit(bare, path17, text2) {
|
|
5662
5662
|
const response = String(text2).trim();
|
|
5663
5663
|
let result;
|
|
5664
5664
|
if (result = initResponseRegex.exec(response)) {
|
|
5665
|
-
return new InitSummary(bare,
|
|
5665
|
+
return new InitSummary(bare, path17, false, result[1]);
|
|
5666
5666
|
}
|
|
5667
5667
|
if (result = reInitResponseRegex.exec(response)) {
|
|
5668
|
-
return new InitSummary(bare,
|
|
5668
|
+
return new InitSummary(bare, path17, true, result[1]);
|
|
5669
5669
|
}
|
|
5670
5670
|
let gitDir = "";
|
|
5671
5671
|
const tokens = response.split(" ");
|
|
@@ -5676,7 +5676,7 @@ function parseInit(bare, path15, text2) {
|
|
|
5676
5676
|
break;
|
|
5677
5677
|
}
|
|
5678
5678
|
}
|
|
5679
|
-
return new InitSummary(bare,
|
|
5679
|
+
return new InitSummary(bare, path17, /^re/i.test(response), gitDir);
|
|
5680
5680
|
}
|
|
5681
5681
|
var InitSummary;
|
|
5682
5682
|
var initResponseRegex;
|
|
@@ -5685,9 +5685,9 @@ var init_InitSummary = __esm({
|
|
|
5685
5685
|
"src/lib/responses/InitSummary.ts"() {
|
|
5686
5686
|
"use strict";
|
|
5687
5687
|
InitSummary = class {
|
|
5688
|
-
constructor(bare,
|
|
5688
|
+
constructor(bare, path17, existing, gitDir) {
|
|
5689
5689
|
this.bare = bare;
|
|
5690
|
-
this.path =
|
|
5690
|
+
this.path = path17;
|
|
5691
5691
|
this.existing = existing;
|
|
5692
5692
|
this.gitDir = gitDir;
|
|
5693
5693
|
}
|
|
@@ -5699,7 +5699,7 @@ var init_InitSummary = __esm({
|
|
|
5699
5699
|
function hasBareCommand(command) {
|
|
5700
5700
|
return command.includes(bareCommand);
|
|
5701
5701
|
}
|
|
5702
|
-
function initTask(bare = false,
|
|
5702
|
+
function initTask(bare = false, path17, customArgs) {
|
|
5703
5703
|
const commands = ["init", ...customArgs];
|
|
5704
5704
|
if (bare && !hasBareCommand(commands)) {
|
|
5705
5705
|
commands.splice(1, 0, bareCommand);
|
|
@@ -5708,7 +5708,7 @@ function initTask(bare = false, path15, customArgs) {
|
|
|
5708
5708
|
commands,
|
|
5709
5709
|
format: "utf-8",
|
|
5710
5710
|
parser(text2) {
|
|
5711
|
-
return parseInit(commands.includes("--bare"),
|
|
5711
|
+
return parseInit(commands.includes("--bare"), path17, text2);
|
|
5712
5712
|
}
|
|
5713
5713
|
};
|
|
5714
5714
|
}
|
|
@@ -6524,12 +6524,12 @@ var init_FileStatusSummary = __esm({
|
|
|
6524
6524
|
"use strict";
|
|
6525
6525
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
6526
6526
|
FileStatusSummary = class {
|
|
6527
|
-
constructor(
|
|
6528
|
-
this.path =
|
|
6527
|
+
constructor(path17, index, working_dir) {
|
|
6528
|
+
this.path = path17;
|
|
6529
6529
|
this.index = index;
|
|
6530
6530
|
this.working_dir = working_dir;
|
|
6531
6531
|
if (index === "R" || working_dir === "R") {
|
|
6532
|
-
const detail = fromPathRegex.exec(
|
|
6532
|
+
const detail = fromPathRegex.exec(path17) || [null, path17, path17];
|
|
6533
6533
|
this.from = detail[2] || "";
|
|
6534
6534
|
this.path = detail[1] || "";
|
|
6535
6535
|
}
|
|
@@ -6560,14 +6560,14 @@ function splitLine(result, lineStr) {
|
|
|
6560
6560
|
default:
|
|
6561
6561
|
return;
|
|
6562
6562
|
}
|
|
6563
|
-
function data(index, workingDir,
|
|
6563
|
+
function data(index, workingDir, path17) {
|
|
6564
6564
|
const raw = `${index}${workingDir}`;
|
|
6565
6565
|
const handler = parsers6.get(raw);
|
|
6566
6566
|
if (handler) {
|
|
6567
|
-
handler(result,
|
|
6567
|
+
handler(result, path17);
|
|
6568
6568
|
}
|
|
6569
6569
|
if (raw !== "##" && raw !== "!!") {
|
|
6570
|
-
result.files.push(new FileStatusSummary(
|
|
6570
|
+
result.files.push(new FileStatusSummary(path17, index, workingDir));
|
|
6571
6571
|
}
|
|
6572
6572
|
}
|
|
6573
6573
|
}
|
|
@@ -6880,9 +6880,9 @@ var init_simple_git_api = __esm({
|
|
|
6880
6880
|
next
|
|
6881
6881
|
);
|
|
6882
6882
|
}
|
|
6883
|
-
hashObject(
|
|
6883
|
+
hashObject(path17, write) {
|
|
6884
6884
|
return this._runTask(
|
|
6885
|
-
hashObjectTask(
|
|
6885
|
+
hashObjectTask(path17, write === true),
|
|
6886
6886
|
trailingFunctionArgument(arguments)
|
|
6887
6887
|
);
|
|
6888
6888
|
}
|
|
@@ -7235,8 +7235,8 @@ var init_branch = __esm({
|
|
|
7235
7235
|
}
|
|
7236
7236
|
});
|
|
7237
7237
|
function toPath(input) {
|
|
7238
|
-
const
|
|
7239
|
-
return
|
|
7238
|
+
const path17 = input.trim().replace(/^["']|["']$/g, "");
|
|
7239
|
+
return path17 && (0, import_node_path.normalize)(path17);
|
|
7240
7240
|
}
|
|
7241
7241
|
var parseCheckIgnore;
|
|
7242
7242
|
var init_CheckIgnore = __esm({
|
|
@@ -7550,8 +7550,8 @@ __export(sub_module_exports, {
|
|
|
7550
7550
|
subModuleTask: () => subModuleTask,
|
|
7551
7551
|
updateSubModuleTask: () => updateSubModuleTask
|
|
7552
7552
|
});
|
|
7553
|
-
function addSubModuleTask(repo,
|
|
7554
|
-
return subModuleTask(["add", repo,
|
|
7553
|
+
function addSubModuleTask(repo, path17) {
|
|
7554
|
+
return subModuleTask(["add", repo, path17]);
|
|
7555
7555
|
}
|
|
7556
7556
|
function initSubModuleTask(customArgs) {
|
|
7557
7557
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -7881,8 +7881,8 @@ var require_git = __commonJS2({
|
|
|
7881
7881
|
}
|
|
7882
7882
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
7883
7883
|
};
|
|
7884
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
7885
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
7884
|
+
Git2.prototype.submoduleAdd = function(repo, path17, then) {
|
|
7885
|
+
return this._runTask(addSubModuleTask2(repo, path17), trailingFunctionArgument2(arguments));
|
|
7886
7886
|
};
|
|
7887
7887
|
Git2.prototype.submoduleUpdate = function(args2, then) {
|
|
7888
7888
|
return this._runTask(
|
|
@@ -8498,10 +8498,10 @@ async function getIndexLockPath(repoPath) {
|
|
|
8498
8498
|
async function getLockInfo(repoPath) {
|
|
8499
8499
|
const lockPath = await getIndexLockPath(repoPath);
|
|
8500
8500
|
try {
|
|
8501
|
-
const
|
|
8501
|
+
const stat4 = await import_promises.default.stat(lockPath);
|
|
8502
8502
|
return {
|
|
8503
8503
|
path: lockPath,
|
|
8504
|
-
ageMs: Date.now() -
|
|
8504
|
+
ageMs: Date.now() - stat4.mtimeMs
|
|
8505
8505
|
};
|
|
8506
8506
|
} catch {
|
|
8507
8507
|
return null;
|
|
@@ -8536,10 +8536,10 @@ var AsyncReaderWriterLock = class {
|
|
|
8536
8536
|
this.readers++;
|
|
8537
8537
|
return;
|
|
8538
8538
|
}
|
|
8539
|
-
return new Promise((
|
|
8539
|
+
return new Promise((resolve7) => {
|
|
8540
8540
|
this.readQueue.push(() => {
|
|
8541
8541
|
this.readers++;
|
|
8542
|
-
|
|
8542
|
+
resolve7();
|
|
8543
8543
|
});
|
|
8544
8544
|
});
|
|
8545
8545
|
}
|
|
@@ -8553,11 +8553,11 @@ var AsyncReaderWriterLock = class {
|
|
|
8553
8553
|
return;
|
|
8554
8554
|
}
|
|
8555
8555
|
this.writerWaiting = true;
|
|
8556
|
-
return new Promise((
|
|
8556
|
+
return new Promise((resolve7) => {
|
|
8557
8557
|
this.writeQueue.push(() => {
|
|
8558
8558
|
this.writerWaiting = this.writeQueue.length > 0;
|
|
8559
8559
|
this.writer = true;
|
|
8560
|
-
|
|
8560
|
+
resolve7();
|
|
8561
8561
|
});
|
|
8562
8562
|
});
|
|
8563
8563
|
}
|
|
@@ -8729,7 +8729,7 @@ var import_zod3 = require("zod");
|
|
|
8729
8729
|
// package.json
|
|
8730
8730
|
var package_default = {
|
|
8731
8731
|
name: "@posthog/agent",
|
|
8732
|
-
version: "2.3.
|
|
8732
|
+
version: "2.3.387",
|
|
8733
8733
|
repository: "https://github.com/PostHog/code",
|
|
8734
8734
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8735
8735
|
exports: {
|
|
@@ -8785,9 +8785,25 @@ var package_default = {
|
|
|
8785
8785
|
types: "./dist/execution-mode.d.ts",
|
|
8786
8786
|
import: "./dist/execution-mode.js"
|
|
8787
8787
|
},
|
|
8788
|
+
"./resume": {
|
|
8789
|
+
types: "./dist/resume.d.ts",
|
|
8790
|
+
import: "./dist/resume.js"
|
|
8791
|
+
},
|
|
8792
|
+
"./handoff-checkpoint": {
|
|
8793
|
+
types: "./dist/handoff-checkpoint.d.ts",
|
|
8794
|
+
import: "./dist/handoff-checkpoint.js"
|
|
8795
|
+
},
|
|
8796
|
+
"./tree-tracker": {
|
|
8797
|
+
types: "./dist/tree-tracker.d.ts",
|
|
8798
|
+
import: "./dist/tree-tracker.js"
|
|
8799
|
+
},
|
|
8788
8800
|
"./server": {
|
|
8789
8801
|
types: "./dist/server/agent-server.d.ts",
|
|
8790
8802
|
import: "./dist/server/agent-server.js"
|
|
8803
|
+
},
|
|
8804
|
+
"./server/schemas": {
|
|
8805
|
+
types: "./dist/server/schemas.d.ts",
|
|
8806
|
+
import: "./dist/server/schemas.js"
|
|
8791
8807
|
}
|
|
8792
8808
|
},
|
|
8793
8809
|
bin: {
|
|
@@ -8878,6 +8894,8 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
8878
8894
|
SDK_SESSION: "_posthog/sdk_session",
|
|
8879
8895
|
/** Tree state snapshot captured (git tree hash + file archive) */
|
|
8880
8896
|
TREE_SNAPSHOT: "_posthog/tree_snapshot",
|
|
8897
|
+
/** Git checkpoint captured for handoff */
|
|
8898
|
+
GIT_CHECKPOINT: "_posthog/git_checkpoint",
|
|
8881
8899
|
/** Agent mode changed (interactive/background) */
|
|
8882
8900
|
MODE_CHANGE: "_posthog/mode_change",
|
|
8883
8901
|
/** Request to resume a session from previous state */
|
|
@@ -8984,17 +9002,17 @@ var Pushable = class {
|
|
|
8984
9002
|
resolvers = [];
|
|
8985
9003
|
done = false;
|
|
8986
9004
|
push(item) {
|
|
8987
|
-
const
|
|
8988
|
-
if (
|
|
8989
|
-
|
|
9005
|
+
const resolve7 = this.resolvers.shift();
|
|
9006
|
+
if (resolve7) {
|
|
9007
|
+
resolve7({ value: item, done: false });
|
|
8990
9008
|
} else {
|
|
8991
9009
|
this.queue.push(item);
|
|
8992
9010
|
}
|
|
8993
9011
|
}
|
|
8994
9012
|
end() {
|
|
8995
9013
|
this.done = true;
|
|
8996
|
-
for (const
|
|
8997
|
-
|
|
9014
|
+
for (const resolve7 of this.resolvers) {
|
|
9015
|
+
resolve7({ value: void 0, done: true });
|
|
8998
9016
|
}
|
|
8999
9017
|
this.resolvers = [];
|
|
9000
9018
|
}
|
|
@@ -9011,8 +9029,8 @@ var Pushable = class {
|
|
|
9011
9029
|
done: true
|
|
9012
9030
|
});
|
|
9013
9031
|
}
|
|
9014
|
-
return new Promise((
|
|
9015
|
-
this.resolvers.push(
|
|
9032
|
+
return new Promise((resolve7) => {
|
|
9033
|
+
this.resolvers.push(resolve7);
|
|
9016
9034
|
});
|
|
9017
9035
|
}
|
|
9018
9036
|
};
|
|
@@ -9126,20 +9144,20 @@ function nodeReadableToWebReadable(nodeStream) {
|
|
|
9126
9144
|
function nodeWritableToWebWritable(nodeStream) {
|
|
9127
9145
|
return new import_web.WritableStream({
|
|
9128
9146
|
write(chunk) {
|
|
9129
|
-
return new Promise((
|
|
9147
|
+
return new Promise((resolve7, reject) => {
|
|
9130
9148
|
const ok = nodeStream.write(Buffer.from(chunk), (err2) => {
|
|
9131
9149
|
if (err2) reject(err2);
|
|
9132
9150
|
});
|
|
9133
9151
|
if (ok) {
|
|
9134
|
-
|
|
9152
|
+
resolve7();
|
|
9135
9153
|
} else {
|
|
9136
|
-
nodeStream.once("drain",
|
|
9154
|
+
nodeStream.once("drain", resolve7);
|
|
9137
9155
|
}
|
|
9138
9156
|
});
|
|
9139
9157
|
},
|
|
9140
9158
|
close() {
|
|
9141
|
-
return new Promise((
|
|
9142
|
-
nodeStream.end(
|
|
9159
|
+
return new Promise((resolve7) => {
|
|
9160
|
+
nodeStream.end(resolve7);
|
|
9143
9161
|
});
|
|
9144
9162
|
},
|
|
9145
9163
|
abort(reason) {
|
|
@@ -13081,9 +13099,9 @@ var PostHogEnricher = class {
|
|
|
13081
13099
|
}
|
|
13082
13100
|
let mtimeMs = 0;
|
|
13083
13101
|
try {
|
|
13084
|
-
const
|
|
13085
|
-
mtimeMs =
|
|
13086
|
-
if (
|
|
13102
|
+
const stat22 = await fs4.stat(absPath);
|
|
13103
|
+
mtimeMs = stat22.mtimeMs;
|
|
13104
|
+
if (stat22.size > MAX_WRAPPER_SOURCE_BYTES) {
|
|
13087
13105
|
return this.setWrapperCache(absPath, mtimeMs, []);
|
|
13088
13106
|
}
|
|
13089
13107
|
} catch {
|
|
@@ -13249,7 +13267,7 @@ async function buildWrapperContext(deps, content, langId, absPath) {
|
|
|
13249
13267
|
// src/utils/common.ts
|
|
13250
13268
|
async function withTimeout(operation, timeoutMs) {
|
|
13251
13269
|
const timeoutPromise = new Promise(
|
|
13252
|
-
(
|
|
13270
|
+
(resolve7) => setTimeout(() => resolve7({ result: "timeout" }), timeoutMs)
|
|
13253
13271
|
);
|
|
13254
13272
|
const operationPromise = operation.then((value) => ({
|
|
13255
13273
|
result: "success",
|
|
@@ -13547,8 +13565,8 @@ var ToolContentBuilder = class {
|
|
|
13547
13565
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
13548
13566
|
return this;
|
|
13549
13567
|
}
|
|
13550
|
-
diff(
|
|
13551
|
-
this.items.push({ type: "diff", path:
|
|
13568
|
+
diff(path17, oldText, newText) {
|
|
13569
|
+
this.items.push({ type: "diff", path: path17, oldText, newText });
|
|
13552
13570
|
return this;
|
|
13553
13571
|
}
|
|
13554
13572
|
build() {
|
|
@@ -13735,7 +13753,7 @@ function buildToolKey(serverName, toolName) {
|
|
|
13735
13753
|
return `mcp__${serverName}__${toolName}`;
|
|
13736
13754
|
}
|
|
13737
13755
|
function delay2(ms) {
|
|
13738
|
-
return new Promise((
|
|
13756
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
13739
13757
|
}
|
|
13740
13758
|
async function fetchMcpToolMetadata(q, logger = new Logger({ debug: false, prefix: "[McpToolMetadata]" })) {
|
|
13741
13759
|
let retries = 0;
|
|
@@ -15949,8 +15967,8 @@ var AsyncMutex = class {
|
|
|
15949
15967
|
this.locked = true;
|
|
15950
15968
|
return;
|
|
15951
15969
|
}
|
|
15952
|
-
return new Promise((
|
|
15953
|
-
this.queue.push(
|
|
15970
|
+
return new Promise((resolve7) => {
|
|
15971
|
+
this.queue.push(resolve7);
|
|
15954
15972
|
});
|
|
15955
15973
|
}
|
|
15956
15974
|
release() {
|
|
@@ -16477,8 +16495,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
16477
16495
|
if (this.session.promptRunning) {
|
|
16478
16496
|
this.session.input.push(userMessage);
|
|
16479
16497
|
const order = this.session.nextPendingOrder++;
|
|
16480
|
-
const cancelled = await new Promise((
|
|
16481
|
-
this.session.pendingMessages.set(promptUuid, { resolve:
|
|
16498
|
+
const cancelled = await new Promise((resolve7) => {
|
|
16499
|
+
this.session.pendingMessages.set(promptUuid, { resolve: resolve7, order });
|
|
16482
16500
|
});
|
|
16483
16501
|
if (cancelled) {
|
|
16484
16502
|
return { stopReason: "cancelled" };
|
|
@@ -17359,7 +17377,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
17359
17377
|
*/
|
|
17360
17378
|
deferBackgroundFetches(q) {
|
|
17361
17379
|
Promise.all([
|
|
17362
|
-
new Promise((
|
|
17380
|
+
new Promise((resolve7) => setTimeout(resolve7, 10)).then(
|
|
17363
17381
|
() => this.sendAvailableCommandsUpdate()
|
|
17364
17382
|
),
|
|
17365
17383
|
fetchMcpToolMetadata(q, this.logger).then(() => {
|
|
@@ -18323,245 +18341,19 @@ function createCodexConnection(config) {
|
|
|
18323
18341
|
};
|
|
18324
18342
|
}
|
|
18325
18343
|
|
|
18326
|
-
// src/
|
|
18344
|
+
// src/handoff-checkpoint.ts
|
|
18345
|
+
var import_promises3 = require("fs/promises");
|
|
18346
|
+
var import_node_path6 = require("path");
|
|
18347
|
+
|
|
18348
|
+
// ../git/dist/handoff.js
|
|
18349
|
+
var import_node_child_process4 = require("child_process");
|
|
18350
|
+
var import_promises2 = require("fs/promises");
|
|
18351
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
18352
|
+
|
|
18353
|
+
// ../git/dist/sagas/checkpoint.js
|
|
18327
18354
|
var import_node_crypto2 = require("crypto");
|
|
18328
18355
|
var fs10 = __toESM(require("fs/promises"), 1);
|
|
18329
|
-
var os6 = __toESM(require("os"), 1);
|
|
18330
18356
|
var path12 = __toESM(require("path"), 1);
|
|
18331
|
-
var CHARS_PER_TOKEN = 4;
|
|
18332
|
-
var DEFAULT_MAX_TOKENS = 15e4;
|
|
18333
|
-
function estimateTurnTokens(turn) {
|
|
18334
|
-
let chars = 0;
|
|
18335
|
-
for (const block of turn.content) {
|
|
18336
|
-
if ("text" in block && typeof block.text === "string") {
|
|
18337
|
-
chars += block.text.length;
|
|
18338
|
-
}
|
|
18339
|
-
}
|
|
18340
|
-
if (turn.toolCalls) {
|
|
18341
|
-
for (const tc of turn.toolCalls) {
|
|
18342
|
-
chars += JSON.stringify(tc.input ?? "").length;
|
|
18343
|
-
if (tc.result !== void 0) {
|
|
18344
|
-
chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
|
|
18345
|
-
}
|
|
18346
|
-
}
|
|
18347
|
-
}
|
|
18348
|
-
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
18349
|
-
}
|
|
18350
|
-
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
18351
|
-
let budget = maxTokens;
|
|
18352
|
-
let startIndex = turns.length;
|
|
18353
|
-
for (let i2 = turns.length - 1; i2 >= 0; i2--) {
|
|
18354
|
-
const cost = estimateTurnTokens(turns[i2]);
|
|
18355
|
-
if (cost > budget) break;
|
|
18356
|
-
budget -= cost;
|
|
18357
|
-
startIndex = i2;
|
|
18358
|
-
}
|
|
18359
|
-
while (startIndex < turns.length && turns[startIndex].role !== "user") {
|
|
18360
|
-
startIndex++;
|
|
18361
|
-
}
|
|
18362
|
-
return turns.slice(startIndex);
|
|
18363
|
-
}
|
|
18364
|
-
|
|
18365
|
-
// src/utils/gateway.ts
|
|
18366
|
-
function getGatewayBaseUrl(posthogHost) {
|
|
18367
|
-
const url = new URL(posthogHost);
|
|
18368
|
-
const hostname = url.hostname;
|
|
18369
|
-
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
18370
|
-
return `${url.protocol}//localhost:3308`;
|
|
18371
|
-
}
|
|
18372
|
-
if (hostname === "host.docker.internal") {
|
|
18373
|
-
return `${url.protocol}//host.docker.internal:3308`;
|
|
18374
|
-
}
|
|
18375
|
-
const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us";
|
|
18376
|
-
return `https://gateway.${region}.posthog.com`;
|
|
18377
|
-
}
|
|
18378
|
-
function getLlmGatewayUrl(posthogHost, product = "posthog_code") {
|
|
18379
|
-
return `${getGatewayBaseUrl(posthogHost)}/${product}`;
|
|
18380
|
-
}
|
|
18381
|
-
|
|
18382
|
-
// src/posthog-api.ts
|
|
18383
|
-
var DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${package_default.version}`;
|
|
18384
|
-
var PostHogAPIClient = class {
|
|
18385
|
-
config;
|
|
18386
|
-
constructor(config) {
|
|
18387
|
-
this.config = config;
|
|
18388
|
-
}
|
|
18389
|
-
get baseUrl() {
|
|
18390
|
-
const host = this.config.apiUrl.endsWith("/") ? this.config.apiUrl.slice(0, -1) : this.config.apiUrl;
|
|
18391
|
-
return host;
|
|
18392
|
-
}
|
|
18393
|
-
isAuthFailure(status) {
|
|
18394
|
-
return status === 401 || status === 403;
|
|
18395
|
-
}
|
|
18396
|
-
async resolveApiKey(forceRefresh = false) {
|
|
18397
|
-
if (forceRefresh && this.config.refreshApiKey) {
|
|
18398
|
-
return this.config.refreshApiKey();
|
|
18399
|
-
}
|
|
18400
|
-
return this.config.getApiKey();
|
|
18401
|
-
}
|
|
18402
|
-
async buildHeaders(options, forceRefresh = false) {
|
|
18403
|
-
const headers = new Headers(options.headers);
|
|
18404
|
-
headers.set(
|
|
18405
|
-
"Authorization",
|
|
18406
|
-
`Bearer ${await this.resolveApiKey(forceRefresh)}`
|
|
18407
|
-
);
|
|
18408
|
-
headers.set("Content-Type", "application/json");
|
|
18409
|
-
headers.set("User-Agent", this.config.userAgent ?? DEFAULT_USER_AGENT);
|
|
18410
|
-
return headers;
|
|
18411
|
-
}
|
|
18412
|
-
async performRequest(endpoint, options, forceRefresh = false) {
|
|
18413
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
18414
|
-
return fetch(url, {
|
|
18415
|
-
...options,
|
|
18416
|
-
headers: await this.buildHeaders(options, forceRefresh)
|
|
18417
|
-
});
|
|
18418
|
-
}
|
|
18419
|
-
async performRequestWithRetry(endpoint, options = {}) {
|
|
18420
|
-
let response = await this.performRequest(endpoint, options);
|
|
18421
|
-
if (!response.ok && this.isAuthFailure(response.status)) {
|
|
18422
|
-
response = await this.performRequest(endpoint, options, true);
|
|
18423
|
-
}
|
|
18424
|
-
return response;
|
|
18425
|
-
}
|
|
18426
|
-
async apiRequest(endpoint, options = {}) {
|
|
18427
|
-
const response = await this.performRequestWithRetry(endpoint, options);
|
|
18428
|
-
if (!response.ok) {
|
|
18429
|
-
let errorMessage;
|
|
18430
|
-
try {
|
|
18431
|
-
const errorResponse = await response.json();
|
|
18432
|
-
errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;
|
|
18433
|
-
} catch {
|
|
18434
|
-
errorMessage = `Failed request: [${response.status}] ${response.statusText}`;
|
|
18435
|
-
}
|
|
18436
|
-
throw new Error(errorMessage);
|
|
18437
|
-
}
|
|
18438
|
-
return response.json();
|
|
18439
|
-
}
|
|
18440
|
-
getTeamId() {
|
|
18441
|
-
return this.config.projectId;
|
|
18442
|
-
}
|
|
18443
|
-
async getApiKey(forceRefresh = false) {
|
|
18444
|
-
return this.resolveApiKey(forceRefresh);
|
|
18445
|
-
}
|
|
18446
|
-
getLlmGatewayUrl() {
|
|
18447
|
-
return getLlmGatewayUrl(this.baseUrl);
|
|
18448
|
-
}
|
|
18449
|
-
async getTask(taskId) {
|
|
18450
|
-
const teamId = this.getTeamId();
|
|
18451
|
-
return this.apiRequest(`/api/projects/${teamId}/tasks/${taskId}/`);
|
|
18452
|
-
}
|
|
18453
|
-
async getTaskRun(taskId, runId) {
|
|
18454
|
-
const teamId = this.getTeamId();
|
|
18455
|
-
return this.apiRequest(
|
|
18456
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`
|
|
18457
|
-
);
|
|
18458
|
-
}
|
|
18459
|
-
async updateTaskRun(taskId, runId, payload) {
|
|
18460
|
-
const teamId = this.getTeamId();
|
|
18461
|
-
return this.apiRequest(
|
|
18462
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,
|
|
18463
|
-
{
|
|
18464
|
-
method: "PATCH",
|
|
18465
|
-
body: JSON.stringify(payload)
|
|
18466
|
-
}
|
|
18467
|
-
);
|
|
18468
|
-
}
|
|
18469
|
-
async setTaskRunOutput(taskId, runId, output) {
|
|
18470
|
-
return this.apiRequest(
|
|
18471
|
-
`/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,
|
|
18472
|
-
{
|
|
18473
|
-
method: "PATCH",
|
|
18474
|
-
body: JSON.stringify(output)
|
|
18475
|
-
}
|
|
18476
|
-
);
|
|
18477
|
-
}
|
|
18478
|
-
async appendTaskRunLog(taskId, runId, entries) {
|
|
18479
|
-
const teamId = this.getTeamId();
|
|
18480
|
-
return this.apiRequest(
|
|
18481
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,
|
|
18482
|
-
{
|
|
18483
|
-
method: "POST",
|
|
18484
|
-
body: JSON.stringify({ entries })
|
|
18485
|
-
}
|
|
18486
|
-
);
|
|
18487
|
-
}
|
|
18488
|
-
async relayMessage(taskId, runId, text2) {
|
|
18489
|
-
const teamId = this.getTeamId();
|
|
18490
|
-
await this.apiRequest(
|
|
18491
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,
|
|
18492
|
-
{
|
|
18493
|
-
method: "POST",
|
|
18494
|
-
body: JSON.stringify({ text: text2 })
|
|
18495
|
-
}
|
|
18496
|
-
);
|
|
18497
|
-
}
|
|
18498
|
-
async uploadTaskArtifacts(taskId, runId, artifacts) {
|
|
18499
|
-
if (!artifacts.length) {
|
|
18500
|
-
return [];
|
|
18501
|
-
}
|
|
18502
|
-
const teamId = this.getTeamId();
|
|
18503
|
-
const response = await this.apiRequest(
|
|
18504
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
18505
|
-
{
|
|
18506
|
-
method: "POST",
|
|
18507
|
-
body: JSON.stringify({ artifacts })
|
|
18508
|
-
}
|
|
18509
|
-
);
|
|
18510
|
-
return response.artifacts ?? [];
|
|
18511
|
-
}
|
|
18512
|
-
/**
|
|
18513
|
-
* Download artifact content by storage path
|
|
18514
|
-
* Streams the file through the PostHog backend so the sandbox does not need
|
|
18515
|
-
* direct access to object storage.
|
|
18516
|
-
*/
|
|
18517
|
-
async downloadArtifact(taskId, runId, storagePath) {
|
|
18518
|
-
const teamId = this.getTeamId();
|
|
18519
|
-
try {
|
|
18520
|
-
const response = await this.performRequestWithRetry(
|
|
18521
|
-
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,
|
|
18522
|
-
{
|
|
18523
|
-
method: "POST",
|
|
18524
|
-
body: JSON.stringify({ storage_path: storagePath })
|
|
18525
|
-
}
|
|
18526
|
-
);
|
|
18527
|
-
if (!response.ok) {
|
|
18528
|
-
throw new Error(`Failed to download artifact: ${response.status}`);
|
|
18529
|
-
}
|
|
18530
|
-
return response.arrayBuffer();
|
|
18531
|
-
} catch {
|
|
18532
|
-
return null;
|
|
18533
|
-
}
|
|
18534
|
-
}
|
|
18535
|
-
/**
|
|
18536
|
-
* Fetch logs for a task run via the logs API endpoint
|
|
18537
|
-
* @param taskRun - The task run to fetch logs for
|
|
18538
|
-
* @returns Array of stored entries, or empty array if no logs available
|
|
18539
|
-
*/
|
|
18540
|
-
async fetchTaskRunLogs(taskRun) {
|
|
18541
|
-
const teamId = this.getTeamId();
|
|
18542
|
-
const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;
|
|
18543
|
-
try {
|
|
18544
|
-
const response = await this.performRequestWithRetry(endpoint);
|
|
18545
|
-
if (!response.ok) {
|
|
18546
|
-
if (response.status === 404) {
|
|
18547
|
-
return [];
|
|
18548
|
-
}
|
|
18549
|
-
throw new Error(
|
|
18550
|
-
`Failed to fetch logs: ${response.status} ${response.statusText}`
|
|
18551
|
-
);
|
|
18552
|
-
}
|
|
18553
|
-
const content = await response.text();
|
|
18554
|
-
if (!content.trim()) {
|
|
18555
|
-
return [];
|
|
18556
|
-
}
|
|
18557
|
-
return content.trim().split("\n").map((line) => JSON.parse(line));
|
|
18558
|
-
} catch (error) {
|
|
18559
|
-
throw new Error(
|
|
18560
|
-
`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`
|
|
18561
|
-
);
|
|
18562
|
-
}
|
|
18563
|
-
}
|
|
18564
|
-
};
|
|
18565
18357
|
|
|
18566
18358
|
// ../shared/dist/index.js
|
|
18567
18359
|
var CLOUD_PROMPT_PREFIX = "__twig_cloud_prompt_v1__:";
|
|
@@ -18705,16 +18497,6 @@ var Saga = class {
|
|
|
18705
18497
|
}
|
|
18706
18498
|
};
|
|
18707
18499
|
|
|
18708
|
-
// src/sagas/apply-snapshot-saga.ts
|
|
18709
|
-
var import_promises2 = require("fs/promises");
|
|
18710
|
-
var import_node_path5 = require("path");
|
|
18711
|
-
|
|
18712
|
-
// ../git/dist/sagas/tree.js
|
|
18713
|
-
var import_node_fs3 = require("fs");
|
|
18714
|
-
var fs11 = __toESM(require("fs/promises"), 1);
|
|
18715
|
-
var path13 = __toESM(require("path"), 1);
|
|
18716
|
-
var tar = __toESM(require("tar"), 1);
|
|
18717
|
-
|
|
18718
18500
|
// ../git/dist/git-saga.js
|
|
18719
18501
|
var GitSaga = class extends Saga {
|
|
18720
18502
|
_git = null;
|
|
@@ -18733,20 +18515,1082 @@ var GitSaga = class extends Saga {
|
|
|
18733
18515
|
}
|
|
18734
18516
|
};
|
|
18735
18517
|
|
|
18736
|
-
// ../git/dist/sagas/
|
|
18737
|
-
var
|
|
18518
|
+
// ../git/dist/sagas/checkpoint.js
|
|
18519
|
+
var CHECKPOINT_REF_PREFIX = "refs/posthog-code-checkpoint/";
|
|
18520
|
+
var CHECKPOINT_VERSION = "v1";
|
|
18521
|
+
var UNMERGED_INDEX_ERROR = "Cannot capture checkpoint with unresolved merge conflicts in the index";
|
|
18522
|
+
var GIT_BUSY_ERROR = "Cannot capture checkpoint while git operation is in progress";
|
|
18523
|
+
var CHECKPOINT_AUTHOR = {
|
|
18524
|
+
name: "PostHog Code",
|
|
18525
|
+
email: "posthog-code@local"
|
|
18526
|
+
};
|
|
18527
|
+
var CaptureCheckpointSaga = class extends GitSaga {
|
|
18528
|
+
sagaName = "CaptureCheckpointSaga";
|
|
18529
|
+
async executeGitOperations(input) {
|
|
18530
|
+
const { baseDir } = input;
|
|
18531
|
+
const headInfo = await this.readOnlyStep("get_head_info", () => getHeadInfo(this.git));
|
|
18532
|
+
const busyState = await this.readOnlyStep("check_git_busy", () => getGitBusyState(this.git));
|
|
18533
|
+
if (busyState.busy) {
|
|
18534
|
+
throw new Error(`${GIT_BUSY_ERROR}: ${busyState.operation}`);
|
|
18535
|
+
}
|
|
18536
|
+
const hasUnmerged = await this.readOnlyStep("check_unmerged_index", () => hasUnmergedEntries(this.git));
|
|
18537
|
+
if (hasUnmerged) {
|
|
18538
|
+
throw new Error(UNMERGED_INDEX_ERROR);
|
|
18539
|
+
}
|
|
18540
|
+
const indexTree = await this.readOnlyStep("write_index_tree", () => this.git.raw(["write-tree"]));
|
|
18541
|
+
const worktreeTree = await this.readOnlyStep("write_worktree_tree", () => createWorktreeTree(this.git, baseDir, headInfo.head));
|
|
18542
|
+
const metaTree = await this.readOnlyStep("write_meta_tree", () => createMetaTree(this.git, baseDir, indexTree.trim(), worktreeTree.trim()));
|
|
18543
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
18544
|
+
const message = formatCheckpointMessage({
|
|
18545
|
+
head: headInfo.head,
|
|
18546
|
+
branch: headInfo.branch,
|
|
18547
|
+
indexTree: indexTree.trim(),
|
|
18548
|
+
worktreeTree: worktreeTree.trim(),
|
|
18549
|
+
timestamp
|
|
18550
|
+
});
|
|
18551
|
+
const commitHash = await this.step({
|
|
18552
|
+
name: "create_checkpoint_commit",
|
|
18553
|
+
execute: async () => {
|
|
18554
|
+
const commitGit = this.git.env({
|
|
18555
|
+
...process.env,
|
|
18556
|
+
GIT_AUTHOR_NAME: CHECKPOINT_AUTHOR.name,
|
|
18557
|
+
GIT_AUTHOR_EMAIL: CHECKPOINT_AUTHOR.email,
|
|
18558
|
+
GIT_COMMITTER_NAME: CHECKPOINT_AUTHOR.name,
|
|
18559
|
+
GIT_COMMITTER_EMAIL: CHECKPOINT_AUTHOR.email
|
|
18560
|
+
});
|
|
18561
|
+
const rawCommit = await commitGit.raw([
|
|
18562
|
+
"commit-tree",
|
|
18563
|
+
metaTree.trim(),
|
|
18564
|
+
...headInfo.head ? ["-p", headInfo.head] : [],
|
|
18565
|
+
"-m",
|
|
18566
|
+
message
|
|
18567
|
+
]);
|
|
18568
|
+
return rawCommit.trim();
|
|
18569
|
+
},
|
|
18570
|
+
rollback: async () => {
|
|
18571
|
+
}
|
|
18572
|
+
});
|
|
18573
|
+
const checkpointId = input.checkpointId ?? (0, import_node_crypto2.randomUUID)();
|
|
18574
|
+
const refName = `${CHECKPOINT_REF_PREFIX}${checkpointId}`;
|
|
18575
|
+
const existingRef = await this.readOnlyStep("check_existing_ref", async () => {
|
|
18576
|
+
try {
|
|
18577
|
+
await this.git.revparse(["--verify", refName]);
|
|
18578
|
+
return true;
|
|
18579
|
+
} catch {
|
|
18580
|
+
return false;
|
|
18581
|
+
}
|
|
18582
|
+
});
|
|
18583
|
+
if (existingRef) {
|
|
18584
|
+
throw new Error(`Checkpoint ref already exists: ${refName}`);
|
|
18585
|
+
}
|
|
18586
|
+
await this.step({
|
|
18587
|
+
name: "update_checkpoint_ref",
|
|
18588
|
+
execute: () => this.git.raw(["update-ref", refName, commitHash]),
|
|
18589
|
+
rollback: async () => {
|
|
18590
|
+
await this.git.raw(["update-ref", "-d", refName]).catch(() => {
|
|
18591
|
+
});
|
|
18592
|
+
}
|
|
18593
|
+
});
|
|
18594
|
+
return {
|
|
18595
|
+
checkpointId,
|
|
18596
|
+
commit: commitHash,
|
|
18597
|
+
head: headInfo.head,
|
|
18598
|
+
branch: headInfo.branch,
|
|
18599
|
+
indexTree: indexTree.trim(),
|
|
18600
|
+
worktreeTree: worktreeTree.trim(),
|
|
18601
|
+
timestamp
|
|
18602
|
+
};
|
|
18603
|
+
}
|
|
18604
|
+
};
|
|
18605
|
+
async function getHeadInfo(git) {
|
|
18606
|
+
let head = null;
|
|
18607
|
+
let branch = null;
|
|
18608
|
+
try {
|
|
18609
|
+
head = (await git.revparse(["HEAD"]))?.trim() || null;
|
|
18610
|
+
} catch {
|
|
18611
|
+
head = null;
|
|
18612
|
+
}
|
|
18613
|
+
try {
|
|
18614
|
+
const rawBranch = await git.raw(["symbolic-ref", "--short", "HEAD"]);
|
|
18615
|
+
branch = rawBranch.trim() || null;
|
|
18616
|
+
} catch {
|
|
18617
|
+
branch = null;
|
|
18618
|
+
}
|
|
18619
|
+
return { head, branch };
|
|
18620
|
+
}
|
|
18621
|
+
async function hasUnmergedEntries(git) {
|
|
18622
|
+
const output = await git.raw(["ls-files", "--unmerged"]);
|
|
18623
|
+
return output.trim().length > 0;
|
|
18624
|
+
}
|
|
18625
|
+
async function getGitBusyState(git) {
|
|
18626
|
+
const toplevel = (await git.raw(["rev-parse", "--show-toplevel"])).trim();
|
|
18627
|
+
const resolveGitPath = async (gitPath) => {
|
|
18628
|
+
const relative = (await git.raw(["rev-parse", "--git-path", gitPath])).trim();
|
|
18629
|
+
return path12.isAbsolute(relative) ? relative : path12.resolve(toplevel, relative);
|
|
18630
|
+
};
|
|
18631
|
+
const pathExists = async (gitPath) => {
|
|
18632
|
+
const resolved = await resolveGitPath(gitPath);
|
|
18633
|
+
try {
|
|
18634
|
+
await fs10.access(resolved);
|
|
18635
|
+
return true;
|
|
18636
|
+
} catch {
|
|
18637
|
+
return false;
|
|
18638
|
+
}
|
|
18639
|
+
};
|
|
18640
|
+
const dirExists = async (gitPath) => {
|
|
18641
|
+
const resolved = await resolveGitPath(gitPath);
|
|
18642
|
+
try {
|
|
18643
|
+
const stat4 = await fs10.stat(resolved);
|
|
18644
|
+
return stat4.isDirectory();
|
|
18645
|
+
} catch {
|
|
18646
|
+
return false;
|
|
18647
|
+
}
|
|
18648
|
+
};
|
|
18649
|
+
if (await dirExists("rebase-merge") || await dirExists("rebase-apply")) {
|
|
18650
|
+
return { busy: true, operation: "rebase" };
|
|
18651
|
+
}
|
|
18652
|
+
if (await pathExists("MERGE_HEAD")) {
|
|
18653
|
+
return { busy: true, operation: "merge" };
|
|
18654
|
+
}
|
|
18655
|
+
if (await pathExists("CHERRY_PICK_HEAD")) {
|
|
18656
|
+
return { busy: true, operation: "cherry-pick" };
|
|
18657
|
+
}
|
|
18658
|
+
if (await pathExists("REVERT_HEAD")) {
|
|
18659
|
+
return { busy: true, operation: "revert" };
|
|
18660
|
+
}
|
|
18661
|
+
return { busy: false };
|
|
18662
|
+
}
|
|
18663
|
+
async function createWorktreeTree(git, baseDir, head) {
|
|
18664
|
+
const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-worktree");
|
|
18665
|
+
try {
|
|
18666
|
+
if (head) {
|
|
18667
|
+
await tempGit.raw(["read-tree", head]);
|
|
18668
|
+
} else {
|
|
18669
|
+
await tempGit.raw(["read-tree", "--empty"]);
|
|
18670
|
+
}
|
|
18671
|
+
await tempGit.raw(["add", "-A", "--", "."]);
|
|
18672
|
+
const treeHash = await tempGit.raw(["write-tree"]);
|
|
18673
|
+
return treeHash.trim();
|
|
18674
|
+
} finally {
|
|
18675
|
+
await fs10.rm(tempIndexPath, { force: true }).catch(() => {
|
|
18676
|
+
});
|
|
18677
|
+
}
|
|
18678
|
+
}
|
|
18679
|
+
async function createMetaTree(git, baseDir, indexTree, worktreeTree) {
|
|
18680
|
+
const { tempGit, tempIndexPath } = await createTempIndexGit(git, baseDir, "checkpoint-meta");
|
|
18681
|
+
try {
|
|
18682
|
+
await tempGit.raw(["read-tree", "--empty"]);
|
|
18683
|
+
await tempGit.raw([
|
|
18684
|
+
"update-index",
|
|
18685
|
+
"--add",
|
|
18686
|
+
"--cacheinfo",
|
|
18687
|
+
"040000",
|
|
18688
|
+
indexTree,
|
|
18689
|
+
"index"
|
|
18690
|
+
]);
|
|
18691
|
+
await tempGit.raw([
|
|
18692
|
+
"update-index",
|
|
18693
|
+
"--add",
|
|
18694
|
+
"--cacheinfo",
|
|
18695
|
+
"040000",
|
|
18696
|
+
worktreeTree,
|
|
18697
|
+
"worktree"
|
|
18698
|
+
]);
|
|
18699
|
+
const metaTree = await tempGit.raw(["write-tree"]);
|
|
18700
|
+
return metaTree.trim();
|
|
18701
|
+
} finally {
|
|
18702
|
+
await fs10.rm(tempIndexPath, { force: true }).catch(() => {
|
|
18703
|
+
});
|
|
18704
|
+
}
|
|
18705
|
+
}
|
|
18706
|
+
function formatCheckpointMessage(meta) {
|
|
18707
|
+
return [
|
|
18708
|
+
`POSTHOG-CODE-CHECKPOINT ${CHECKPOINT_VERSION}`,
|
|
18709
|
+
`head=${meta.head ?? "null"}`,
|
|
18710
|
+
`branch=${meta.branch ?? "null"}`,
|
|
18711
|
+
`index=${meta.indexTree}`,
|
|
18712
|
+
`worktree=${meta.worktreeTree}`,
|
|
18713
|
+
`timestamp=${meta.timestamp}`
|
|
18714
|
+
].join("\n");
|
|
18715
|
+
}
|
|
18716
|
+
async function getGitCommonDir(git, baseDir) {
|
|
18717
|
+
const raw = await git.raw(["rev-parse", "--git-common-dir"]);
|
|
18718
|
+
const dir = raw.trim() || ".git";
|
|
18719
|
+
return path12.isAbsolute(dir) ? dir : path12.resolve(baseDir, dir);
|
|
18720
|
+
}
|
|
18721
|
+
async function createTempIndexGit(git, baseDir, label) {
|
|
18722
|
+
const tmpDir = path12.join(await getGitCommonDir(git, baseDir), "posthog-code-tmp");
|
|
18723
|
+
await fs10.mkdir(tmpDir, { recursive: true });
|
|
18724
|
+
const tempIndexPath = path12.join(tmpDir, `${label}-${Date.now()}-${(0, import_node_crypto2.randomUUID)()}`);
|
|
18725
|
+
const tempGit = createGitClient(baseDir).env({
|
|
18726
|
+
...process.env,
|
|
18727
|
+
GIT_INDEX_FILE: tempIndexPath
|
|
18728
|
+
});
|
|
18729
|
+
return { tempGit, tempIndexPath };
|
|
18730
|
+
}
|
|
18731
|
+
async function refExists(git, refName) {
|
|
18732
|
+
try {
|
|
18733
|
+
await git.revparse(["--verify", refName]);
|
|
18734
|
+
return true;
|
|
18735
|
+
} catch {
|
|
18736
|
+
return false;
|
|
18737
|
+
}
|
|
18738
|
+
}
|
|
18739
|
+
async function deleteCheckpoint(git, checkpointId) {
|
|
18740
|
+
const refName = `${CHECKPOINT_REF_PREFIX}${checkpointId}`;
|
|
18741
|
+
const exists2 = await refExists(git, refName);
|
|
18742
|
+
if (!exists2) {
|
|
18743
|
+
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
18744
|
+
}
|
|
18745
|
+
await git.raw(["update-ref", "-d", refName]);
|
|
18746
|
+
}
|
|
18747
|
+
|
|
18748
|
+
// ../git/dist/handoff.js
|
|
18749
|
+
var HANDOFF_HEAD_REF_PREFIX = "refs/posthog-code-handoff/head/";
|
|
18750
|
+
var CHECKPOINT_REF_PREFIX2 = "refs/posthog-code-checkpoint/";
|
|
18751
|
+
var GitHandoffTracker = class {
|
|
18752
|
+
repositoryPath;
|
|
18753
|
+
logger;
|
|
18754
|
+
constructor(config) {
|
|
18755
|
+
this.repositoryPath = config.repositoryPath;
|
|
18756
|
+
this.logger = config.logger;
|
|
18757
|
+
}
|
|
18758
|
+
async captureForHandoff(localGitState) {
|
|
18759
|
+
const captureSaga = new CaptureCheckpointSaga(this.logger);
|
|
18760
|
+
const result = await captureSaga.run({ baseDir: this.repositoryPath });
|
|
18761
|
+
if (!result.success) {
|
|
18762
|
+
throw new Error(`Failed to capture checkpoint at step '${result.failedStep}': ${result.error}`);
|
|
18763
|
+
}
|
|
18764
|
+
const checkpoint = result.data;
|
|
18765
|
+
const git = createGitClient(this.repositoryPath);
|
|
18766
|
+
const tempDir = await this.getTempDir(git);
|
|
18767
|
+
const checkpointRef = `${CHECKPOINT_REF_PREFIX2}${checkpoint.checkpointId}`;
|
|
18768
|
+
const shouldIncludeHead = !!checkpoint.head && checkpoint.head !== localGitState?.head;
|
|
18769
|
+
const headRef = shouldIncludeHead ? `${HANDOFF_HEAD_REF_PREFIX}${checkpoint.checkpointId}` : void 0;
|
|
18770
|
+
const packPrefix = import_node_path5.default.join(tempDir, checkpoint.checkpointId);
|
|
18771
|
+
try {
|
|
18772
|
+
const [headPack, indexFile, tracking] = await Promise.all([
|
|
18773
|
+
shouldIncludeHead && checkpoint.head ? this.captureHeadPack(packPrefix, checkpoint.head) : Promise.resolve(void 0),
|
|
18774
|
+
this.copyIndexFile(git, checkpoint.checkpointId),
|
|
18775
|
+
getTrackingMetadata(git, checkpoint.branch)
|
|
18776
|
+
]);
|
|
18777
|
+
return {
|
|
18778
|
+
checkpoint: {
|
|
18779
|
+
checkpointId: checkpoint.checkpointId,
|
|
18780
|
+
commit: checkpoint.commit,
|
|
18781
|
+
checkpointRef,
|
|
18782
|
+
headRef,
|
|
18783
|
+
head: checkpoint.head,
|
|
18784
|
+
branch: checkpoint.branch,
|
|
18785
|
+
indexTree: checkpoint.indexTree,
|
|
18786
|
+
worktreeTree: checkpoint.worktreeTree,
|
|
18787
|
+
timestamp: checkpoint.timestamp,
|
|
18788
|
+
upstreamRemote: tracking.upstreamRemote,
|
|
18789
|
+
upstreamMergeRef: tracking.upstreamMergeRef,
|
|
18790
|
+
remoteUrl: tracking.remoteUrl
|
|
18791
|
+
},
|
|
18792
|
+
headPack,
|
|
18793
|
+
indexFile,
|
|
18794
|
+
totalBytes: (headPack?.rawBytes ?? 0) + indexFile.rawBytes
|
|
18795
|
+
};
|
|
18796
|
+
} finally {
|
|
18797
|
+
await deleteCheckpoint(git, checkpoint.checkpointId).catch(() => {
|
|
18798
|
+
});
|
|
18799
|
+
}
|
|
18800
|
+
}
|
|
18801
|
+
async applyFromHandoff(input) {
|
|
18802
|
+
const { checkpoint, headPackPath, indexPath, localGitState, onDivergedBranch } = input;
|
|
18803
|
+
const git = createGitClient(this.repositoryPath);
|
|
18804
|
+
if (headPackPath) {
|
|
18805
|
+
await this.unpackPackFile(headPackPath);
|
|
18806
|
+
}
|
|
18807
|
+
if (checkpoint.branch && checkpoint.head) {
|
|
18808
|
+
const branchStatus2 = await this.resolveBranchRestoreStatus(git, checkpoint.branch, checkpoint.head, localGitState);
|
|
18809
|
+
const tracking = this.getPreferredTracking(localGitState, checkpoint);
|
|
18810
|
+
if (branchStatus2.kind === "diverged" && !await onDivergedBranch?.(branchStatus2.divergence)) {
|
|
18811
|
+
throw new Error(`Handoff aborted: local branch '${checkpoint.branch}' has diverged`);
|
|
18812
|
+
}
|
|
18813
|
+
await this.checkoutBranchAtHead(git, checkpoint.branch, checkpoint.head);
|
|
18814
|
+
if (this.shouldRestoreTracking(branchStatus2, localGitState, tracking)) {
|
|
18815
|
+
await this.ensureRemoteForTracking(git, tracking);
|
|
18816
|
+
await this.configureUpstream(git, checkpoint.branch, tracking);
|
|
18817
|
+
}
|
|
18818
|
+
} else if (checkpoint.head) {
|
|
18819
|
+
await git.checkout(checkpoint.head);
|
|
18820
|
+
}
|
|
18821
|
+
if (indexPath) {
|
|
18822
|
+
await this.restoreIndexFile(git, indexPath);
|
|
18823
|
+
}
|
|
18824
|
+
const packBytes = headPackPath ? await this.getFileSize(headPackPath) : 0;
|
|
18825
|
+
const indexBytes = indexPath ? await this.getFileSize(indexPath) : 0;
|
|
18826
|
+
return {
|
|
18827
|
+
packBytes,
|
|
18828
|
+
indexBytes,
|
|
18829
|
+
totalBytes: packBytes + indexBytes
|
|
18830
|
+
};
|
|
18831
|
+
}
|
|
18832
|
+
async captureHeadPack(packPrefix, headCommit) {
|
|
18833
|
+
const hash = await this.runGitWithInput(["pack-objects", packPrefix, "--revs"], `${headCommit}
|
|
18834
|
+
`);
|
|
18835
|
+
const packPath = `${packPrefix}-${hash.trim()}.pack`;
|
|
18836
|
+
const rawBytes = await this.getFileSize(packPath);
|
|
18837
|
+
await (0, import_promises2.rm)(`${packPath}.idx`, { force: true }).catch(() => {
|
|
18838
|
+
});
|
|
18839
|
+
return { path: packPath, rawBytes };
|
|
18840
|
+
}
|
|
18841
|
+
async copyIndexFile(git, checkpointId) {
|
|
18842
|
+
const indexPath = await this.getGitPath(git, "index");
|
|
18843
|
+
const tempDir = await this.getTempDir(git);
|
|
18844
|
+
const copiedIndexPath = import_node_path5.default.join(tempDir, `${checkpointId}.index`);
|
|
18845
|
+
await (0, import_promises2.copyFile)(indexPath, copiedIndexPath);
|
|
18846
|
+
return {
|
|
18847
|
+
path: copiedIndexPath,
|
|
18848
|
+
rawBytes: await this.getFileSize(copiedIndexPath)
|
|
18849
|
+
};
|
|
18850
|
+
}
|
|
18851
|
+
async restoreIndexFile(git, indexPath) {
|
|
18852
|
+
const gitIndexPath = await this.getGitPath(git, "index");
|
|
18853
|
+
await (0, import_promises2.copyFile)(indexPath, gitIndexPath);
|
|
18854
|
+
}
|
|
18855
|
+
async unpackPackFile(packPath) {
|
|
18856
|
+
const content = await (0, import_promises2.readFile)(packPath);
|
|
18857
|
+
await this.runGitWithBuffer(["unpack-objects", "-r"], content);
|
|
18858
|
+
}
|
|
18859
|
+
getPreferredTracking(localGitState, checkpoint) {
|
|
18860
|
+
const state = localGitState;
|
|
18861
|
+
if (state && hasTrackingConfig(state)) {
|
|
18862
|
+
return {
|
|
18863
|
+
upstreamRemote: state.upstreamRemote ?? null,
|
|
18864
|
+
upstreamMergeRef: state.upstreamMergeRef ?? null,
|
|
18865
|
+
remoteUrl: state.upstreamRemote && state.upstreamRemote === checkpoint.upstreamRemote ? checkpoint.remoteUrl : null
|
|
18866
|
+
};
|
|
18867
|
+
}
|
|
18868
|
+
return {
|
|
18869
|
+
upstreamRemote: checkpoint.upstreamRemote,
|
|
18870
|
+
upstreamMergeRef: checkpoint.upstreamMergeRef,
|
|
18871
|
+
remoteUrl: checkpoint.remoteUrl
|
|
18872
|
+
};
|
|
18873
|
+
}
|
|
18874
|
+
shouldRestoreTracking(branchStatus2, localGitState, tracking) {
|
|
18875
|
+
return branchStatus2.kind === "missing" || !hasTrackingConfig(localGitState) && (tracking.upstreamRemote !== null || tracking.upstreamMergeRef !== null);
|
|
18876
|
+
}
|
|
18877
|
+
async ensureRemoteForTracking(git, tracking) {
|
|
18878
|
+
if (!tracking.upstreamRemote || !tracking.remoteUrl)
|
|
18879
|
+
return;
|
|
18880
|
+
const remotes = await git.getRemotes(true);
|
|
18881
|
+
const existing = remotes.find((remote) => remote.name === tracking.upstreamRemote);
|
|
18882
|
+
if (!existing) {
|
|
18883
|
+
await git.addRemote(tracking.upstreamRemote, tracking.remoteUrl);
|
|
18884
|
+
}
|
|
18885
|
+
}
|
|
18886
|
+
async configureUpstream(git, branch, tracking) {
|
|
18887
|
+
if (tracking.upstreamRemote) {
|
|
18888
|
+
await git.raw([
|
|
18889
|
+
"config",
|
|
18890
|
+
`branch.${branch}.remote`,
|
|
18891
|
+
tracking.upstreamRemote
|
|
18892
|
+
]);
|
|
18893
|
+
}
|
|
18894
|
+
if (tracking.upstreamMergeRef) {
|
|
18895
|
+
await git.raw([
|
|
18896
|
+
"config",
|
|
18897
|
+
`branch.${branch}.merge`,
|
|
18898
|
+
tracking.upstreamMergeRef
|
|
18899
|
+
]);
|
|
18900
|
+
}
|
|
18901
|
+
}
|
|
18902
|
+
async resolveBranchRestoreStatus(git, branch, cloudHead, localGitState) {
|
|
18903
|
+
const branchRef = `refs/heads/${branch}`;
|
|
18904
|
+
const branchExists = await this.refExists(git, branchRef);
|
|
18905
|
+
if (!branchExists) {
|
|
18906
|
+
return { kind: "missing" };
|
|
18907
|
+
}
|
|
18908
|
+
const currentBranchHead = (await git.revparse([branchRef])).trim();
|
|
18909
|
+
const candidateHeads = [
|
|
18910
|
+
currentBranchHead,
|
|
18911
|
+
...localGitState?.branch === branch && localGitState.head ? [localGitState.head] : []
|
|
18912
|
+
].filter((value, index, array) => array.indexOf(value) === index);
|
|
18913
|
+
if (candidateHeads.every((head) => head === cloudHead)) {
|
|
18914
|
+
return { kind: "match" };
|
|
18915
|
+
}
|
|
18916
|
+
const nonAncestorHead = await this.findNonAncestorHead(git, candidateHeads, cloudHead);
|
|
18917
|
+
if (!nonAncestorHead) {
|
|
18918
|
+
return { kind: "fast_forward" };
|
|
18919
|
+
}
|
|
18920
|
+
return {
|
|
18921
|
+
kind: "diverged",
|
|
18922
|
+
divergence: {
|
|
18923
|
+
branch,
|
|
18924
|
+
localHead: nonAncestorHead,
|
|
18925
|
+
cloudHead
|
|
18926
|
+
}
|
|
18927
|
+
};
|
|
18928
|
+
}
|
|
18929
|
+
async findNonAncestorHead(_git, heads, cloudHead) {
|
|
18930
|
+
for (const head of heads) {
|
|
18931
|
+
if (head === cloudHead) {
|
|
18932
|
+
continue;
|
|
18933
|
+
}
|
|
18934
|
+
if (!await this.isAncestor(head, cloudHead)) {
|
|
18935
|
+
return head;
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18938
|
+
return null;
|
|
18939
|
+
}
|
|
18940
|
+
async checkoutBranchAtHead(git, branch, head) {
|
|
18941
|
+
const currentBranch = await getCurrentBranchName(git);
|
|
18942
|
+
if (currentBranch === branch) {
|
|
18943
|
+
await git.reset(["--hard", head]);
|
|
18944
|
+
return;
|
|
18945
|
+
}
|
|
18946
|
+
const branchRef = `refs/heads/${branch}`;
|
|
18947
|
+
if (await this.refExists(git, branchRef)) {
|
|
18948
|
+
await git.branch(["-f", branch, head]);
|
|
18949
|
+
await git.checkout(branch);
|
|
18950
|
+
return;
|
|
18951
|
+
}
|
|
18952
|
+
await git.checkout(["-b", branch, head]);
|
|
18953
|
+
}
|
|
18954
|
+
async refExists(git, ref) {
|
|
18955
|
+
try {
|
|
18956
|
+
await git.revparse(["--verify", ref]);
|
|
18957
|
+
return true;
|
|
18958
|
+
} catch {
|
|
18959
|
+
return false;
|
|
18960
|
+
}
|
|
18961
|
+
}
|
|
18962
|
+
async isAncestor(ancestor, descendant) {
|
|
18963
|
+
const exitCode = await this.runGitProcessAllowingFailure([
|
|
18964
|
+
"merge-base",
|
|
18965
|
+
"--is-ancestor",
|
|
18966
|
+
ancestor,
|
|
18967
|
+
descendant
|
|
18968
|
+
]);
|
|
18969
|
+
return exitCode === 0;
|
|
18970
|
+
}
|
|
18971
|
+
async getTempDir(git) {
|
|
18972
|
+
const raw = await git.raw(["rev-parse", "--git-common-dir"]);
|
|
18973
|
+
const commonDir = raw.trim() || ".git";
|
|
18974
|
+
const resolved = import_node_path5.default.isAbsolute(commonDir) ? commonDir : import_node_path5.default.resolve(this.repositoryPath, commonDir);
|
|
18975
|
+
const tempDir = import_node_path5.default.join(resolved, "posthog-code-tmp");
|
|
18976
|
+
await (0, import_promises2.mkdir)(tempDir, { recursive: true });
|
|
18977
|
+
return tempDir;
|
|
18978
|
+
}
|
|
18979
|
+
async getGitPath(git, gitPath) {
|
|
18980
|
+
const raw = await git.raw(["rev-parse", "--git-path", gitPath]);
|
|
18981
|
+
const resolved = raw.trim();
|
|
18982
|
+
return import_node_path5.default.isAbsolute(resolved) ? resolved : import_node_path5.default.resolve(this.repositoryPath, resolved);
|
|
18983
|
+
}
|
|
18984
|
+
async getFileSize(filePath) {
|
|
18985
|
+
return (await (0, import_promises2.stat)(filePath)).size;
|
|
18986
|
+
}
|
|
18987
|
+
async runGitWithInput(args2, input) {
|
|
18988
|
+
const { stdout } = await this.runGitProcess(args2, input);
|
|
18989
|
+
return stdout;
|
|
18990
|
+
}
|
|
18991
|
+
async runGitWithBuffer(args2, input) {
|
|
18992
|
+
await this.runGitProcess(args2, input);
|
|
18993
|
+
}
|
|
18994
|
+
async runGitProcessAllowingFailure(args2) {
|
|
18995
|
+
return new Promise((resolve7, reject) => {
|
|
18996
|
+
const child = (0, import_node_child_process4.spawn)("git", args2, {
|
|
18997
|
+
cwd: this.repositoryPath,
|
|
18998
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
18999
|
+
});
|
|
19000
|
+
let stderr = "";
|
|
19001
|
+
child.stderr.on("data", (chunk) => {
|
|
19002
|
+
stderr += chunk.toString();
|
|
19003
|
+
});
|
|
19004
|
+
child.on("error", reject);
|
|
19005
|
+
child.on("close", (code) => {
|
|
19006
|
+
if (code === null) {
|
|
19007
|
+
reject(new Error(`git ${args2.join(" ")} exited unexpectedly`));
|
|
19008
|
+
return;
|
|
19009
|
+
}
|
|
19010
|
+
if (code > 1) {
|
|
19011
|
+
reject(new Error(stderr || `git ${args2.join(" ")} failed with code ${code}`));
|
|
19012
|
+
return;
|
|
19013
|
+
}
|
|
19014
|
+
resolve7(code);
|
|
19015
|
+
});
|
|
19016
|
+
});
|
|
19017
|
+
}
|
|
19018
|
+
runGitProcess(args2, input) {
|
|
19019
|
+
return new Promise((resolve7, reject) => {
|
|
19020
|
+
const child = (0, import_node_child_process4.spawn)("git", args2, {
|
|
19021
|
+
cwd: this.repositoryPath,
|
|
19022
|
+
stdio: "pipe"
|
|
19023
|
+
});
|
|
19024
|
+
let stdout = "";
|
|
19025
|
+
let stderr = "";
|
|
19026
|
+
child.stdout.on("data", (chunk) => {
|
|
19027
|
+
stdout += chunk.toString();
|
|
19028
|
+
});
|
|
19029
|
+
child.stderr.on("data", (chunk) => {
|
|
19030
|
+
stderr += chunk.toString();
|
|
19031
|
+
});
|
|
19032
|
+
child.on("error", reject);
|
|
19033
|
+
child.on("close", (code) => {
|
|
19034
|
+
if (code === 0) {
|
|
19035
|
+
resolve7({ stdout, stderr });
|
|
19036
|
+
return;
|
|
19037
|
+
}
|
|
19038
|
+
reject(new Error(stderr || `git ${args2.join(" ")} failed with code ${code}`));
|
|
19039
|
+
});
|
|
19040
|
+
child.stdin.end(input);
|
|
19041
|
+
});
|
|
19042
|
+
}
|
|
19043
|
+
};
|
|
19044
|
+
async function getCurrentBranchName(git) {
|
|
19045
|
+
try {
|
|
19046
|
+
const raw = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
19047
|
+
const branch = raw.trim();
|
|
19048
|
+
return branch === "HEAD" ? null : branch;
|
|
19049
|
+
} catch {
|
|
19050
|
+
return null;
|
|
19051
|
+
}
|
|
19052
|
+
}
|
|
19053
|
+
async function getTrackingMetadata(git, branch) {
|
|
19054
|
+
if (!branch) {
|
|
19055
|
+
return {
|
|
19056
|
+
upstreamRemote: null,
|
|
19057
|
+
upstreamMergeRef: null,
|
|
19058
|
+
remoteUrl: null
|
|
19059
|
+
};
|
|
19060
|
+
}
|
|
19061
|
+
const upstreamRemote = await getGitConfigValue(git, `branch.${branch}.remote`);
|
|
19062
|
+
const upstreamMergeRef = await getGitConfigValue(git, `branch.${branch}.merge`);
|
|
19063
|
+
const remoteUrl = upstreamRemote ? await getRemoteUrl(git, upstreamRemote) : null;
|
|
19064
|
+
return { upstreamRemote, upstreamMergeRef, remoteUrl };
|
|
19065
|
+
}
|
|
19066
|
+
async function getGitConfigValue(git, key) {
|
|
19067
|
+
try {
|
|
19068
|
+
const value = await git.raw(["config", "--get", key]);
|
|
19069
|
+
return value.trim() || null;
|
|
19070
|
+
} catch {
|
|
19071
|
+
return null;
|
|
19072
|
+
}
|
|
19073
|
+
}
|
|
19074
|
+
async function getRemoteUrl(git, remote) {
|
|
19075
|
+
try {
|
|
19076
|
+
const value = await git.remote(["get-url", remote]);
|
|
19077
|
+
return typeof value === "string" ? value.trim() || null : null;
|
|
19078
|
+
} catch {
|
|
19079
|
+
return null;
|
|
19080
|
+
}
|
|
19081
|
+
}
|
|
19082
|
+
function hasTrackingConfig(localGitState) {
|
|
19083
|
+
return !!(localGitState?.upstreamRemote || localGitState?.upstreamMergeRef);
|
|
19084
|
+
}
|
|
19085
|
+
|
|
19086
|
+
// src/handoff-checkpoint.ts
|
|
19087
|
+
var HandoffCheckpointTracker = class {
|
|
19088
|
+
repositoryPath;
|
|
19089
|
+
taskId;
|
|
19090
|
+
runId;
|
|
19091
|
+
apiClient;
|
|
19092
|
+
logger;
|
|
19093
|
+
constructor(config) {
|
|
19094
|
+
this.repositoryPath = config.repositoryPath;
|
|
19095
|
+
this.taskId = config.taskId;
|
|
19096
|
+
this.runId = config.runId;
|
|
19097
|
+
this.apiClient = config.apiClient;
|
|
19098
|
+
this.logger = config.logger || new Logger({ debug: false, prefix: "[HandoffCheckpointTracker]" });
|
|
19099
|
+
}
|
|
19100
|
+
async captureForHandoff(localGitState) {
|
|
19101
|
+
if (!this.apiClient) {
|
|
19102
|
+
throw new Error(
|
|
19103
|
+
"Cannot capture handoff checkpoint: API client not configured"
|
|
19104
|
+
);
|
|
19105
|
+
}
|
|
19106
|
+
const gitTracker = this.createGitTracker();
|
|
19107
|
+
const capture = await gitTracker.captureForHandoff(localGitState);
|
|
19108
|
+
try {
|
|
19109
|
+
const uploads = await this.uploadArtifacts([
|
|
19110
|
+
{
|
|
19111
|
+
key: "pack",
|
|
19112
|
+
filePath: capture.headPack?.path,
|
|
19113
|
+
name: `handoff/${capture.checkpoint.checkpointId}.pack`,
|
|
19114
|
+
contentType: "application/x-git-packed-objects"
|
|
19115
|
+
},
|
|
19116
|
+
{
|
|
19117
|
+
key: "index",
|
|
19118
|
+
filePath: capture.indexFile.path,
|
|
19119
|
+
name: `handoff/${capture.checkpoint.checkpointId}.index`,
|
|
19120
|
+
contentType: "application/octet-stream"
|
|
19121
|
+
}
|
|
19122
|
+
]);
|
|
19123
|
+
this.logCaptureMetrics(capture.checkpoint, uploads);
|
|
19124
|
+
return {
|
|
19125
|
+
...capture.checkpoint,
|
|
19126
|
+
artifactPath: uploads.pack?.storagePath,
|
|
19127
|
+
indexArtifactPath: uploads.index?.storagePath
|
|
19128
|
+
};
|
|
19129
|
+
} finally {
|
|
19130
|
+
await this.removeIfPresent(capture.headPack?.path);
|
|
19131
|
+
await this.removeIfPresent(capture.indexFile.path);
|
|
19132
|
+
}
|
|
19133
|
+
}
|
|
19134
|
+
async applyFromHandoff(checkpoint, options) {
|
|
19135
|
+
if (!this.apiClient) {
|
|
19136
|
+
throw new Error(
|
|
19137
|
+
"Cannot apply handoff checkpoint: API client not configured"
|
|
19138
|
+
);
|
|
19139
|
+
}
|
|
19140
|
+
const gitTracker = this.createGitTracker();
|
|
19141
|
+
const tmpDir = (0, import_node_path6.join)(this.repositoryPath, ".posthog", "tmp");
|
|
19142
|
+
await (0, import_promises3.mkdir)(tmpDir, { recursive: true });
|
|
19143
|
+
const packPath = (0, import_node_path6.join)(tmpDir, `${checkpoint.checkpointId}.pack`);
|
|
19144
|
+
const indexPath = (0, import_node_path6.join)(tmpDir, `${checkpoint.checkpointId}.index`);
|
|
19145
|
+
try {
|
|
19146
|
+
const downloads = await this.downloadArtifacts([
|
|
19147
|
+
{
|
|
19148
|
+
key: "pack",
|
|
19149
|
+
storagePath: checkpoint.artifactPath,
|
|
19150
|
+
filePath: packPath,
|
|
19151
|
+
label: "handoff pack"
|
|
19152
|
+
},
|
|
19153
|
+
{
|
|
19154
|
+
key: "index",
|
|
19155
|
+
storagePath: checkpoint.indexArtifactPath,
|
|
19156
|
+
filePath: indexPath,
|
|
19157
|
+
label: "handoff index"
|
|
19158
|
+
}
|
|
19159
|
+
]);
|
|
19160
|
+
const applyResult = await gitTracker.applyFromHandoff({
|
|
19161
|
+
checkpoint: this.toGitCheckpoint(checkpoint),
|
|
19162
|
+
headPackPath: downloads.pack?.filePath,
|
|
19163
|
+
indexPath: downloads.index?.filePath,
|
|
19164
|
+
localGitState: options?.localGitState,
|
|
19165
|
+
onDivergedBranch: options?.onDivergedBranch
|
|
19166
|
+
});
|
|
19167
|
+
this.logApplyMetrics(checkpoint, downloads, applyResult.totalBytes);
|
|
19168
|
+
} finally {
|
|
19169
|
+
await this.removeIfPresent(packPath);
|
|
19170
|
+
await this.removeIfPresent(indexPath);
|
|
19171
|
+
}
|
|
19172
|
+
}
|
|
19173
|
+
toGitCheckpoint(checkpoint) {
|
|
19174
|
+
return {
|
|
19175
|
+
checkpointId: checkpoint.checkpointId,
|
|
19176
|
+
commit: checkpoint.commit,
|
|
19177
|
+
checkpointRef: checkpoint.checkpointRef,
|
|
19178
|
+
headRef: checkpoint.headRef,
|
|
19179
|
+
head: checkpoint.head,
|
|
19180
|
+
branch: checkpoint.branch,
|
|
19181
|
+
indexTree: checkpoint.indexTree,
|
|
19182
|
+
worktreeTree: checkpoint.worktreeTree,
|
|
19183
|
+
timestamp: checkpoint.timestamp,
|
|
19184
|
+
upstreamRemote: checkpoint.upstreamRemote ?? null,
|
|
19185
|
+
upstreamMergeRef: checkpoint.upstreamMergeRef ?? null,
|
|
19186
|
+
remoteUrl: checkpoint.remoteUrl ?? null
|
|
19187
|
+
};
|
|
19188
|
+
}
|
|
19189
|
+
async uploadArtifactFile(filePath, name2, contentType) {
|
|
19190
|
+
if (!this.apiClient) {
|
|
19191
|
+
return { rawBytes: 0, wireBytes: 0 };
|
|
19192
|
+
}
|
|
19193
|
+
const content = await (0, import_promises3.readFile)(filePath);
|
|
19194
|
+
const base64Content = content.toString("base64");
|
|
19195
|
+
const artifacts = await this.apiClient.uploadTaskArtifacts(
|
|
19196
|
+
this.taskId,
|
|
19197
|
+
this.runId,
|
|
19198
|
+
[
|
|
19199
|
+
{
|
|
19200
|
+
name: name2,
|
|
19201
|
+
type: "artifact",
|
|
19202
|
+
content: base64Content,
|
|
19203
|
+
content_type: contentType
|
|
19204
|
+
}
|
|
19205
|
+
]
|
|
19206
|
+
);
|
|
19207
|
+
return {
|
|
19208
|
+
storagePath: artifacts.at(-1)?.storage_path,
|
|
19209
|
+
rawBytes: content.byteLength,
|
|
19210
|
+
wireBytes: Buffer.byteLength(base64Content, "utf-8")
|
|
19211
|
+
};
|
|
19212
|
+
}
|
|
19213
|
+
async uploadArtifacts(specs) {
|
|
19214
|
+
const uploads = await Promise.all(
|
|
19215
|
+
specs.map(async (spec) => {
|
|
19216
|
+
if (!spec.filePath) {
|
|
19217
|
+
return [spec.key, void 0];
|
|
19218
|
+
}
|
|
19219
|
+
return [
|
|
19220
|
+
spec.key,
|
|
19221
|
+
await this.uploadArtifactFile(
|
|
19222
|
+
spec.filePath,
|
|
19223
|
+
spec.name,
|
|
19224
|
+
spec.contentType
|
|
19225
|
+
)
|
|
19226
|
+
];
|
|
19227
|
+
})
|
|
19228
|
+
);
|
|
19229
|
+
return Object.fromEntries(uploads);
|
|
19230
|
+
}
|
|
19231
|
+
async downloadArtifactToFile(artifactPath, filePath, label) {
|
|
19232
|
+
if (!this.apiClient) {
|
|
19233
|
+
throw new Error(`Cannot download ${label}: API client not configured`);
|
|
19234
|
+
}
|
|
19235
|
+
const arrayBuffer = await this.apiClient.downloadArtifact(
|
|
19236
|
+
this.taskId,
|
|
19237
|
+
this.runId,
|
|
19238
|
+
artifactPath
|
|
19239
|
+
);
|
|
19240
|
+
if (!arrayBuffer) {
|
|
19241
|
+
throw new Error(`Failed to download ${label}`);
|
|
19242
|
+
}
|
|
19243
|
+
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
19244
|
+
const binaryContent = Buffer.from(base64Content, "base64");
|
|
19245
|
+
await (0, import_promises3.writeFile)(filePath, binaryContent);
|
|
19246
|
+
return {
|
|
19247
|
+
filePath,
|
|
19248
|
+
rawBytes: binaryContent.byteLength,
|
|
19249
|
+
wireBytes: arrayBuffer.byteLength
|
|
19250
|
+
};
|
|
19251
|
+
}
|
|
19252
|
+
async downloadArtifacts(specs) {
|
|
19253
|
+
const downloads = await Promise.all(
|
|
19254
|
+
specs.map(async (spec) => {
|
|
19255
|
+
if (!spec.storagePath) {
|
|
19256
|
+
return [spec.key, void 0];
|
|
19257
|
+
}
|
|
19258
|
+
return [
|
|
19259
|
+
spec.key,
|
|
19260
|
+
await this.downloadArtifactToFile(
|
|
19261
|
+
spec.storagePath,
|
|
19262
|
+
spec.filePath,
|
|
19263
|
+
spec.label
|
|
19264
|
+
)
|
|
19265
|
+
];
|
|
19266
|
+
})
|
|
19267
|
+
);
|
|
19268
|
+
return Object.fromEntries(downloads);
|
|
19269
|
+
}
|
|
19270
|
+
createGitTracker() {
|
|
19271
|
+
return new GitHandoffTracker({
|
|
19272
|
+
repositoryPath: this.repositoryPath,
|
|
19273
|
+
logger: this.logger
|
|
19274
|
+
});
|
|
19275
|
+
}
|
|
19276
|
+
logCaptureMetrics(checkpoint, uploads) {
|
|
19277
|
+
this.logger.info("Captured handoff checkpoint", {
|
|
19278
|
+
checkpointId: checkpoint.checkpointId,
|
|
19279
|
+
branch: checkpoint.branch,
|
|
19280
|
+
head: checkpoint.head,
|
|
19281
|
+
artifactPath: uploads.pack?.storagePath,
|
|
19282
|
+
indexArtifactPath: uploads.index?.storagePath,
|
|
19283
|
+
...this.buildMetricPayload(uploads)
|
|
19284
|
+
});
|
|
19285
|
+
}
|
|
19286
|
+
logApplyMetrics(checkpoint, downloads, totalBytes) {
|
|
19287
|
+
this.logger.info("Applied handoff checkpoint", {
|
|
19288
|
+
checkpointId: checkpoint.checkpointId,
|
|
19289
|
+
commit: checkpoint.commit,
|
|
19290
|
+
branch: checkpoint.branch,
|
|
19291
|
+
head: checkpoint.head,
|
|
19292
|
+
packBytes: downloads.pack?.rawBytes ?? 0,
|
|
19293
|
+
packWireBytes: downloads.pack?.wireBytes ?? 0,
|
|
19294
|
+
indexBytes: downloads.index?.rawBytes ?? 0,
|
|
19295
|
+
indexWireBytes: downloads.index?.wireBytes ?? 0,
|
|
19296
|
+
totalBytes,
|
|
19297
|
+
totalWireBytes: this.sumWireBytes(downloads.pack, downloads.index)
|
|
19298
|
+
});
|
|
19299
|
+
}
|
|
19300
|
+
buildMetricPayload(metrics) {
|
|
19301
|
+
return {
|
|
19302
|
+
packBytes: metrics.pack?.rawBytes ?? 0,
|
|
19303
|
+
packWireBytes: metrics.pack?.wireBytes ?? 0,
|
|
19304
|
+
indexBytes: metrics.index?.rawBytes ?? 0,
|
|
19305
|
+
indexWireBytes: metrics.index?.wireBytes ?? 0,
|
|
19306
|
+
totalBytes: this.sumRawBytes(metrics.pack, metrics.index),
|
|
19307
|
+
totalWireBytes: this.sumWireBytes(metrics.pack, metrics.index)
|
|
19308
|
+
};
|
|
19309
|
+
}
|
|
19310
|
+
sumRawBytes(...artifacts) {
|
|
19311
|
+
return artifacts.reduce(
|
|
19312
|
+
(total, artifact) => total + (artifact?.rawBytes ?? 0),
|
|
19313
|
+
0
|
|
19314
|
+
);
|
|
19315
|
+
}
|
|
19316
|
+
sumWireBytes(...artifacts) {
|
|
19317
|
+
return artifacts.reduce(
|
|
19318
|
+
(total, artifact) => total + (artifact?.wireBytes ?? 0),
|
|
19319
|
+
0
|
|
19320
|
+
);
|
|
19321
|
+
}
|
|
19322
|
+
async removeIfPresent(filePath) {
|
|
19323
|
+
if (!filePath) {
|
|
19324
|
+
return;
|
|
19325
|
+
}
|
|
19326
|
+
await (0, import_promises3.rm)(filePath, { force: true }).catch(() => {
|
|
19327
|
+
});
|
|
19328
|
+
}
|
|
19329
|
+
};
|
|
19330
|
+
|
|
19331
|
+
// src/utils/gateway.ts
|
|
19332
|
+
function getGatewayBaseUrl(posthogHost) {
|
|
19333
|
+
const url = new URL(posthogHost);
|
|
19334
|
+
const hostname = url.hostname;
|
|
19335
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
19336
|
+
return `${url.protocol}//localhost:3308`;
|
|
19337
|
+
}
|
|
19338
|
+
if (hostname === "host.docker.internal") {
|
|
19339
|
+
return `${url.protocol}//host.docker.internal:3308`;
|
|
19340
|
+
}
|
|
19341
|
+
const region = hostname.match(/^(us|eu)\.posthog\.com$/)?.[1] ?? "us";
|
|
19342
|
+
return `https://gateway.${region}.posthog.com`;
|
|
19343
|
+
}
|
|
19344
|
+
function getLlmGatewayUrl(posthogHost, product = "posthog_code") {
|
|
19345
|
+
return `${getGatewayBaseUrl(posthogHost)}/${product}`;
|
|
19346
|
+
}
|
|
19347
|
+
|
|
19348
|
+
// src/posthog-api.ts
|
|
19349
|
+
var DEFAULT_USER_AGENT = `posthog/agent.hog.dev; version: ${package_default.version}`;
|
|
19350
|
+
var PostHogAPIClient = class {
|
|
19351
|
+
config;
|
|
19352
|
+
constructor(config) {
|
|
19353
|
+
this.config = config;
|
|
19354
|
+
}
|
|
19355
|
+
get baseUrl() {
|
|
19356
|
+
const host = this.config.apiUrl.endsWith("/") ? this.config.apiUrl.slice(0, -1) : this.config.apiUrl;
|
|
19357
|
+
return host;
|
|
19358
|
+
}
|
|
19359
|
+
isAuthFailure(status) {
|
|
19360
|
+
return status === 401 || status === 403;
|
|
19361
|
+
}
|
|
19362
|
+
async resolveApiKey(forceRefresh = false) {
|
|
19363
|
+
if (forceRefresh && this.config.refreshApiKey) {
|
|
19364
|
+
return this.config.refreshApiKey();
|
|
19365
|
+
}
|
|
19366
|
+
return this.config.getApiKey();
|
|
19367
|
+
}
|
|
19368
|
+
async buildHeaders(options, forceRefresh = false) {
|
|
19369
|
+
const headers = new Headers(options.headers);
|
|
19370
|
+
headers.set(
|
|
19371
|
+
"Authorization",
|
|
19372
|
+
`Bearer ${await this.resolveApiKey(forceRefresh)}`
|
|
19373
|
+
);
|
|
19374
|
+
headers.set("Content-Type", "application/json");
|
|
19375
|
+
headers.set("User-Agent", this.config.userAgent ?? DEFAULT_USER_AGENT);
|
|
19376
|
+
return headers;
|
|
19377
|
+
}
|
|
19378
|
+
async performRequest(endpoint, options, forceRefresh = false) {
|
|
19379
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
19380
|
+
return fetch(url, {
|
|
19381
|
+
...options,
|
|
19382
|
+
headers: await this.buildHeaders(options, forceRefresh)
|
|
19383
|
+
});
|
|
19384
|
+
}
|
|
19385
|
+
async performRequestWithRetry(endpoint, options = {}) {
|
|
19386
|
+
let response = await this.performRequest(endpoint, options);
|
|
19387
|
+
if (!response.ok && this.isAuthFailure(response.status)) {
|
|
19388
|
+
response = await this.performRequest(endpoint, options, true);
|
|
19389
|
+
}
|
|
19390
|
+
return response;
|
|
19391
|
+
}
|
|
19392
|
+
async apiRequest(endpoint, options = {}) {
|
|
19393
|
+
const response = await this.performRequestWithRetry(endpoint, options);
|
|
19394
|
+
if (!response.ok) {
|
|
19395
|
+
let errorMessage;
|
|
19396
|
+
try {
|
|
19397
|
+
const errorResponse = await response.json();
|
|
19398
|
+
errorMessage = `Failed request: [${response.status}] ${JSON.stringify(errorResponse)}`;
|
|
19399
|
+
} catch {
|
|
19400
|
+
errorMessage = `Failed request: [${response.status}] ${response.statusText}`;
|
|
19401
|
+
}
|
|
19402
|
+
throw new Error(errorMessage);
|
|
19403
|
+
}
|
|
19404
|
+
return response.json();
|
|
19405
|
+
}
|
|
19406
|
+
getTeamId() {
|
|
19407
|
+
return this.config.projectId;
|
|
19408
|
+
}
|
|
19409
|
+
async getApiKey(forceRefresh = false) {
|
|
19410
|
+
return this.resolveApiKey(forceRefresh);
|
|
19411
|
+
}
|
|
19412
|
+
getLlmGatewayUrl() {
|
|
19413
|
+
return getLlmGatewayUrl(this.baseUrl);
|
|
19414
|
+
}
|
|
19415
|
+
async getTask(taskId) {
|
|
19416
|
+
const teamId = this.getTeamId();
|
|
19417
|
+
return this.apiRequest(`/api/projects/${teamId}/tasks/${taskId}/`);
|
|
19418
|
+
}
|
|
19419
|
+
async getTaskRun(taskId, runId) {
|
|
19420
|
+
const teamId = this.getTeamId();
|
|
19421
|
+
return this.apiRequest(
|
|
19422
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`
|
|
19423
|
+
);
|
|
19424
|
+
}
|
|
19425
|
+
async updateTaskRun(taskId, runId, payload) {
|
|
19426
|
+
const teamId = this.getTeamId();
|
|
19427
|
+
return this.apiRequest(
|
|
19428
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/`,
|
|
19429
|
+
{
|
|
19430
|
+
method: "PATCH",
|
|
19431
|
+
body: JSON.stringify(payload)
|
|
19432
|
+
}
|
|
19433
|
+
);
|
|
19434
|
+
}
|
|
19435
|
+
async setTaskRunOutput(taskId, runId, output) {
|
|
19436
|
+
return this.apiRequest(
|
|
19437
|
+
`/api/projects/${this.getTeamId()}/tasks/${taskId}/runs/${runId}/set_output/`,
|
|
19438
|
+
{
|
|
19439
|
+
method: "PATCH",
|
|
19440
|
+
body: JSON.stringify(output)
|
|
19441
|
+
}
|
|
19442
|
+
);
|
|
19443
|
+
}
|
|
19444
|
+
async appendTaskRunLog(taskId, runId, entries) {
|
|
19445
|
+
const teamId = this.getTeamId();
|
|
19446
|
+
return this.apiRequest(
|
|
19447
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/append_log/`,
|
|
19448
|
+
{
|
|
19449
|
+
method: "POST",
|
|
19450
|
+
body: JSON.stringify({ entries })
|
|
19451
|
+
}
|
|
19452
|
+
);
|
|
19453
|
+
}
|
|
19454
|
+
async relayMessage(taskId, runId, text2) {
|
|
19455
|
+
const teamId = this.getTeamId();
|
|
19456
|
+
await this.apiRequest(
|
|
19457
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/relay_message/`,
|
|
19458
|
+
{
|
|
19459
|
+
method: "POST",
|
|
19460
|
+
body: JSON.stringify({ text: text2 })
|
|
19461
|
+
}
|
|
19462
|
+
);
|
|
19463
|
+
}
|
|
19464
|
+
async uploadTaskArtifacts(taskId, runId, artifacts) {
|
|
19465
|
+
if (!artifacts.length) {
|
|
19466
|
+
return [];
|
|
19467
|
+
}
|
|
19468
|
+
const teamId = this.getTeamId();
|
|
19469
|
+
const response = await this.apiRequest(
|
|
19470
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/`,
|
|
19471
|
+
{
|
|
19472
|
+
method: "POST",
|
|
19473
|
+
body: JSON.stringify({ artifacts })
|
|
19474
|
+
}
|
|
19475
|
+
);
|
|
19476
|
+
const manifest = response.artifacts ?? [];
|
|
19477
|
+
return manifest.slice(-artifacts.length);
|
|
19478
|
+
}
|
|
19479
|
+
/**
|
|
19480
|
+
* Download artifact content by storage path
|
|
19481
|
+
* Streams the file through the PostHog backend so the sandbox does not need
|
|
19482
|
+
* direct access to object storage.
|
|
19483
|
+
*/
|
|
19484
|
+
async downloadArtifact(taskId, runId, storagePath) {
|
|
19485
|
+
const teamId = this.getTeamId();
|
|
19486
|
+
try {
|
|
19487
|
+
const response = await this.performRequestWithRetry(
|
|
19488
|
+
`/api/projects/${teamId}/tasks/${taskId}/runs/${runId}/artifacts/download/`,
|
|
19489
|
+
{
|
|
19490
|
+
method: "POST",
|
|
19491
|
+
body: JSON.stringify({ storage_path: storagePath })
|
|
19492
|
+
}
|
|
19493
|
+
);
|
|
19494
|
+
if (!response.ok) {
|
|
19495
|
+
throw new Error(`Failed to download artifact: ${response.status}`);
|
|
19496
|
+
}
|
|
19497
|
+
return response.arrayBuffer();
|
|
19498
|
+
} catch {
|
|
19499
|
+
return null;
|
|
19500
|
+
}
|
|
19501
|
+
}
|
|
19502
|
+
/**
|
|
19503
|
+
* Fetch logs for a task run via the logs API endpoint
|
|
19504
|
+
* @param taskRun - The task run to fetch logs for
|
|
19505
|
+
* @returns Array of stored entries, or empty array if no logs available
|
|
19506
|
+
*/
|
|
19507
|
+
async fetchTaskRunLogs(taskRun) {
|
|
19508
|
+
const teamId = this.getTeamId();
|
|
19509
|
+
const endpoint = `/api/projects/${teamId}/tasks/${taskRun.task}/runs/${taskRun.id}/logs`;
|
|
19510
|
+
try {
|
|
19511
|
+
const response = await this.performRequestWithRetry(endpoint);
|
|
19512
|
+
if (!response.ok) {
|
|
19513
|
+
if (response.status === 404) {
|
|
19514
|
+
return [];
|
|
19515
|
+
}
|
|
19516
|
+
throw new Error(
|
|
19517
|
+
`Failed to fetch logs: ${response.status} ${response.statusText}`
|
|
19518
|
+
);
|
|
19519
|
+
}
|
|
19520
|
+
const content = await response.text();
|
|
19521
|
+
if (!content.trim()) {
|
|
19522
|
+
return [];
|
|
19523
|
+
}
|
|
19524
|
+
return content.trim().split("\n").map((line) => JSON.parse(line));
|
|
19525
|
+
} catch (error) {
|
|
19526
|
+
throw new Error(
|
|
19527
|
+
`Failed to fetch task run logs: ${error instanceof Error ? error.message : String(error)}`
|
|
19528
|
+
);
|
|
19529
|
+
}
|
|
19530
|
+
}
|
|
19531
|
+
};
|
|
19532
|
+
|
|
19533
|
+
// src/adapters/claude/session/jsonl-hydration.ts
|
|
19534
|
+
var import_node_crypto3 = require("crypto");
|
|
19535
|
+
var fs11 = __toESM(require("fs/promises"), 1);
|
|
19536
|
+
var os6 = __toESM(require("os"), 1);
|
|
19537
|
+
var path14 = __toESM(require("path"), 1);
|
|
19538
|
+
var CHARS_PER_TOKEN = 4;
|
|
19539
|
+
var DEFAULT_MAX_TOKENS = 15e4;
|
|
19540
|
+
function estimateTurnTokens(turn) {
|
|
19541
|
+
let chars = 0;
|
|
19542
|
+
for (const block of turn.content) {
|
|
19543
|
+
if ("text" in block && typeof block.text === "string") {
|
|
19544
|
+
chars += block.text.length;
|
|
19545
|
+
}
|
|
19546
|
+
}
|
|
19547
|
+
if (turn.toolCalls) {
|
|
19548
|
+
for (const tc of turn.toolCalls) {
|
|
19549
|
+
chars += JSON.stringify(tc.input ?? "").length;
|
|
19550
|
+
if (tc.result !== void 0) {
|
|
19551
|
+
chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
|
|
19552
|
+
}
|
|
19553
|
+
}
|
|
19554
|
+
}
|
|
19555
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
19556
|
+
}
|
|
19557
|
+
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
19558
|
+
let budget = maxTokens;
|
|
19559
|
+
let startIndex = turns.length;
|
|
19560
|
+
for (let i2 = turns.length - 1; i2 >= 0; i2--) {
|
|
19561
|
+
const cost = estimateTurnTokens(turns[i2]);
|
|
19562
|
+
if (cost > budget) break;
|
|
19563
|
+
budget -= cost;
|
|
19564
|
+
startIndex = i2;
|
|
19565
|
+
}
|
|
19566
|
+
while (startIndex < turns.length && turns[startIndex].role !== "user") {
|
|
19567
|
+
startIndex++;
|
|
19568
|
+
}
|
|
19569
|
+
return turns.slice(startIndex);
|
|
19570
|
+
}
|
|
19571
|
+
|
|
19572
|
+
// src/sagas/apply-snapshot-saga.ts
|
|
19573
|
+
var import_promises4 = require("fs/promises");
|
|
19574
|
+
var import_node_path7 = require("path");
|
|
19575
|
+
|
|
19576
|
+
// ../git/dist/sagas/tree.js
|
|
19577
|
+
var import_node_fs3 = require("fs");
|
|
19578
|
+
var fs12 = __toESM(require("fs/promises"), 1);
|
|
19579
|
+
var path15 = __toESM(require("path"), 1);
|
|
19580
|
+
var tar = __toESM(require("tar"), 1);
|
|
19581
|
+
var CaptureTreeSaga = class extends GitSaga {
|
|
18738
19582
|
sagaName = "CaptureTreeSaga";
|
|
18739
19583
|
tempIndexPath = null;
|
|
18740
19584
|
async executeGitOperations(input) {
|
|
18741
19585
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
18742
|
-
const tmpDir =
|
|
19586
|
+
const tmpDir = path15.join(baseDir, ".git", "posthog-code-tmp");
|
|
18743
19587
|
await this.step({
|
|
18744
19588
|
name: "create_tmp_dir",
|
|
18745
|
-
execute: () =>
|
|
19589
|
+
execute: () => fs12.mkdir(tmpDir, { recursive: true }),
|
|
18746
19590
|
rollback: async () => {
|
|
18747
19591
|
}
|
|
18748
19592
|
});
|
|
18749
|
-
this.tempIndexPath =
|
|
19593
|
+
this.tempIndexPath = path15.join(tmpDir, `index-${Date.now()}`);
|
|
18750
19594
|
const tempIndexGit = this.git.env({
|
|
18751
19595
|
...process.env,
|
|
18752
19596
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -18756,7 +19600,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18756
19600
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
18757
19601
|
rollback: async () => {
|
|
18758
19602
|
if (this.tempIndexPath) {
|
|
18759
|
-
await
|
|
19603
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18760
19604
|
});
|
|
18761
19605
|
}
|
|
18762
19606
|
}
|
|
@@ -18765,7 +19609,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18765
19609
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
18766
19610
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
18767
19611
|
this.log.debug("No changes since last capture", { treeHash });
|
|
18768
|
-
await
|
|
19612
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18769
19613
|
});
|
|
18770
19614
|
return { snapshot: null, changed: false };
|
|
18771
19615
|
}
|
|
@@ -18777,7 +19621,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18777
19621
|
}
|
|
18778
19622
|
});
|
|
18779
19623
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
18780
|
-
await
|
|
19624
|
+
await fs12.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
18781
19625
|
});
|
|
18782
19626
|
const snapshot = {
|
|
18783
19627
|
treeHash,
|
|
@@ -18801,15 +19645,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18801
19645
|
if (filesToArchive.length === 0) {
|
|
18802
19646
|
return void 0;
|
|
18803
19647
|
}
|
|
18804
|
-
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(
|
|
19648
|
+
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(path15.join(baseDir, f)));
|
|
18805
19649
|
if (existingFiles.length === 0) {
|
|
18806
19650
|
return void 0;
|
|
18807
19651
|
}
|
|
18808
19652
|
await this.step({
|
|
18809
19653
|
name: "create_archive",
|
|
18810
19654
|
execute: async () => {
|
|
18811
|
-
const archiveDir =
|
|
18812
|
-
await
|
|
19655
|
+
const archiveDir = path15.dirname(archivePath);
|
|
19656
|
+
await fs12.mkdir(archiveDir, { recursive: true });
|
|
18813
19657
|
await tar.create({
|
|
18814
19658
|
gzip: true,
|
|
18815
19659
|
file: archivePath,
|
|
@@ -18817,7 +19661,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
18817
19661
|
}, existingFiles);
|
|
18818
19662
|
},
|
|
18819
19663
|
rollback: async () => {
|
|
18820
|
-
await
|
|
19664
|
+
await fs12.rm(archivePath, { force: true }).catch(() => {
|
|
18821
19665
|
});
|
|
18822
19666
|
}
|
|
18823
19667
|
});
|
|
@@ -18917,9 +19761,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18917
19761
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
18918
19762
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
18919
19763
|
for (const filePath of filesToExtract) {
|
|
18920
|
-
const fullPath =
|
|
19764
|
+
const fullPath = path15.join(baseDir, filePath);
|
|
18921
19765
|
try {
|
|
18922
|
-
const content = await
|
|
19766
|
+
const content = await fs12.readFile(fullPath);
|
|
18923
19767
|
this.fileBackups.set(filePath, content);
|
|
18924
19768
|
} catch {
|
|
18925
19769
|
}
|
|
@@ -18936,16 +19780,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18936
19780
|
},
|
|
18937
19781
|
rollback: async () => {
|
|
18938
19782
|
for (const filePath of this.extractedFiles) {
|
|
18939
|
-
const fullPath =
|
|
19783
|
+
const fullPath = path15.join(baseDir, filePath);
|
|
18940
19784
|
const backup = this.fileBackups.get(filePath);
|
|
18941
19785
|
if (backup) {
|
|
18942
|
-
const dir =
|
|
18943
|
-
await
|
|
19786
|
+
const dir = path15.dirname(fullPath);
|
|
19787
|
+
await fs12.mkdir(dir, { recursive: true }).catch(() => {
|
|
18944
19788
|
});
|
|
18945
|
-
await
|
|
19789
|
+
await fs12.writeFile(fullPath, backup).catch(() => {
|
|
18946
19790
|
});
|
|
18947
19791
|
} else {
|
|
18948
|
-
await
|
|
19792
|
+
await fs12.rm(fullPath, { force: true }).catch(() => {
|
|
18949
19793
|
});
|
|
18950
19794
|
}
|
|
18951
19795
|
}
|
|
@@ -18953,10 +19797,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18953
19797
|
});
|
|
18954
19798
|
}
|
|
18955
19799
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
18956
|
-
const fullPath =
|
|
19800
|
+
const fullPath = path15.join(baseDir, change.path);
|
|
18957
19801
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
18958
19802
|
try {
|
|
18959
|
-
return await
|
|
19803
|
+
return await fs12.readFile(fullPath);
|
|
18960
19804
|
} catch {
|
|
18961
19805
|
return null;
|
|
18962
19806
|
}
|
|
@@ -18964,15 +19808,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
18964
19808
|
await this.step({
|
|
18965
19809
|
name: `delete_${change.path}`,
|
|
18966
19810
|
execute: async () => {
|
|
18967
|
-
await
|
|
19811
|
+
await fs12.rm(fullPath, { force: true });
|
|
18968
19812
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
18969
19813
|
},
|
|
18970
19814
|
rollback: async () => {
|
|
18971
19815
|
if (backupContent) {
|
|
18972
|
-
const dir =
|
|
18973
|
-
await
|
|
19816
|
+
const dir = path15.dirname(fullPath);
|
|
19817
|
+
await fs12.mkdir(dir, { recursive: true }).catch(() => {
|
|
18974
19818
|
});
|
|
18975
|
-
await
|
|
19819
|
+
await fs12.writeFile(fullPath, backupContent).catch(() => {
|
|
18976
19820
|
});
|
|
18977
19821
|
}
|
|
18978
19822
|
}
|
|
@@ -18995,18 +19839,18 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
18995
19839
|
archivePath = null;
|
|
18996
19840
|
async execute(input) {
|
|
18997
19841
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
18998
|
-
const tmpDir = (0,
|
|
19842
|
+
const tmpDir = (0, import_node_path7.join)(repositoryPath, ".posthog", "tmp");
|
|
18999
19843
|
if (!snapshot.archiveUrl) {
|
|
19000
19844
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
19001
19845
|
}
|
|
19002
19846
|
const archiveUrl = snapshot.archiveUrl;
|
|
19003
19847
|
await this.step({
|
|
19004
19848
|
name: "create_tmp_dir",
|
|
19005
|
-
execute: () => (0,
|
|
19849
|
+
execute: () => (0, import_promises4.mkdir)(tmpDir, { recursive: true }),
|
|
19006
19850
|
rollback: async () => {
|
|
19007
19851
|
}
|
|
19008
19852
|
});
|
|
19009
|
-
const archivePath = (0,
|
|
19853
|
+
const archivePath = (0, import_node_path7.join)(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
19010
19854
|
this.archivePath = archivePath;
|
|
19011
19855
|
await this.step({
|
|
19012
19856
|
name: "download_archive",
|
|
@@ -19021,11 +19865,18 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19021
19865
|
}
|
|
19022
19866
|
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
19023
19867
|
const binaryContent = Buffer.from(base64Content, "base64");
|
|
19024
|
-
await (0,
|
|
19868
|
+
await (0, import_promises4.writeFile)(archivePath, binaryContent);
|
|
19869
|
+
this.log.info("Tree archive downloaded", {
|
|
19870
|
+
treeHash: snapshot.treeHash,
|
|
19871
|
+
snapshotBytes: binaryContent.byteLength,
|
|
19872
|
+
snapshotWireBytes: arrayBuffer.byteLength,
|
|
19873
|
+
totalBytes: binaryContent.byteLength,
|
|
19874
|
+
totalWireBytes: arrayBuffer.byteLength
|
|
19875
|
+
});
|
|
19025
19876
|
},
|
|
19026
19877
|
rollback: async () => {
|
|
19027
19878
|
if (this.archivePath) {
|
|
19028
|
-
await (0,
|
|
19879
|
+
await (0, import_promises4.rm)(this.archivePath, { force: true }).catch(() => {
|
|
19029
19880
|
});
|
|
19030
19881
|
}
|
|
19031
19882
|
}
|
|
@@ -19041,7 +19892,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19041
19892
|
if (!applyResult.success) {
|
|
19042
19893
|
throw new Error(`Failed to apply tree: ${applyResult.error}`);
|
|
19043
19894
|
}
|
|
19044
|
-
await (0,
|
|
19895
|
+
await (0, import_promises4.rm)(this.archivePath, { force: true }).catch(() => {
|
|
19045
19896
|
});
|
|
19046
19897
|
this.log.info("Tree snapshot applied", {
|
|
19047
19898
|
treeHash: snapshot.treeHash,
|
|
@@ -19054,8 +19905,8 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
19054
19905
|
|
|
19055
19906
|
// src/sagas/capture-tree-saga.ts
|
|
19056
19907
|
var import_node_fs4 = require("fs");
|
|
19057
|
-
var
|
|
19058
|
-
var
|
|
19908
|
+
var import_promises5 = require("fs/promises");
|
|
19909
|
+
var import_node_path8 = require("path");
|
|
19059
19910
|
var CaptureTreeSaga2 = class extends Saga {
|
|
19060
19911
|
sagaName = "CaptureTreeSaga";
|
|
19061
19912
|
async execute(input) {
|
|
@@ -19067,14 +19918,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19067
19918
|
taskId,
|
|
19068
19919
|
runId
|
|
19069
19920
|
} = input;
|
|
19070
|
-
const tmpDir = (0,
|
|
19071
|
-
if ((0, import_node_fs4.existsSync)((0,
|
|
19921
|
+
const tmpDir = (0, import_node_path8.join)(repositoryPath, ".posthog", "tmp");
|
|
19922
|
+
if ((0, import_node_fs4.existsSync)((0, import_node_path8.join)(repositoryPath, ".gitmodules"))) {
|
|
19072
19923
|
this.log.warn(
|
|
19073
19924
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
19074
19925
|
);
|
|
19075
19926
|
}
|
|
19076
19927
|
const shouldArchive = !!apiClient;
|
|
19077
|
-
const archivePath = shouldArchive ? (0,
|
|
19928
|
+
const archivePath = shouldArchive ? (0, import_node_path8.join)(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
19078
19929
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
19079
19930
|
const captureResult = await gitCaptureSaga.run({
|
|
19080
19931
|
baseDir: repositoryPath,
|
|
@@ -19104,7 +19955,7 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19104
19955
|
runId
|
|
19105
19956
|
);
|
|
19106
19957
|
} finally {
|
|
19107
|
-
await (0,
|
|
19958
|
+
await (0, import_promises5.rm)(createdArchivePath, { force: true }).catch(() => {
|
|
19108
19959
|
});
|
|
19109
19960
|
}
|
|
19110
19961
|
}
|
|
@@ -19128,8 +19979,10 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19128
19979
|
const archiveUrl = await this.step({
|
|
19129
19980
|
name: "upload_archive",
|
|
19130
19981
|
execute: async () => {
|
|
19131
|
-
const archiveContent = await (0,
|
|
19982
|
+
const archiveContent = await (0, import_promises5.readFile)(archivePath);
|
|
19132
19983
|
const base64Content = archiveContent.toString("base64");
|
|
19984
|
+
const snapshotBytes = archiveContent.byteLength;
|
|
19985
|
+
const snapshotWireBytes = Buffer.byteLength(base64Content, "utf-8");
|
|
19133
19986
|
const artifacts = await apiClient.uploadTaskArtifacts(taskId, runId, [
|
|
19134
19987
|
{
|
|
19135
19988
|
name: `trees/${treeHash}.tar.gz`,
|
|
@@ -19138,17 +19991,22 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
19138
19991
|
content_type: "application/gzip"
|
|
19139
19992
|
}
|
|
19140
19993
|
]);
|
|
19141
|
-
|
|
19994
|
+
const uploadedArtifact = artifacts[0];
|
|
19995
|
+
if (uploadedArtifact?.storage_path) {
|
|
19142
19996
|
this.log.info("Tree archive uploaded", {
|
|
19143
|
-
storagePath:
|
|
19144
|
-
treeHash
|
|
19997
|
+
storagePath: uploadedArtifact.storage_path,
|
|
19998
|
+
treeHash,
|
|
19999
|
+
snapshotBytes,
|
|
20000
|
+
snapshotWireBytes,
|
|
20001
|
+
totalBytes: snapshotBytes,
|
|
20002
|
+
totalWireBytes: snapshotWireBytes
|
|
19145
20003
|
});
|
|
19146
|
-
return
|
|
20004
|
+
return uploadedArtifact.storage_path;
|
|
19147
20005
|
}
|
|
19148
20006
|
return void 0;
|
|
19149
20007
|
},
|
|
19150
20008
|
rollback: async () => {
|
|
19151
|
-
await (0,
|
|
20009
|
+
await (0, import_promises5.rm)(archivePath, { force: true }).catch(() => {
|
|
19152
20010
|
});
|
|
19153
20011
|
}
|
|
19154
20012
|
});
|
|
@@ -19276,6 +20134,10 @@ var ResumeSaga = class extends Saga {
|
|
|
19276
20134
|
"find_snapshot",
|
|
19277
20135
|
() => Promise.resolve(this.findLatestTreeSnapshot(entries))
|
|
19278
20136
|
);
|
|
20137
|
+
const latestGitCheckpoint = await this.readOnlyStep(
|
|
20138
|
+
"find_git_checkpoint",
|
|
20139
|
+
() => Promise.resolve(this.findLatestGitCheckpoint(entries))
|
|
20140
|
+
);
|
|
19279
20141
|
let snapshotApplied = false;
|
|
19280
20142
|
if (latestSnapshot?.archiveUrl && repositoryPath) {
|
|
19281
20143
|
this.log.info("Found tree snapshot", {
|
|
@@ -19348,6 +20210,7 @@ var ResumeSaga = class extends Saga {
|
|
|
19348
20210
|
return {
|
|
19349
20211
|
conversation,
|
|
19350
20212
|
latestSnapshot,
|
|
20213
|
+
latestGitCheckpoint,
|
|
19351
20214
|
snapshotApplied,
|
|
19352
20215
|
interrupted: latestSnapshot?.interrupted ?? false,
|
|
19353
20216
|
lastDevice,
|
|
@@ -19358,6 +20221,7 @@ var ResumeSaga = class extends Saga {
|
|
|
19358
20221
|
return {
|
|
19359
20222
|
conversation: [],
|
|
19360
20223
|
latestSnapshot: null,
|
|
20224
|
+
latestGitCheckpoint: null,
|
|
19361
20225
|
snapshotApplied: false,
|
|
19362
20226
|
interrupted: false,
|
|
19363
20227
|
logEntryCount: 0
|
|
@@ -19378,6 +20242,20 @@ var ResumeSaga = class extends Saga {
|
|
|
19378
20242
|
}
|
|
19379
20243
|
return null;
|
|
19380
20244
|
}
|
|
20245
|
+
findLatestGitCheckpoint(entries) {
|
|
20246
|
+
const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;
|
|
20247
|
+
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
20248
|
+
const entry = entries[i2];
|
|
20249
|
+
const method = entry.notification?.method;
|
|
20250
|
+
if (method === sdkPrefixedMethod || method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT) {
|
|
20251
|
+
const params = entry.notification?.params;
|
|
20252
|
+
if (params?.checkpointId && params?.checkpointRef) {
|
|
20253
|
+
return params;
|
|
20254
|
+
}
|
|
20255
|
+
}
|
|
20256
|
+
}
|
|
20257
|
+
return null;
|
|
20258
|
+
}
|
|
19381
20259
|
findLastDeviceInfo(entries) {
|
|
19382
20260
|
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
19383
20261
|
const entry = entries[i2];
|
|
@@ -19526,17 +20404,49 @@ async function resumeFromLog(config) {
|
|
|
19526
20404
|
return {
|
|
19527
20405
|
conversation: result.data.conversation,
|
|
19528
20406
|
latestSnapshot: result.data.latestSnapshot,
|
|
20407
|
+
latestGitCheckpoint: result.data.latestGitCheckpoint,
|
|
19529
20408
|
snapshotApplied: result.data.snapshotApplied,
|
|
19530
20409
|
interrupted: result.data.interrupted,
|
|
19531
20410
|
lastDevice: result.data.lastDevice,
|
|
19532
20411
|
logEntryCount: result.data.logEntryCount
|
|
19533
20412
|
};
|
|
19534
20413
|
}
|
|
20414
|
+
var RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
20415
|
+
var TOOL_RESULT_MAX_CHARS = 2e3;
|
|
20416
|
+
function formatConversationForResume(conversation) {
|
|
20417
|
+
const selected = selectRecentTurns(conversation, RESUME_HISTORY_TOKEN_BUDGET);
|
|
20418
|
+
const parts2 = [];
|
|
20419
|
+
if (selected.length < conversation.length) {
|
|
20420
|
+
parts2.push(
|
|
20421
|
+
`*(${conversation.length - selected.length} earlier turns omitted)*`
|
|
20422
|
+
);
|
|
20423
|
+
}
|
|
20424
|
+
for (const turn of selected) {
|
|
20425
|
+
const role = turn.role === "user" ? "User" : "Assistant";
|
|
20426
|
+
const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
|
|
20427
|
+
if (textParts.length > 0) {
|
|
20428
|
+
parts2.push(`**${role}**: ${textParts.join("\n")}`);
|
|
20429
|
+
}
|
|
20430
|
+
if (turn.toolCalls?.length) {
|
|
20431
|
+
const toolSummary = turn.toolCalls.map((tc) => {
|
|
20432
|
+
let resultStr = "";
|
|
20433
|
+
if (tc.result !== void 0) {
|
|
20434
|
+
const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
|
|
20435
|
+
resultStr = raw.length > TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
|
|
20436
|
+
}
|
|
20437
|
+
return ` - ${tc.toolName}${resultStr}`;
|
|
20438
|
+
}).join("\n");
|
|
20439
|
+
parts2.push(`**${role} (tools)**:
|
|
20440
|
+
${toolSummary}`);
|
|
20441
|
+
}
|
|
20442
|
+
}
|
|
20443
|
+
return parts2.join("\n\n");
|
|
20444
|
+
}
|
|
19535
20445
|
|
|
19536
20446
|
// src/session-log-writer.ts
|
|
19537
20447
|
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
19538
|
-
var
|
|
19539
|
-
var
|
|
20448
|
+
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
20449
|
+
var import_node_path9 = __toESM(require("path"), 1);
|
|
19540
20450
|
var SessionLogWriter = class _SessionLogWriter {
|
|
19541
20451
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
19542
20452
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -19572,7 +20482,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19572
20482
|
this.sessions.set(sessionId, { context, currentTurnMessages: [] });
|
|
19573
20483
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
19574
20484
|
if (this.localCachePath) {
|
|
19575
|
-
const sessionDir =
|
|
20485
|
+
const sessionDir = import_node_path9.default.join(
|
|
19576
20486
|
this.localCachePath,
|
|
19577
20487
|
"sessions",
|
|
19578
20488
|
context.runId
|
|
@@ -19830,7 +20740,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19830
20740
|
if (!this.localCachePath) return;
|
|
19831
20741
|
const session = this.sessions.get(sessionId);
|
|
19832
20742
|
if (!session) return;
|
|
19833
|
-
const logPath =
|
|
20743
|
+
const logPath = import_node_path9.default.join(
|
|
19834
20744
|
this.localCachePath,
|
|
19835
20745
|
"sessions",
|
|
19836
20746
|
session.context.runId,
|
|
@@ -19849,17 +20759,17 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
19849
20759
|
}
|
|
19850
20760
|
}
|
|
19851
20761
|
static async cleanupOldSessions(localCachePath) {
|
|
19852
|
-
const sessionsDir =
|
|
20762
|
+
const sessionsDir = import_node_path9.default.join(localCachePath, "sessions");
|
|
19853
20763
|
let deleted = 0;
|
|
19854
20764
|
try {
|
|
19855
|
-
const entries = await
|
|
20765
|
+
const entries = await import_promises6.default.readdir(sessionsDir);
|
|
19856
20766
|
const now = Date.now();
|
|
19857
20767
|
for (const entry of entries) {
|
|
19858
|
-
const entryPath =
|
|
20768
|
+
const entryPath = import_node_path9.default.join(sessionsDir, entry);
|
|
19859
20769
|
try {
|
|
19860
|
-
const stats = await
|
|
20770
|
+
const stats = await import_promises6.default.stat(entryPath);
|
|
19861
20771
|
if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {
|
|
19862
|
-
await
|
|
20772
|
+
await import_promises6.default.rm(entryPath, { recursive: true, force: true });
|
|
19863
20773
|
deleted++;
|
|
19864
20774
|
}
|
|
19865
20775
|
} catch {
|
|
@@ -19937,6 +20847,14 @@ var httpHeaderSchema = import_v4.z.object({
|
|
|
19937
20847
|
name: import_v4.z.string(),
|
|
19938
20848
|
value: import_v4.z.string()
|
|
19939
20849
|
});
|
|
20850
|
+
var nullishString = import_v4.z.string().nullish().transform((value) => value ?? null);
|
|
20851
|
+
var handoffLocalGitStateSchema = import_v4.z.object({
|
|
20852
|
+
head: nullishString,
|
|
20853
|
+
branch: nullishString,
|
|
20854
|
+
upstreamHead: nullishString,
|
|
20855
|
+
upstreamRemote: nullishString,
|
|
20856
|
+
upstreamMergeRef: nullishString
|
|
20857
|
+
});
|
|
19940
20858
|
var remoteMcpServerSchema = import_v4.z.object({
|
|
19941
20859
|
type: import_v4.z.enum(["http", "sse"]),
|
|
19942
20860
|
name: import_v4.z.string().min(1, "MCP server name is required"),
|
|
@@ -19988,13 +20906,16 @@ var setConfigOptionParamsSchema = import_v4.z.object({
|
|
|
19988
20906
|
var refreshSessionParamsSchema = import_v4.z.object({
|
|
19989
20907
|
mcpServers: mcpServersSchema
|
|
19990
20908
|
});
|
|
20909
|
+
var closeParamsSchema = import_v4.z.object({
|
|
20910
|
+
localGitState: handoffLocalGitStateSchema.optional()
|
|
20911
|
+
}).optional();
|
|
19991
20912
|
var commandParamsSchemas = {
|
|
19992
20913
|
user_message: userMessageParamsSchema,
|
|
19993
20914
|
"posthog/user_message": userMessageParamsSchema,
|
|
19994
20915
|
cancel: import_v4.z.object({}).optional(),
|
|
19995
20916
|
"posthog/cancel": import_v4.z.object({}).optional(),
|
|
19996
|
-
close:
|
|
19997
|
-
"posthog/close":
|
|
20917
|
+
close: closeParamsSchema,
|
|
20918
|
+
"posthog/close": closeParamsSchema,
|
|
19998
20919
|
permission_response: permissionResponseParamsSchema,
|
|
19999
20920
|
"posthog/permission_response": permissionResponseParamsSchema,
|
|
20000
20921
|
set_config_option: setConfigOptionParamsSchema,
|
|
@@ -20117,7 +21038,7 @@ function getTaskRunStateString(taskRun, key) {
|
|
|
20117
21038
|
const value = state[key];
|
|
20118
21039
|
return typeof value === "string" ? value : null;
|
|
20119
21040
|
}
|
|
20120
|
-
var AgentServer = class
|
|
21041
|
+
var AgentServer = class {
|
|
20121
21042
|
config;
|
|
20122
21043
|
logger;
|
|
20123
21044
|
server = null;
|
|
@@ -20316,7 +21237,7 @@ var AgentServer = class _AgentServer {
|
|
|
20316
21237
|
return app;
|
|
20317
21238
|
}
|
|
20318
21239
|
async start() {
|
|
20319
|
-
await new Promise((
|
|
21240
|
+
await new Promise((resolve7) => {
|
|
20320
21241
|
this.server = (0, import_node_server.serve)(
|
|
20321
21242
|
{
|
|
20322
21243
|
fetch: this.app.fetch,
|
|
@@ -20326,7 +21247,7 @@ var AgentServer = class _AgentServer {
|
|
|
20326
21247
|
this.logger.debug(
|
|
20327
21248
|
`HTTP server listening on port ${this.config.port}`
|
|
20328
21249
|
);
|
|
20329
|
-
|
|
21250
|
+
resolve7();
|
|
20330
21251
|
}
|
|
20331
21252
|
);
|
|
20332
21253
|
});
|
|
@@ -20480,6 +21401,10 @@ var AgentServer = class _AgentServer {
|
|
|
20480
21401
|
case POSTHOG_NOTIFICATIONS.CLOSE:
|
|
20481
21402
|
case "close": {
|
|
20482
21403
|
this.logger.debug("Close requested");
|
|
21404
|
+
const localGitState = this.extractHandoffLocalGitState(params);
|
|
21405
|
+
if (localGitState && this.session) {
|
|
21406
|
+
this.session.pendingHandoffGitState = localGitState;
|
|
21407
|
+
}
|
|
20483
21408
|
await this.cleanupSession();
|
|
20484
21409
|
return { closed: true };
|
|
20485
21410
|
}
|
|
@@ -20706,7 +21631,8 @@ var AgentServer = class _AgentServer {
|
|
|
20706
21631
|
deviceInfo,
|
|
20707
21632
|
logWriter,
|
|
20708
21633
|
permissionMode: initialPermissionMode,
|
|
20709
|
-
hasDesktopConnected: sseController !== null
|
|
21634
|
+
hasDesktopConnected: sseController !== null,
|
|
21635
|
+
pendingHandoffGitState: void 0
|
|
20710
21636
|
};
|
|
20711
21637
|
this.logger = new Logger({
|
|
20712
21638
|
debug: true,
|
|
@@ -20842,7 +21768,7 @@ var AgentServer = class _AgentServer {
|
|
|
20842
21768
|
async sendResumeMessage(payload, taskRun) {
|
|
20843
21769
|
if (!this.session || !this.resumeState) return;
|
|
20844
21770
|
try {
|
|
20845
|
-
const conversationSummary =
|
|
21771
|
+
const conversationSummary = formatConversationForResume(
|
|
20846
21772
|
this.resumeState.conversation
|
|
20847
21773
|
);
|
|
20848
21774
|
const pendingUserPrompt = await this.getPendingUserPrompt(taskRun);
|
|
@@ -20913,40 +21839,6 @@ Continue from where you left off. The user is waiting for your response.`
|
|
|
20913
21839
|
await this.classifyAndSignalFailure(payload, "resume", error);
|
|
20914
21840
|
}
|
|
20915
21841
|
}
|
|
20916
|
-
static RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
20917
|
-
static TOOL_RESULT_MAX_CHARS = 2e3;
|
|
20918
|
-
formatConversationForResume(conversation) {
|
|
20919
|
-
const selected = selectRecentTurns(
|
|
20920
|
-
conversation,
|
|
20921
|
-
_AgentServer.RESUME_HISTORY_TOKEN_BUDGET
|
|
20922
|
-
);
|
|
20923
|
-
const parts2 = [];
|
|
20924
|
-
if (selected.length < conversation.length) {
|
|
20925
|
-
parts2.push(
|
|
20926
|
-
`*(${conversation.length - selected.length} earlier turns omitted)*`
|
|
20927
|
-
);
|
|
20928
|
-
}
|
|
20929
|
-
for (const turn of selected) {
|
|
20930
|
-
const role = turn.role === "user" ? "User" : "Assistant";
|
|
20931
|
-
const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
|
|
20932
|
-
if (textParts.length > 0) {
|
|
20933
|
-
parts2.push(`**${role}**: ${textParts.join("\n")}`);
|
|
20934
|
-
}
|
|
20935
|
-
if (turn.toolCalls?.length) {
|
|
20936
|
-
const toolSummary = turn.toolCalls.map((tc) => {
|
|
20937
|
-
let resultStr = "";
|
|
20938
|
-
if (tc.result !== void 0) {
|
|
20939
|
-
const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
|
|
20940
|
-
resultStr = raw.length > _AgentServer.TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, _AgentServer.TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
|
|
20941
|
-
}
|
|
20942
|
-
return ` - ${tc.toolName}${resultStr}`;
|
|
20943
|
-
}).join("\n");
|
|
20944
|
-
parts2.push(`**${role} (tools)**:
|
|
20945
|
-
${toolSummary}`);
|
|
20946
|
-
}
|
|
20947
|
-
}
|
|
20948
|
-
return parts2.join("\n\n");
|
|
20949
|
-
}
|
|
20950
21842
|
getInitialPromptOverride(taskRun) {
|
|
20951
21843
|
const state = taskRun.state;
|
|
20952
21844
|
const override = state?.initial_prompt_override;
|
|
@@ -21065,23 +21957,23 @@ ${toolSummary}`);
|
|
|
21065
21957
|
throw new Error(`Failed to download artifact ${artifact.name}`);
|
|
21066
21958
|
}
|
|
21067
21959
|
const safeName = this.getSafeArtifactName(artifact.name);
|
|
21068
|
-
const artifactDir = (0,
|
|
21960
|
+
const artifactDir = (0, import_node_path10.join)(
|
|
21069
21961
|
this.config.repositoryPath ?? "/tmp/workspace",
|
|
21070
21962
|
".posthog",
|
|
21071
21963
|
"attachments",
|
|
21072
21964
|
runId,
|
|
21073
21965
|
artifact.id ?? safeName
|
|
21074
21966
|
);
|
|
21075
|
-
await (0,
|
|
21076
|
-
const artifactPath = (0,
|
|
21077
|
-
await (0,
|
|
21967
|
+
await (0, import_promises7.mkdir)(artifactDir, { recursive: true });
|
|
21968
|
+
const artifactPath = (0, import_node_path10.join)(artifactDir, safeName);
|
|
21969
|
+
await (0, import_promises7.writeFile)(artifactPath, Buffer.from(data));
|
|
21078
21970
|
return resourceLink((0, import_node_url2.pathToFileURL)(artifactPath).toString(), artifact.name, {
|
|
21079
21971
|
...artifact.content_type ? { mimeType: artifact.content_type } : {},
|
|
21080
21972
|
...typeof artifact.size === "number" ? { size: artifact.size } : {}
|
|
21081
21973
|
});
|
|
21082
21974
|
}
|
|
21083
21975
|
getSafeArtifactName(name2) {
|
|
21084
|
-
const baseName = (0,
|
|
21976
|
+
const baseName = (0, import_node_path10.basename)(name2).trim();
|
|
21085
21977
|
const normalizedName = baseName.replace(/[^\w.-]/g, "_");
|
|
21086
21978
|
return normalizedName.length > 0 ? normalizedName : "attachment";
|
|
21087
21979
|
}
|
|
@@ -21573,6 +22465,11 @@ ${attributionInstructions}
|
|
|
21573
22465
|
async cleanupSession() {
|
|
21574
22466
|
if (!this.session) return;
|
|
21575
22467
|
this.logger.debug("Cleaning up session");
|
|
22468
|
+
try {
|
|
22469
|
+
await this.captureHandoffCheckpoint();
|
|
22470
|
+
} catch (error) {
|
|
22471
|
+
this.logger.error("Failed to capture handoff checkpoint", error);
|
|
22472
|
+
}
|
|
21576
22473
|
try {
|
|
21577
22474
|
await this.captureTreeState();
|
|
21578
22475
|
} catch (error) {
|
|
@@ -21632,6 +22529,50 @@ ${attributionInstructions}
|
|
|
21632
22529
|
this.logger.error("Failed to capture tree state", error);
|
|
21633
22530
|
}
|
|
21634
22531
|
}
|
|
22532
|
+
async captureHandoffCheckpoint() {
|
|
22533
|
+
if (!this.session?.treeTracker || !this.session.pendingHandoffGitState) {
|
|
22534
|
+
return;
|
|
22535
|
+
}
|
|
22536
|
+
if (!this.posthogAPI) {
|
|
22537
|
+
this.logger.warn(
|
|
22538
|
+
"Skipping handoff checkpoint capture: PostHog API client is not configured"
|
|
22539
|
+
);
|
|
22540
|
+
return;
|
|
22541
|
+
}
|
|
22542
|
+
const tracker = new HandoffCheckpointTracker({
|
|
22543
|
+
repositoryPath: this.config.repositoryPath ?? "/tmp/workspace",
|
|
22544
|
+
taskId: this.session.payload.task_id,
|
|
22545
|
+
runId: this.session.payload.run_id,
|
|
22546
|
+
apiClient: this.posthogAPI,
|
|
22547
|
+
logger: this.logger.child("HandoffCheckpoint")
|
|
22548
|
+
});
|
|
22549
|
+
const checkpoint = await tracker.captureForHandoff(
|
|
22550
|
+
this.session.pendingHandoffGitState
|
|
22551
|
+
);
|
|
22552
|
+
if (!checkpoint) return;
|
|
22553
|
+
const checkpointWithDevice = {
|
|
22554
|
+
...checkpoint,
|
|
22555
|
+
device: this.session.deviceInfo
|
|
22556
|
+
};
|
|
22557
|
+
const notification = {
|
|
22558
|
+
jsonrpc: "2.0",
|
|
22559
|
+
method: POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT,
|
|
22560
|
+
params: checkpointWithDevice
|
|
22561
|
+
};
|
|
22562
|
+
this.broadcastEvent({
|
|
22563
|
+
type: "notification",
|
|
22564
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22565
|
+
notification
|
|
22566
|
+
});
|
|
22567
|
+
this.session.logWriter.appendRawLine(
|
|
22568
|
+
this.session.payload.run_id,
|
|
22569
|
+
JSON.stringify(notification)
|
|
22570
|
+
);
|
|
22571
|
+
}
|
|
22572
|
+
extractHandoffLocalGitState(params) {
|
|
22573
|
+
const result = handoffLocalGitStateSchema.safeParse(params.localGitState);
|
|
22574
|
+
return result.success ? result.data : null;
|
|
22575
|
+
}
|
|
21635
22576
|
broadcastTurnComplete(stopReason) {
|
|
21636
22577
|
if (!this.session) return;
|
|
21637
22578
|
this.broadcastEvent({
|
|
@@ -21685,8 +22626,8 @@ ${attributionInstructions}
|
|
|
21685
22626
|
options: params.options,
|
|
21686
22627
|
toolCall: params.toolCall
|
|
21687
22628
|
});
|
|
21688
|
-
return new Promise((
|
|
21689
|
-
this.pendingPermissions.set(requestId, { resolve:
|
|
22629
|
+
return new Promise((resolve7) => {
|
|
22630
|
+
this.pendingPermissions.set(requestId, { resolve: resolve7 });
|
|
21690
22631
|
});
|
|
21691
22632
|
}
|
|
21692
22633
|
resolvePermission(requestId, optionId, customInput, answers) {
|