@posthog/agent 2.3.401 → 2.3.403
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -14
- package/dist/agent.js +1 -7
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.d.ts +0 -3
- package/dist/handoff-checkpoint.js +38 -69
- package/dist/handoff-checkpoint.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +1 -5
- package/dist/posthog-api.js.map +1 -1
- package/dist/resume.d.ts +5 -6
- package/dist/resume.js +2 -41
- package/dist/resume.js.map +1 -1
- package/dist/server/agent-server.d.ts +1 -2
- package/dist/server/agent-server.js +103 -815
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +101 -806
- package/dist/server/bin.cjs.map +1 -1
- package/dist/types.d.ts +2 -13
- package/dist/types.js.map +1 -1
- package/package.json +3 -7
- package/src/acp-extensions.ts +0 -3
- package/src/handoff-checkpoint.test.ts +2 -17
- package/src/handoff-checkpoint.ts +15 -61
- package/src/resume.ts +5 -11
- package/src/sagas/resume-saga.test.ts +27 -77
- package/src/sagas/resume-saga.ts +3 -44
- package/src/sagas/test-fixtures.ts +17 -76
- package/src/server/agent-server.ts +22 -103
- package/src/test/fixtures/api.ts +2 -15
- package/src/types.ts +0 -16
- package/dist/tree-tracker.d.ts +0 -68
- package/dist/tree-tracker.js +0 -6462
- package/dist/tree-tracker.js.map +0 -1
- package/src/sagas/apply-snapshot-saga.test.ts +0 -691
- package/src/sagas/apply-snapshot-saga.ts +0 -114
- package/src/sagas/capture-tree-saga.test.ts +0 -910
- package/src/sagas/capture-tree-saga.ts +0 -165
- package/src/tree-tracker.ts +0 -173
package/dist/server/bin.cjs
CHANGED
|
@@ -805,10 +805,10 @@ 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(path16, isFile2, isDirectory) {
|
|
809
|
+
log(`checking %s`, path16);
|
|
810
810
|
try {
|
|
811
|
-
const stat4 = fs_1.statSync(
|
|
811
|
+
const stat4 = fs_1.statSync(path16);
|
|
812
812
|
if (stat4.isFile() && isFile2) {
|
|
813
813
|
log(`[OK] path represents a file`);
|
|
814
814
|
return true;
|
|
@@ -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(path16, type = exports2.READABLE) {
|
|
832
|
+
return check(path16, (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(path16) {
|
|
929
929
|
if (Module["locateFile"]) {
|
|
930
|
-
return Module["locateFile"](
|
|
930
|
+
return Module["locateFile"](path16, scriptDirectory);
|
|
931
931
|
}
|
|
932
|
-
return scriptDirectory +
|
|
932
|
+
return scriptDirectory + path16;
|
|
933
933
|
}
|
|
934
934
|
var readAsync, readBinary;
|
|
935
935
|
if (ENVIRONMENT_IS_NODE) {
|
|
@@ -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 fs13 = require("fs");
|
|
3454
|
+
bytes = Promise.resolve(fs13.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_promises5 = require("fs/promises");
|
|
3916
|
+
var import_node_path8 = 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(path16) {
|
|
3963
|
+
return path16 instanceof String && cache.has(path16);
|
|
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(path16) {
|
|
4053
|
+
return (0, import_file_exists.exists)(path16, 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(path16) {
|
|
4458
|
+
return /^\.(git)?$/.test(path16.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 [path16, line, preview] = input.split(NULL);
|
|
4893
|
+
paths.add(path16);
|
|
4894
|
+
(results[path16] = results[path16] || []).push({
|
|
4895
4895
|
line: asNumber(line),
|
|
4896
|
-
path:
|
|
4896
|
+
path: path16,
|
|
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, path16, 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, path16, false, result[1]);
|
|
5666
5666
|
}
|
|
5667
5667
|
if (result = reInitResponseRegex.exec(response)) {
|
|
5668
|
-
return new InitSummary(bare,
|
|
5668
|
+
return new InitSummary(bare, path16, true, result[1]);
|
|
5669
5669
|
}
|
|
5670
5670
|
let gitDir = "";
|
|
5671
5671
|
const tokens = response.split(" ");
|
|
@@ -5676,7 +5676,7 @@ function parseInit(bare, path17, text2) {
|
|
|
5676
5676
|
break;
|
|
5677
5677
|
}
|
|
5678
5678
|
}
|
|
5679
|
-
return new InitSummary(bare,
|
|
5679
|
+
return new InitSummary(bare, path16, /^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, path16, existing, gitDir) {
|
|
5689
5689
|
this.bare = bare;
|
|
5690
|
-
this.path =
|
|
5690
|
+
this.path = path16;
|
|
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, path16, 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, path17, 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"), path16, 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(path16, index, working_dir) {
|
|
6528
|
+
this.path = path16;
|
|
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(path16) || [null, path16, path16];
|
|
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, path16) {
|
|
6564
6564
|
const raw = `${index}${workingDir}`;
|
|
6565
6565
|
const handler = parsers6.get(raw);
|
|
6566
6566
|
if (handler) {
|
|
6567
|
-
handler(result,
|
|
6567
|
+
handler(result, path16);
|
|
6568
6568
|
}
|
|
6569
6569
|
if (raw !== "##" && raw !== "!!") {
|
|
6570
|
-
result.files.push(new FileStatusSummary(
|
|
6570
|
+
result.files.push(new FileStatusSummary(path16, 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(path16, write) {
|
|
6884
6884
|
return this._runTask(
|
|
6885
|
-
hashObjectTask(
|
|
6885
|
+
hashObjectTask(path16, 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 path16 = input.trim().replace(/^["']|["']$/g, "");
|
|
7239
|
+
return path16 && (0, import_node_path.normalize)(path16);
|
|
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, path16) {
|
|
7554
|
+
return subModuleTask(["add", repo, path16]);
|
|
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, path16, then) {
|
|
7885
|
+
return this._runTask(addSubModuleTask2(repo, path16), trailingFunctionArgument2(arguments));
|
|
7886
7886
|
};
|
|
7887
7887
|
Git2.prototype.submoduleUpdate = function(args2, then) {
|
|
7888
7888
|
return this._runTask(
|
|
@@ -8715,12 +8715,6 @@ async function listWorktrees(baseDir, options) {
|
|
|
8715
8715
|
return worktrees;
|
|
8716
8716
|
}, { signal: options?.abortSignal });
|
|
8717
8717
|
}
|
|
8718
|
-
async function getHeadSha(baseDir, options) {
|
|
8719
|
-
const manager = getGitOperationManager();
|
|
8720
|
-
return manager.executeRead(baseDir, (git) => git.revparse(["HEAD"]), {
|
|
8721
|
-
signal: options?.abortSignal
|
|
8722
|
-
});
|
|
8723
|
-
}
|
|
8724
8718
|
|
|
8725
8719
|
// src/server/agent-server.ts
|
|
8726
8720
|
var import_hono = require("hono");
|
|
@@ -8729,7 +8723,7 @@ var import_zod3 = require("zod");
|
|
|
8729
8723
|
// package.json
|
|
8730
8724
|
var package_default = {
|
|
8731
8725
|
name: "@posthog/agent",
|
|
8732
|
-
version: "2.3.
|
|
8726
|
+
version: "2.3.403",
|
|
8733
8727
|
repository: "https://github.com/PostHog/code",
|
|
8734
8728
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8735
8729
|
exports: {
|
|
@@ -8797,10 +8791,6 @@ var package_default = {
|
|
|
8797
8791
|
types: "./dist/handoff-checkpoint.d.ts",
|
|
8798
8792
|
import: "./dist/handoff-checkpoint.js"
|
|
8799
8793
|
},
|
|
8800
|
-
"./tree-tracker": {
|
|
8801
|
-
types: "./dist/tree-tracker.d.ts",
|
|
8802
|
-
import: "./dist/tree-tracker.js"
|
|
8803
|
-
},
|
|
8804
8794
|
"./server": {
|
|
8805
8795
|
types: "./dist/server/agent-server.d.ts",
|
|
8806
8796
|
import: "./dist/server/agent-server.js"
|
|
@@ -8896,8 +8886,6 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
8896
8886
|
CONSOLE: "_posthog/console",
|
|
8897
8887
|
/** Maps taskRunId to agent's sessionId and adapter type (for resumption) */
|
|
8898
8888
|
SDK_SESSION: "_posthog/sdk_session",
|
|
8899
|
-
/** Tree state snapshot captured (git tree hash + file archive) */
|
|
8900
|
-
TREE_SNAPSHOT: "_posthog/tree_snapshot",
|
|
8901
8889
|
/** Git checkpoint captured for handoff */
|
|
8902
8890
|
GIT_CHECKPOINT: "_posthog/git_checkpoint",
|
|
8903
8891
|
/** Agent mode changed (interactive/background) */
|
|
@@ -8936,9 +8924,6 @@ function matchesExt(method, expected) {
|
|
|
8936
8924
|
if (!method) return false;
|
|
8937
8925
|
return method === expected || method === `_${expected}`;
|
|
8938
8926
|
}
|
|
8939
|
-
function isNotification(method, expected) {
|
|
8940
|
-
return matchesExt(method, expected);
|
|
8941
|
-
}
|
|
8942
8927
|
function isMethod(method, expected) {
|
|
8943
8928
|
return matchesExt(method, expected);
|
|
8944
8929
|
}
|
|
@@ -13569,8 +13554,8 @@ var ToolContentBuilder = class {
|
|
|
13569
13554
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
13570
13555
|
return this;
|
|
13571
13556
|
}
|
|
13572
|
-
diff(
|
|
13573
|
-
this.items.push({ type: "diff", path:
|
|
13557
|
+
diff(path16, oldText, newText) {
|
|
13558
|
+
this.items.push({ type: "diff", path: path16, oldText, newText });
|
|
13574
13559
|
return this;
|
|
13575
13560
|
}
|
|
13576
13561
|
build() {
|
|
@@ -18453,11 +18438,13 @@ function createCodexConnection(config) {
|
|
|
18453
18438
|
|
|
18454
18439
|
// src/handoff-checkpoint.ts
|
|
18455
18440
|
var import_promises3 = require("fs/promises");
|
|
18441
|
+
var import_node_os2 = require("os");
|
|
18456
18442
|
var import_node_path6 = require("path");
|
|
18457
18443
|
|
|
18458
18444
|
// ../git/dist/handoff.js
|
|
18459
18445
|
var import_node_child_process4 = require("child_process");
|
|
18460
18446
|
var import_promises2 = require("fs/promises");
|
|
18447
|
+
var import_node_os = require("os");
|
|
18461
18448
|
var import_node_path5 = __toESM(require("path"), 1);
|
|
18462
18449
|
|
|
18463
18450
|
// ../git/dist/sagas/checkpoint.js
|
|
@@ -18865,7 +18852,7 @@ var GitHandoffTracker = class {
|
|
|
18865
18852
|
this.repositoryPath = config.repositoryPath;
|
|
18866
18853
|
this.logger = config.logger;
|
|
18867
18854
|
}
|
|
18868
|
-
async captureForHandoff(
|
|
18855
|
+
async captureForHandoff(_localGitState) {
|
|
18869
18856
|
const captureSaga = new CaptureCheckpointSaga(this.logger);
|
|
18870
18857
|
const result = await captureSaga.run({ baseDir: this.repositoryPath });
|
|
18871
18858
|
if (!result.success) {
|
|
@@ -18873,15 +18860,19 @@ var GitHandoffTracker = class {
|
|
|
18873
18860
|
}
|
|
18874
18861
|
const checkpoint = result.data;
|
|
18875
18862
|
const git = createGitClient(this.repositoryPath);
|
|
18876
|
-
const tempDir = await this.
|
|
18863
|
+
const tempDir = await this.createTempDir(checkpoint.checkpointId);
|
|
18877
18864
|
const checkpointRef = `${CHECKPOINT_REF_PREFIX2}${checkpoint.checkpointId}`;
|
|
18878
|
-
const
|
|
18879
|
-
|
|
18865
|
+
const packRefs = [
|
|
18866
|
+
checkpoint.head,
|
|
18867
|
+
checkpoint.indexTree,
|
|
18868
|
+
checkpoint.worktreeTree
|
|
18869
|
+
].filter((ref) => !!ref);
|
|
18870
|
+
const headRef = checkpoint.head ? `${HANDOFF_HEAD_REF_PREFIX}${checkpoint.checkpointId}` : void 0;
|
|
18880
18871
|
const packPrefix = import_node_path5.default.join(tempDir, checkpoint.checkpointId);
|
|
18881
18872
|
try {
|
|
18882
18873
|
const [headPack, indexFile, tracking] = await Promise.all([
|
|
18883
|
-
|
|
18884
|
-
this.copyIndexFile(git, checkpoint.checkpointId),
|
|
18874
|
+
this.captureObjectPack(packPrefix, packRefs),
|
|
18875
|
+
this.copyIndexFile(git, checkpoint.checkpointId, tempDir),
|
|
18885
18876
|
getTrackingMetadata(git, checkpoint.branch)
|
|
18886
18877
|
]);
|
|
18887
18878
|
return {
|
|
@@ -18928,6 +18919,8 @@ var GitHandoffTracker = class {
|
|
|
18928
18919
|
} else if (checkpoint.head) {
|
|
18929
18920
|
await git.checkout(checkpoint.head);
|
|
18930
18921
|
}
|
|
18922
|
+
await git.clean(["f", "d"]);
|
|
18923
|
+
await git.raw(["read-tree", "--reset", "-u", checkpoint.worktreeTree]);
|
|
18931
18924
|
if (indexPath) {
|
|
18932
18925
|
await this.restoreIndexFile(git, indexPath);
|
|
18933
18926
|
}
|
|
@@ -18939,8 +18932,8 @@ var GitHandoffTracker = class {
|
|
|
18939
18932
|
totalBytes: packBytes + indexBytes
|
|
18940
18933
|
};
|
|
18941
18934
|
}
|
|
18942
|
-
async
|
|
18943
|
-
const hash = await this.runGitWithInput(["pack-objects", packPrefix, "--revs"], `${
|
|
18935
|
+
async captureObjectPack(packPrefix, refs) {
|
|
18936
|
+
const hash = await this.runGitWithInput(["pack-objects", packPrefix, "--revs"], `${refs.join("\n")}
|
|
18944
18937
|
`);
|
|
18945
18938
|
const packPath = `${packPrefix}-${hash.trim()}.pack`;
|
|
18946
18939
|
const rawBytes = await this.getFileSize(packPath);
|
|
@@ -18948,9 +18941,8 @@ var GitHandoffTracker = class {
|
|
|
18948
18941
|
});
|
|
18949
18942
|
return { path: packPath, rawBytes };
|
|
18950
18943
|
}
|
|
18951
|
-
async copyIndexFile(git, checkpointId) {
|
|
18944
|
+
async copyIndexFile(git, checkpointId, tempDir) {
|
|
18952
18945
|
const indexPath = await this.getGitPath(git, "index");
|
|
18953
|
-
const tempDir = await this.getTempDir(git);
|
|
18954
18946
|
const copiedIndexPath = import_node_path5.default.join(tempDir, `${checkpointId}.index`);
|
|
18955
18947
|
await (0, import_promises2.copyFile)(indexPath, copiedIndexPath);
|
|
18956
18948
|
return {
|
|
@@ -19078,13 +19070,8 @@ var GitHandoffTracker = class {
|
|
|
19078
19070
|
]);
|
|
19079
19071
|
return exitCode === 0;
|
|
19080
19072
|
}
|
|
19081
|
-
async
|
|
19082
|
-
|
|
19083
|
-
const commonDir = raw.trim() || ".git";
|
|
19084
|
-
const resolved = import_node_path5.default.isAbsolute(commonDir) ? commonDir : import_node_path5.default.resolve(this.repositoryPath, commonDir);
|
|
19085
|
-
const tempDir = import_node_path5.default.join(resolved, "posthog-code-tmp");
|
|
19086
|
-
await (0, import_promises2.mkdir)(tempDir, { recursive: true });
|
|
19087
|
-
return tempDir;
|
|
19073
|
+
async createTempDir(checkpointId) {
|
|
19074
|
+
return (0, import_promises2.mkdtemp)(joinTempPrefix(checkpointId));
|
|
19088
19075
|
}
|
|
19089
19076
|
async getGitPath(git, gitPath) {
|
|
19090
19077
|
const raw = await git.raw(["rev-parse", "--git-path", gitPath]);
|
|
@@ -19151,6 +19138,9 @@ var GitHandoffTracker = class {
|
|
|
19151
19138
|
});
|
|
19152
19139
|
}
|
|
19153
19140
|
};
|
|
19141
|
+
function joinTempPrefix(checkpointId) {
|
|
19142
|
+
return import_node_path5.default.join((0, import_node_os.tmpdir)(), `posthog-code-handoff-${checkpointId}-`);
|
|
19143
|
+
}
|
|
19154
19144
|
async function getCurrentBranchName(git) {
|
|
19155
19145
|
try {
|
|
19156
19146
|
const raw = await git.revparse(["--abbrev-ref", "HEAD"]);
|
|
@@ -19237,8 +19227,11 @@ var HandoffCheckpointTracker = class {
|
|
|
19237
19227
|
indexArtifactPath: uploads.index?.storagePath
|
|
19238
19228
|
};
|
|
19239
19229
|
} finally {
|
|
19230
|
+
const tempDir = capture.headPack?.path ? (0, import_node_path6.dirname)(capture.headPack.path) : (0, import_node_path6.dirname)(capture.indexFile.path);
|
|
19240
19231
|
await this.removeIfPresent(capture.headPack?.path);
|
|
19241
19232
|
await this.removeIfPresent(capture.indexFile.path);
|
|
19233
|
+
await (0, import_promises3.rm)(tempDir, { recursive: true, force: true }).catch(() => {
|
|
19234
|
+
});
|
|
19242
19235
|
}
|
|
19243
19236
|
}
|
|
19244
19237
|
async applyFromHandoff(checkpoint, options) {
|
|
@@ -19248,8 +19241,9 @@ var HandoffCheckpointTracker = class {
|
|
|
19248
19241
|
);
|
|
19249
19242
|
}
|
|
19250
19243
|
const gitTracker = this.createGitTracker();
|
|
19251
|
-
const tmpDir = (0,
|
|
19252
|
-
|
|
19244
|
+
const tmpDir = await (0, import_promises3.mkdtemp)(
|
|
19245
|
+
(0, import_node_path6.join)((0, import_node_os2.tmpdir)(), `posthog-code-handoff-${checkpoint.checkpointId}-`)
|
|
19246
|
+
);
|
|
19253
19247
|
const packPath = (0, import_node_path6.join)(tmpDir, `${checkpoint.checkpointId}.pack`);
|
|
19254
19248
|
const indexPath = (0, import_node_path6.join)(tmpDir, `${checkpoint.checkpointId}.index`);
|
|
19255
19249
|
try {
|
|
@@ -19283,7 +19277,8 @@ var HandoffCheckpointTracker = class {
|
|
|
19283
19277
|
} finally {
|
|
19284
19278
|
await this.removeIfPresent(packPath);
|
|
19285
19279
|
await this.removeIfPresent(indexPath);
|
|
19286
|
-
await
|
|
19280
|
+
await (0, import_promises3.rm)(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
19281
|
+
});
|
|
19287
19282
|
}
|
|
19288
19283
|
}
|
|
19289
19284
|
toGitCheckpoint(checkpoint) {
|
|
@@ -19391,50 +19386,24 @@ var HandoffCheckpointTracker = class {
|
|
|
19391
19386
|
}
|
|
19392
19387
|
logCaptureMetrics(checkpoint, uploads) {
|
|
19393
19388
|
this.logger.info("Captured handoff checkpoint", {
|
|
19394
|
-
checkpointId: checkpoint.checkpointId,
|
|
19395
19389
|
branch: checkpoint.branch,
|
|
19396
|
-
head: checkpoint.head,
|
|
19397
|
-
|
|
19398
|
-
indexArtifactPath: uploads.index?.storagePath,
|
|
19399
|
-
...this.buildMetricPayload(uploads)
|
|
19390
|
+
head: checkpoint.head?.slice(0, 7),
|
|
19391
|
+
totalBytes: this.sumRawBytes(uploads.pack, uploads.index)
|
|
19400
19392
|
});
|
|
19401
19393
|
}
|
|
19402
|
-
logApplyMetrics(checkpoint,
|
|
19394
|
+
logApplyMetrics(checkpoint, _downloads, totalBytes) {
|
|
19403
19395
|
this.logger.info("Applied handoff checkpoint", {
|
|
19404
|
-
checkpointId: checkpoint.checkpointId,
|
|
19405
|
-
commit: checkpoint.commit,
|
|
19406
19396
|
branch: checkpoint.branch,
|
|
19407
|
-
head: checkpoint.head,
|
|
19408
|
-
|
|
19409
|
-
packWireBytes: downloads.pack?.wireBytes ?? 0,
|
|
19410
|
-
indexBytes: downloads.index?.rawBytes ?? 0,
|
|
19411
|
-
indexWireBytes: downloads.index?.wireBytes ?? 0,
|
|
19412
|
-
totalBytes,
|
|
19413
|
-
totalWireBytes: this.sumWireBytes(downloads.pack, downloads.index)
|
|
19397
|
+
head: checkpoint.head?.slice(0, 7),
|
|
19398
|
+
totalBytes
|
|
19414
19399
|
});
|
|
19415
19400
|
}
|
|
19416
|
-
buildMetricPayload(metrics) {
|
|
19417
|
-
return {
|
|
19418
|
-
packBytes: metrics.pack?.rawBytes ?? 0,
|
|
19419
|
-
packWireBytes: metrics.pack?.wireBytes ?? 0,
|
|
19420
|
-
indexBytes: metrics.index?.rawBytes ?? 0,
|
|
19421
|
-
indexWireBytes: metrics.index?.wireBytes ?? 0,
|
|
19422
|
-
totalBytes: this.sumRawBytes(metrics.pack, metrics.index),
|
|
19423
|
-
totalWireBytes: this.sumWireBytes(metrics.pack, metrics.index)
|
|
19424
|
-
};
|
|
19425
|
-
}
|
|
19426
19401
|
sumRawBytes(...artifacts) {
|
|
19427
19402
|
return artifacts.reduce(
|
|
19428
19403
|
(total, artifact) => total + (artifact?.rawBytes ?? 0),
|
|
19429
19404
|
0
|
|
19430
19405
|
);
|
|
19431
19406
|
}
|
|
19432
|
-
sumWireBytes(...artifacts) {
|
|
19433
|
-
return artifacts.reduce(
|
|
19434
|
-
(total, artifact) => total + (artifact?.wireBytes ?? 0),
|
|
19435
|
-
0
|
|
19436
|
-
);
|
|
19437
|
-
}
|
|
19438
19407
|
async removeIfPresent(filePath) {
|
|
19439
19408
|
if (!filePath) {
|
|
19440
19409
|
return;
|
|
@@ -19442,14 +19411,6 @@ var HandoffCheckpointTracker = class {
|
|
|
19442
19411
|
await (0, import_promises3.rm)(filePath, { force: true }).catch(() => {
|
|
19443
19412
|
});
|
|
19444
19413
|
}
|
|
19445
|
-
async removeTmpDirIfEmpty(tmpDir) {
|
|
19446
|
-
const entries = await (0, import_promises3.readdir)(tmpDir).catch(() => null);
|
|
19447
|
-
if (!entries || entries.length > 0) {
|
|
19448
|
-
return;
|
|
19449
|
-
}
|
|
19450
|
-
await (0, import_promises3.rmdir)(tmpDir).catch(() => {
|
|
19451
|
-
});
|
|
19452
|
-
}
|
|
19453
19414
|
};
|
|
19454
19415
|
|
|
19455
19416
|
// src/utils/gateway.ts
|
|
@@ -19722,21 +19683,10 @@ var ResumeSaga = class extends Saga {
|
|
|
19722
19683
|
return this.emptyResult();
|
|
19723
19684
|
}
|
|
19724
19685
|
this.log.info("Fetched log entries", { count: entries.length });
|
|
19725
|
-
const latestSnapshot = await this.readOnlyStep(
|
|
19726
|
-
"find_snapshot",
|
|
19727
|
-
() => Promise.resolve(this.findLatestTreeSnapshot(entries))
|
|
19728
|
-
);
|
|
19729
19686
|
const latestGitCheckpoint = await this.readOnlyStep(
|
|
19730
19687
|
"find_git_checkpoint",
|
|
19731
19688
|
() => Promise.resolve(this.findLatestGitCheckpoint(entries))
|
|
19732
19689
|
);
|
|
19733
|
-
if (latestSnapshot) {
|
|
19734
|
-
this.log.info("Found tree snapshot", {
|
|
19735
|
-
treeHash: latestSnapshot.treeHash,
|
|
19736
|
-
hasArchiveUrl: !!latestSnapshot.archiveUrl,
|
|
19737
|
-
changes: latestSnapshot.changes?.length ?? 0
|
|
19738
|
-
});
|
|
19739
|
-
}
|
|
19740
19690
|
if (latestGitCheckpoint) {
|
|
19741
19691
|
this.log.info("Found git checkpoint", {
|
|
19742
19692
|
checkpointId: latestGitCheckpoint.checkpointId,
|
|
@@ -19753,15 +19703,13 @@ var ResumeSaga = class extends Saga {
|
|
|
19753
19703
|
);
|
|
19754
19704
|
this.log.info("Resume state rebuilt", {
|
|
19755
19705
|
turns: conversation.length,
|
|
19756
|
-
hasSnapshot: !!latestSnapshot,
|
|
19757
19706
|
hasGitCheckpoint: !!latestGitCheckpoint,
|
|
19758
|
-
interrupted:
|
|
19707
|
+
interrupted: false
|
|
19759
19708
|
});
|
|
19760
19709
|
return {
|
|
19761
19710
|
conversation,
|
|
19762
|
-
latestSnapshot,
|
|
19763
19711
|
latestGitCheckpoint,
|
|
19764
|
-
interrupted:
|
|
19712
|
+
interrupted: false,
|
|
19765
19713
|
lastDevice,
|
|
19766
19714
|
logEntryCount: entries.length
|
|
19767
19715
|
};
|
|
@@ -19769,27 +19717,11 @@ var ResumeSaga = class extends Saga {
|
|
|
19769
19717
|
emptyResult() {
|
|
19770
19718
|
return {
|
|
19771
19719
|
conversation: [],
|
|
19772
|
-
latestSnapshot: null,
|
|
19773
19720
|
latestGitCheckpoint: null,
|
|
19774
19721
|
interrupted: false,
|
|
19775
19722
|
logEntryCount: 0
|
|
19776
19723
|
};
|
|
19777
19724
|
}
|
|
19778
|
-
findLatestTreeSnapshot(entries) {
|
|
19779
|
-
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
19780
|
-
const entry = entries[i2];
|
|
19781
|
-
if (isNotification(
|
|
19782
|
-
entry.notification?.method,
|
|
19783
|
-
POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT
|
|
19784
|
-
)) {
|
|
19785
|
-
const params = entry.notification.params;
|
|
19786
|
-
if (params?.treeHash) {
|
|
19787
|
-
return params;
|
|
19788
|
-
}
|
|
19789
|
-
}
|
|
19790
|
-
}
|
|
19791
|
-
return null;
|
|
19792
|
-
}
|
|
19793
19725
|
findLatestGitCheckpoint(entries) {
|
|
19794
19726
|
const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;
|
|
19795
19727
|
for (let i2 = entries.length - 1; i2 >= 0; i2--) {
|
|
@@ -19951,7 +19883,6 @@ async function resumeFromLog(config) {
|
|
|
19951
19883
|
}
|
|
19952
19884
|
return {
|
|
19953
19885
|
conversation: result.data.conversation,
|
|
19954
|
-
latestSnapshot: result.data.latestSnapshot,
|
|
19955
19886
|
latestGitCheckpoint: result.data.latestGitCheckpoint,
|
|
19956
19887
|
interrupted: result.data.interrupted,
|
|
19957
19888
|
lastDevice: result.data.lastDevice,
|
|
@@ -20339,575 +20270,6 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
20339
20270
|
}
|
|
20340
20271
|
};
|
|
20341
20272
|
|
|
20342
|
-
// src/sagas/apply-snapshot-saga.ts
|
|
20343
|
-
var import_promises5 = require("fs/promises");
|
|
20344
|
-
var import_node_path8 = require("path");
|
|
20345
|
-
|
|
20346
|
-
// ../git/dist/sagas/tree.js
|
|
20347
|
-
var import_node_fs4 = require("fs");
|
|
20348
|
-
var fs13 = __toESM(require("fs/promises"), 1);
|
|
20349
|
-
var path16 = __toESM(require("path"), 1);
|
|
20350
|
-
var tar = __toESM(require("tar"), 1);
|
|
20351
|
-
var CaptureTreeSaga = class extends GitSaga {
|
|
20352
|
-
sagaName = "CaptureTreeSaga";
|
|
20353
|
-
tempIndexPath = null;
|
|
20354
|
-
async executeGitOperations(input) {
|
|
20355
|
-
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
20356
|
-
const tmpDir = path16.join(baseDir, ".git", "posthog-code-tmp");
|
|
20357
|
-
await this.step({
|
|
20358
|
-
name: "create_tmp_dir",
|
|
20359
|
-
execute: () => fs13.mkdir(tmpDir, { recursive: true }),
|
|
20360
|
-
rollback: async () => {
|
|
20361
|
-
}
|
|
20362
|
-
});
|
|
20363
|
-
this.tempIndexPath = path16.join(tmpDir, `index-${Date.now()}`);
|
|
20364
|
-
const tempIndexGit = this.git.env({
|
|
20365
|
-
...process.env,
|
|
20366
|
-
GIT_INDEX_FILE: this.tempIndexPath
|
|
20367
|
-
});
|
|
20368
|
-
await this.step({
|
|
20369
|
-
name: "init_temp_index",
|
|
20370
|
-
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
20371
|
-
rollback: async () => {
|
|
20372
|
-
if (this.tempIndexPath) {
|
|
20373
|
-
await fs13.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
20374
|
-
});
|
|
20375
|
-
}
|
|
20376
|
-
}
|
|
20377
|
-
});
|
|
20378
|
-
await this.readOnlyStep("stage_files", () => tempIndexGit.raw(["add", "-A"]));
|
|
20379
|
-
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
20380
|
-
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
20381
|
-
this.log.debug("No changes since last capture", { treeHash });
|
|
20382
|
-
await fs13.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
20383
|
-
});
|
|
20384
|
-
return { snapshot: null, changed: false };
|
|
20385
|
-
}
|
|
20386
|
-
const baseCommit = await this.readOnlyStep("get_base_commit", async () => {
|
|
20387
|
-
try {
|
|
20388
|
-
return await getHeadSha(baseDir, { abortSignal: signal });
|
|
20389
|
-
} catch {
|
|
20390
|
-
return null;
|
|
20391
|
-
}
|
|
20392
|
-
});
|
|
20393
|
-
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
20394
|
-
await fs13.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
20395
|
-
});
|
|
20396
|
-
const snapshot = {
|
|
20397
|
-
treeHash,
|
|
20398
|
-
baseCommit,
|
|
20399
|
-
changes,
|
|
20400
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
20401
|
-
};
|
|
20402
|
-
let createdArchivePath;
|
|
20403
|
-
if (archivePath) {
|
|
20404
|
-
createdArchivePath = await this.createArchive(baseDir, archivePath, changes);
|
|
20405
|
-
}
|
|
20406
|
-
this.log.info("Tree captured", {
|
|
20407
|
-
treeHash,
|
|
20408
|
-
changes: changes.length,
|
|
20409
|
-
archived: !!createdArchivePath
|
|
20410
|
-
});
|
|
20411
|
-
return { snapshot, archivePath: createdArchivePath, changed: true };
|
|
20412
|
-
}
|
|
20413
|
-
async createArchive(baseDir, archivePath, changes) {
|
|
20414
|
-
const filesToArchive = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
20415
|
-
if (filesToArchive.length === 0) {
|
|
20416
|
-
return void 0;
|
|
20417
|
-
}
|
|
20418
|
-
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs4.existsSync)(path16.join(baseDir, f)));
|
|
20419
|
-
if (existingFiles.length === 0) {
|
|
20420
|
-
return void 0;
|
|
20421
|
-
}
|
|
20422
|
-
await this.step({
|
|
20423
|
-
name: "create_archive",
|
|
20424
|
-
execute: async () => {
|
|
20425
|
-
const archiveDir = path16.dirname(archivePath);
|
|
20426
|
-
await fs13.mkdir(archiveDir, { recursive: true });
|
|
20427
|
-
await tar.create({
|
|
20428
|
-
gzip: true,
|
|
20429
|
-
file: archivePath,
|
|
20430
|
-
cwd: baseDir
|
|
20431
|
-
}, existingFiles);
|
|
20432
|
-
},
|
|
20433
|
-
rollback: async () => {
|
|
20434
|
-
await fs13.rm(archivePath, { force: true }).catch(() => {
|
|
20435
|
-
});
|
|
20436
|
-
}
|
|
20437
|
-
});
|
|
20438
|
-
return archivePath;
|
|
20439
|
-
}
|
|
20440
|
-
async getChanges(git, fromRef, toRef) {
|
|
20441
|
-
if (!fromRef) {
|
|
20442
|
-
const stdout2 = await git.raw(["ls-tree", "-r", "--name-only", toRef]);
|
|
20443
|
-
return stdout2.split("\n").filter((p) => p.trim()).map((p) => ({ path: p, status: "A" }));
|
|
20444
|
-
}
|
|
20445
|
-
const stdout = await git.raw([
|
|
20446
|
-
"diff-tree",
|
|
20447
|
-
"-r",
|
|
20448
|
-
"--name-status",
|
|
20449
|
-
fromRef,
|
|
20450
|
-
toRef
|
|
20451
|
-
]);
|
|
20452
|
-
const changes = [];
|
|
20453
|
-
for (const line of stdout.split("\n")) {
|
|
20454
|
-
if (!line.trim())
|
|
20455
|
-
continue;
|
|
20456
|
-
const [status, filePath] = line.split(" ");
|
|
20457
|
-
if (!filePath)
|
|
20458
|
-
continue;
|
|
20459
|
-
let normalizedStatus;
|
|
20460
|
-
if (status === "D") {
|
|
20461
|
-
normalizedStatus = "D";
|
|
20462
|
-
} else if (status === "A") {
|
|
20463
|
-
normalizedStatus = "A";
|
|
20464
|
-
} else {
|
|
20465
|
-
normalizedStatus = "M";
|
|
20466
|
-
}
|
|
20467
|
-
changes.push({ path: filePath, status: normalizedStatus });
|
|
20468
|
-
}
|
|
20469
|
-
return changes;
|
|
20470
|
-
}
|
|
20471
|
-
};
|
|
20472
|
-
var ApplyTreeSaga = class extends GitSaga {
|
|
20473
|
-
sagaName = "ApplyTreeSaga";
|
|
20474
|
-
originalHead = null;
|
|
20475
|
-
originalBranch = null;
|
|
20476
|
-
extractedFiles = [];
|
|
20477
|
-
fileBackups = /* @__PURE__ */ new Map();
|
|
20478
|
-
async executeGitOperations(input) {
|
|
20479
|
-
const { baseDir, treeHash, baseCommit, changes, archivePath } = input;
|
|
20480
|
-
const headInfo = await this.readOnlyStep("get_current_head", async () => {
|
|
20481
|
-
let head = null;
|
|
20482
|
-
let branch = null;
|
|
20483
|
-
try {
|
|
20484
|
-
head = await this.git.revparse(["HEAD"]);
|
|
20485
|
-
} catch {
|
|
20486
|
-
head = null;
|
|
20487
|
-
}
|
|
20488
|
-
try {
|
|
20489
|
-
branch = await this.git.raw(["symbolic-ref", "--short", "HEAD"]);
|
|
20490
|
-
} catch {
|
|
20491
|
-
branch = null;
|
|
20492
|
-
}
|
|
20493
|
-
return { head, branch };
|
|
20494
|
-
});
|
|
20495
|
-
this.originalHead = headInfo.head;
|
|
20496
|
-
this.originalBranch = headInfo.branch;
|
|
20497
|
-
let checkoutPerformed = false;
|
|
20498
|
-
if (baseCommit && baseCommit !== this.originalHead) {
|
|
20499
|
-
await this.readOnlyStep("check_working_tree", async () => {
|
|
20500
|
-
const status = await this.git.status();
|
|
20501
|
-
if (!status.isClean()) {
|
|
20502
|
-
const changedFiles = status.modified.length + status.staged.length + status.deleted.length;
|
|
20503
|
-
throw new Error(`Cannot apply tree: ${changedFiles} uncommitted change(s) exist. Commit or stash your changes first.`);
|
|
20504
|
-
}
|
|
20505
|
-
});
|
|
20506
|
-
await this.step({
|
|
20507
|
-
name: "checkout_base",
|
|
20508
|
-
execute: async () => {
|
|
20509
|
-
await this.git.checkout(baseCommit);
|
|
20510
|
-
checkoutPerformed = true;
|
|
20511
|
-
this.log.warn("Applied tree from different commit - now in detached HEAD state", {
|
|
20512
|
-
originalHead: this.originalHead,
|
|
20513
|
-
originalBranch: this.originalBranch,
|
|
20514
|
-
baseCommit
|
|
20515
|
-
});
|
|
20516
|
-
},
|
|
20517
|
-
rollback: async () => {
|
|
20518
|
-
try {
|
|
20519
|
-
if (this.originalBranch) {
|
|
20520
|
-
await this.git.checkout(this.originalBranch);
|
|
20521
|
-
} else if (this.originalHead) {
|
|
20522
|
-
await this.git.checkout(this.originalHead);
|
|
20523
|
-
}
|
|
20524
|
-
} catch (error) {
|
|
20525
|
-
this.log.warn("Failed to rollback checkout", { error });
|
|
20526
|
-
}
|
|
20527
|
-
}
|
|
20528
|
-
});
|
|
20529
|
-
}
|
|
20530
|
-
if (archivePath) {
|
|
20531
|
-
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
20532
|
-
await this.readOnlyStep("backup_existing_files", async () => {
|
|
20533
|
-
for (const filePath of filesToExtract) {
|
|
20534
|
-
const fullPath = path16.join(baseDir, filePath);
|
|
20535
|
-
try {
|
|
20536
|
-
const content = await fs13.readFile(fullPath);
|
|
20537
|
-
this.fileBackups.set(filePath, content);
|
|
20538
|
-
} catch {
|
|
20539
|
-
}
|
|
20540
|
-
}
|
|
20541
|
-
});
|
|
20542
|
-
await this.step({
|
|
20543
|
-
name: "extract_archive",
|
|
20544
|
-
execute: async () => {
|
|
20545
|
-
await tar.extract({
|
|
20546
|
-
file: archivePath,
|
|
20547
|
-
cwd: baseDir
|
|
20548
|
-
});
|
|
20549
|
-
this.extractedFiles = filesToExtract;
|
|
20550
|
-
},
|
|
20551
|
-
rollback: async () => {
|
|
20552
|
-
for (const filePath of this.extractedFiles) {
|
|
20553
|
-
const fullPath = path16.join(baseDir, filePath);
|
|
20554
|
-
const backup = this.fileBackups.get(filePath);
|
|
20555
|
-
if (backup) {
|
|
20556
|
-
const dir = path16.dirname(fullPath);
|
|
20557
|
-
await fs13.mkdir(dir, { recursive: true }).catch(() => {
|
|
20558
|
-
});
|
|
20559
|
-
await fs13.writeFile(fullPath, backup).catch(() => {
|
|
20560
|
-
});
|
|
20561
|
-
} else {
|
|
20562
|
-
await fs13.rm(fullPath, { force: true }).catch(() => {
|
|
20563
|
-
});
|
|
20564
|
-
}
|
|
20565
|
-
}
|
|
20566
|
-
}
|
|
20567
|
-
});
|
|
20568
|
-
}
|
|
20569
|
-
for (const change of changes.filter((c) => c.status === "D")) {
|
|
20570
|
-
const fullPath = path16.join(baseDir, change.path);
|
|
20571
|
-
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
20572
|
-
try {
|
|
20573
|
-
return await fs13.readFile(fullPath);
|
|
20574
|
-
} catch {
|
|
20575
|
-
return null;
|
|
20576
|
-
}
|
|
20577
|
-
});
|
|
20578
|
-
await this.step({
|
|
20579
|
-
name: `delete_${change.path}`,
|
|
20580
|
-
execute: async () => {
|
|
20581
|
-
await fs13.rm(fullPath, { force: true });
|
|
20582
|
-
this.log.debug(`Deleted file: ${change.path}`);
|
|
20583
|
-
},
|
|
20584
|
-
rollback: async () => {
|
|
20585
|
-
if (backupContent) {
|
|
20586
|
-
const dir = path16.dirname(fullPath);
|
|
20587
|
-
await fs13.mkdir(dir, { recursive: true }).catch(() => {
|
|
20588
|
-
});
|
|
20589
|
-
await fs13.writeFile(fullPath, backupContent).catch(() => {
|
|
20590
|
-
});
|
|
20591
|
-
}
|
|
20592
|
-
}
|
|
20593
|
-
});
|
|
20594
|
-
}
|
|
20595
|
-
const deletedCount = changes.filter((c) => c.status === "D").length;
|
|
20596
|
-
this.log.info("Tree applied", {
|
|
20597
|
-
treeHash,
|
|
20598
|
-
totalChanges: changes.length,
|
|
20599
|
-
deletedFiles: deletedCount,
|
|
20600
|
-
checkoutPerformed
|
|
20601
|
-
});
|
|
20602
|
-
return { treeHash, checkoutPerformed };
|
|
20603
|
-
}
|
|
20604
|
-
};
|
|
20605
|
-
|
|
20606
|
-
// src/sagas/apply-snapshot-saga.ts
|
|
20607
|
-
var ApplySnapshotSaga = class extends Saga {
|
|
20608
|
-
sagaName = "ApplySnapshotSaga";
|
|
20609
|
-
archivePath = null;
|
|
20610
|
-
async execute(input) {
|
|
20611
|
-
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
20612
|
-
const tmpDir = (0, import_node_path8.join)(repositoryPath, ".posthog", "tmp");
|
|
20613
|
-
if (!snapshot.archiveUrl) {
|
|
20614
|
-
throw new Error("Cannot apply snapshot: no archive URL");
|
|
20615
|
-
}
|
|
20616
|
-
const archiveUrl = snapshot.archiveUrl;
|
|
20617
|
-
try {
|
|
20618
|
-
await this.step({
|
|
20619
|
-
name: "create_tmp_dir",
|
|
20620
|
-
execute: () => (0, import_promises5.mkdir)(tmpDir, { recursive: true }),
|
|
20621
|
-
rollback: async () => {
|
|
20622
|
-
}
|
|
20623
|
-
});
|
|
20624
|
-
const archivePath = (0, import_node_path8.join)(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
20625
|
-
this.archivePath = archivePath;
|
|
20626
|
-
await this.step({
|
|
20627
|
-
name: "download_archive",
|
|
20628
|
-
execute: async () => {
|
|
20629
|
-
const arrayBuffer = await apiClient.downloadArtifact(
|
|
20630
|
-
taskId,
|
|
20631
|
-
runId,
|
|
20632
|
-
archiveUrl
|
|
20633
|
-
);
|
|
20634
|
-
if (!arrayBuffer) {
|
|
20635
|
-
throw new Error("Failed to download archive");
|
|
20636
|
-
}
|
|
20637
|
-
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
20638
|
-
const binaryContent = Buffer.from(base64Content, "base64");
|
|
20639
|
-
await (0, import_promises5.writeFile)(archivePath, binaryContent);
|
|
20640
|
-
this.log.info("Tree archive downloaded", {
|
|
20641
|
-
treeHash: snapshot.treeHash,
|
|
20642
|
-
snapshotBytes: binaryContent.byteLength,
|
|
20643
|
-
snapshotWireBytes: arrayBuffer.byteLength,
|
|
20644
|
-
totalBytes: binaryContent.byteLength,
|
|
20645
|
-
totalWireBytes: arrayBuffer.byteLength
|
|
20646
|
-
});
|
|
20647
|
-
},
|
|
20648
|
-
rollback: async () => {
|
|
20649
|
-
if (this.archivePath) {
|
|
20650
|
-
await (0, import_promises5.rm)(this.archivePath, { force: true }).catch(() => {
|
|
20651
|
-
});
|
|
20652
|
-
}
|
|
20653
|
-
}
|
|
20654
|
-
});
|
|
20655
|
-
const gitApplySaga = new ApplyTreeSaga(this.log);
|
|
20656
|
-
const applyResult = await gitApplySaga.run({
|
|
20657
|
-
baseDir: repositoryPath,
|
|
20658
|
-
treeHash: snapshot.treeHash,
|
|
20659
|
-
baseCommit: snapshot.baseCommit,
|
|
20660
|
-
changes: snapshot.changes,
|
|
20661
|
-
archivePath: this.archivePath
|
|
20662
|
-
});
|
|
20663
|
-
if (!applyResult.success) {
|
|
20664
|
-
throw new Error(`Failed to apply tree: ${applyResult.error}`);
|
|
20665
|
-
}
|
|
20666
|
-
this.log.info("Tree snapshot applied", {
|
|
20667
|
-
treeHash: snapshot.treeHash,
|
|
20668
|
-
totalChanges: snapshot.changes.length,
|
|
20669
|
-
deletedFiles: snapshot.changes.filter((c) => c.status === "D").length
|
|
20670
|
-
});
|
|
20671
|
-
return { treeHash: snapshot.treeHash };
|
|
20672
|
-
} finally {
|
|
20673
|
-
if (this.archivePath) {
|
|
20674
|
-
await (0, import_promises5.rm)(this.archivePath, { force: true }).catch(() => {
|
|
20675
|
-
});
|
|
20676
|
-
}
|
|
20677
|
-
await this.removeTmpDirIfEmpty(tmpDir);
|
|
20678
|
-
this.archivePath = null;
|
|
20679
|
-
}
|
|
20680
|
-
}
|
|
20681
|
-
async removeTmpDirIfEmpty(tmpDir) {
|
|
20682
|
-
const entries = await (0, import_promises5.readdir)(tmpDir).catch(() => null);
|
|
20683
|
-
if (!entries || entries.length > 0) {
|
|
20684
|
-
return;
|
|
20685
|
-
}
|
|
20686
|
-
await (0, import_promises5.rmdir)(tmpDir).catch(() => {
|
|
20687
|
-
});
|
|
20688
|
-
}
|
|
20689
|
-
};
|
|
20690
|
-
|
|
20691
|
-
// src/sagas/capture-tree-saga.ts
|
|
20692
|
-
var import_node_fs5 = require("fs");
|
|
20693
|
-
var import_promises6 = require("fs/promises");
|
|
20694
|
-
var import_node_path9 = require("path");
|
|
20695
|
-
var CaptureTreeSaga2 = class extends Saga {
|
|
20696
|
-
sagaName = "CaptureTreeSaga";
|
|
20697
|
-
async execute(input) {
|
|
20698
|
-
const {
|
|
20699
|
-
repositoryPath,
|
|
20700
|
-
lastTreeHash,
|
|
20701
|
-
interrupted,
|
|
20702
|
-
apiClient,
|
|
20703
|
-
taskId,
|
|
20704
|
-
runId
|
|
20705
|
-
} = input;
|
|
20706
|
-
const tmpDir = (0, import_node_path9.join)(repositoryPath, ".posthog", "tmp");
|
|
20707
|
-
if ((0, import_node_fs5.existsSync)((0, import_node_path9.join)(repositoryPath, ".gitmodules"))) {
|
|
20708
|
-
this.log.warn(
|
|
20709
|
-
"Repository has submodules - snapshot may not capture submodule state"
|
|
20710
|
-
);
|
|
20711
|
-
}
|
|
20712
|
-
const shouldArchive = !!apiClient;
|
|
20713
|
-
const archivePath = shouldArchive ? (0, import_node_path9.join)(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
20714
|
-
try {
|
|
20715
|
-
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
20716
|
-
const captureResult = await gitCaptureSaga.run({
|
|
20717
|
-
baseDir: repositoryPath,
|
|
20718
|
-
lastTreeHash,
|
|
20719
|
-
archivePath
|
|
20720
|
-
});
|
|
20721
|
-
if (!captureResult.success) {
|
|
20722
|
-
throw new Error(`Failed to capture tree: ${captureResult.error}`);
|
|
20723
|
-
}
|
|
20724
|
-
const {
|
|
20725
|
-
snapshot: gitSnapshot,
|
|
20726
|
-
archivePath: createdArchivePath,
|
|
20727
|
-
changed
|
|
20728
|
-
} = captureResult.data;
|
|
20729
|
-
if (!changed || !gitSnapshot) {
|
|
20730
|
-
this.log.debug("No changes since last capture", { lastTreeHash });
|
|
20731
|
-
return { snapshot: null, newTreeHash: lastTreeHash };
|
|
20732
|
-
}
|
|
20733
|
-
let archiveUrl;
|
|
20734
|
-
if (apiClient && createdArchivePath) {
|
|
20735
|
-
try {
|
|
20736
|
-
archiveUrl = await this.uploadArchive(
|
|
20737
|
-
createdArchivePath,
|
|
20738
|
-
gitSnapshot.treeHash,
|
|
20739
|
-
apiClient,
|
|
20740
|
-
taskId,
|
|
20741
|
-
runId
|
|
20742
|
-
);
|
|
20743
|
-
} finally {
|
|
20744
|
-
await (0, import_promises6.rm)(createdArchivePath, { force: true }).catch(() => {
|
|
20745
|
-
});
|
|
20746
|
-
}
|
|
20747
|
-
}
|
|
20748
|
-
const snapshot = {
|
|
20749
|
-
treeHash: gitSnapshot.treeHash,
|
|
20750
|
-
baseCommit: gitSnapshot.baseCommit,
|
|
20751
|
-
changes: gitSnapshot.changes,
|
|
20752
|
-
timestamp: gitSnapshot.timestamp,
|
|
20753
|
-
interrupted,
|
|
20754
|
-
archiveUrl
|
|
20755
|
-
};
|
|
20756
|
-
this.log.info("Tree captured", {
|
|
20757
|
-
treeHash: snapshot.treeHash,
|
|
20758
|
-
changes: snapshot.changes.length,
|
|
20759
|
-
interrupted,
|
|
20760
|
-
archiveUrl
|
|
20761
|
-
});
|
|
20762
|
-
return { snapshot, newTreeHash: snapshot.treeHash };
|
|
20763
|
-
} finally {
|
|
20764
|
-
if (archivePath) {
|
|
20765
|
-
await (0, import_promises6.rm)(archivePath, { force: true }).catch(() => {
|
|
20766
|
-
});
|
|
20767
|
-
}
|
|
20768
|
-
await this.removeTmpDirIfEmpty(tmpDir);
|
|
20769
|
-
}
|
|
20770
|
-
}
|
|
20771
|
-
async uploadArchive(archivePath, treeHash, apiClient, taskId, runId) {
|
|
20772
|
-
const archiveUrl = await this.step({
|
|
20773
|
-
name: "upload_archive",
|
|
20774
|
-
execute: async () => {
|
|
20775
|
-
const archiveContent = await (0, import_promises6.readFile)(archivePath);
|
|
20776
|
-
const base64Content = archiveContent.toString("base64");
|
|
20777
|
-
const snapshotBytes = archiveContent.byteLength;
|
|
20778
|
-
const snapshotWireBytes = Buffer.byteLength(base64Content, "utf-8");
|
|
20779
|
-
const artifacts = await apiClient.uploadTaskArtifacts(taskId, runId, [
|
|
20780
|
-
{
|
|
20781
|
-
name: `trees/${treeHash}.tar.gz`,
|
|
20782
|
-
type: "tree_snapshot",
|
|
20783
|
-
content: base64Content,
|
|
20784
|
-
content_type: "application/gzip"
|
|
20785
|
-
}
|
|
20786
|
-
]);
|
|
20787
|
-
const uploadedArtifact = artifacts[0];
|
|
20788
|
-
if (uploadedArtifact?.storage_path) {
|
|
20789
|
-
this.log.info("Tree archive uploaded", {
|
|
20790
|
-
storagePath: uploadedArtifact.storage_path,
|
|
20791
|
-
treeHash,
|
|
20792
|
-
snapshotBytes,
|
|
20793
|
-
snapshotWireBytes,
|
|
20794
|
-
totalBytes: snapshotBytes,
|
|
20795
|
-
totalWireBytes: snapshotWireBytes
|
|
20796
|
-
});
|
|
20797
|
-
return uploadedArtifact.storage_path;
|
|
20798
|
-
}
|
|
20799
|
-
return void 0;
|
|
20800
|
-
},
|
|
20801
|
-
rollback: async () => {
|
|
20802
|
-
await (0, import_promises6.rm)(archivePath, { force: true }).catch(() => {
|
|
20803
|
-
});
|
|
20804
|
-
}
|
|
20805
|
-
});
|
|
20806
|
-
return archiveUrl;
|
|
20807
|
-
}
|
|
20808
|
-
async removeTmpDirIfEmpty(tmpDir) {
|
|
20809
|
-
const entries = await (0, import_promises6.readdir)(tmpDir).catch(() => null);
|
|
20810
|
-
if (!entries || entries.length > 0) {
|
|
20811
|
-
return;
|
|
20812
|
-
}
|
|
20813
|
-
await (0, import_promises6.rmdir)(tmpDir).catch(() => {
|
|
20814
|
-
});
|
|
20815
|
-
}
|
|
20816
|
-
};
|
|
20817
|
-
|
|
20818
|
-
// src/tree-tracker.ts
|
|
20819
|
-
var TreeTracker = class {
|
|
20820
|
-
repositoryPath;
|
|
20821
|
-
taskId;
|
|
20822
|
-
runId;
|
|
20823
|
-
apiClient;
|
|
20824
|
-
logger;
|
|
20825
|
-
lastTreeHash = null;
|
|
20826
|
-
constructor(config) {
|
|
20827
|
-
this.repositoryPath = config.repositoryPath;
|
|
20828
|
-
this.taskId = config.taskId;
|
|
20829
|
-
this.runId = config.runId;
|
|
20830
|
-
this.apiClient = config.apiClient;
|
|
20831
|
-
this.logger = config.logger || new Logger({ debug: false, prefix: "[TreeTracker]" });
|
|
20832
|
-
}
|
|
20833
|
-
/**
|
|
20834
|
-
* Capture current working tree state as a snapshot.
|
|
20835
|
-
* Uses a temporary index to avoid modifying user's staging area.
|
|
20836
|
-
* Uses Saga pattern for atomic operation with automatic cleanup on failure.
|
|
20837
|
-
*/
|
|
20838
|
-
async captureTree(options) {
|
|
20839
|
-
const saga = new CaptureTreeSaga2(this.logger);
|
|
20840
|
-
const result = await saga.run({
|
|
20841
|
-
repositoryPath: this.repositoryPath,
|
|
20842
|
-
taskId: this.taskId,
|
|
20843
|
-
runId: this.runId,
|
|
20844
|
-
apiClient: this.apiClient,
|
|
20845
|
-
lastTreeHash: this.lastTreeHash,
|
|
20846
|
-
interrupted: options?.interrupted
|
|
20847
|
-
});
|
|
20848
|
-
if (!result.success) {
|
|
20849
|
-
this.logger.error("Failed to capture tree", {
|
|
20850
|
-
error: result.error,
|
|
20851
|
-
failedStep: result.failedStep
|
|
20852
|
-
});
|
|
20853
|
-
throw new Error(
|
|
20854
|
-
`Failed to capture tree at step '${result.failedStep}': ${result.error}`
|
|
20855
|
-
);
|
|
20856
|
-
}
|
|
20857
|
-
if (result.data.newTreeHash !== null) {
|
|
20858
|
-
this.lastTreeHash = result.data.newTreeHash;
|
|
20859
|
-
}
|
|
20860
|
-
return result.data.snapshot;
|
|
20861
|
-
}
|
|
20862
|
-
/**
|
|
20863
|
-
* Download and apply a tree snapshot.
|
|
20864
|
-
* Uses Saga pattern for atomic operation with rollback on failure.
|
|
20865
|
-
*/
|
|
20866
|
-
async applyTreeSnapshot(snapshot) {
|
|
20867
|
-
if (!this.apiClient) {
|
|
20868
|
-
throw new Error("Cannot apply snapshot: API client not configured");
|
|
20869
|
-
}
|
|
20870
|
-
if (!snapshot.archiveUrl) {
|
|
20871
|
-
this.logger.warn("Cannot apply snapshot: no archive URL", {
|
|
20872
|
-
treeHash: snapshot.treeHash,
|
|
20873
|
-
changes: snapshot.changes.length
|
|
20874
|
-
});
|
|
20875
|
-
throw new Error("Cannot apply snapshot: no archive URL");
|
|
20876
|
-
}
|
|
20877
|
-
const saga = new ApplySnapshotSaga(this.logger);
|
|
20878
|
-
const result = await saga.run({
|
|
20879
|
-
snapshot,
|
|
20880
|
-
repositoryPath: this.repositoryPath,
|
|
20881
|
-
apiClient: this.apiClient,
|
|
20882
|
-
taskId: this.taskId,
|
|
20883
|
-
runId: this.runId
|
|
20884
|
-
});
|
|
20885
|
-
if (!result.success) {
|
|
20886
|
-
this.logger.error("Failed to apply tree snapshot", {
|
|
20887
|
-
error: result.error,
|
|
20888
|
-
failedStep: result.failedStep,
|
|
20889
|
-
treeHash: snapshot.treeHash
|
|
20890
|
-
});
|
|
20891
|
-
throw new Error(
|
|
20892
|
-
`Failed to apply snapshot at step '${result.failedStep}': ${result.error}`
|
|
20893
|
-
);
|
|
20894
|
-
}
|
|
20895
|
-
this.lastTreeHash = result.data.treeHash;
|
|
20896
|
-
}
|
|
20897
|
-
/**
|
|
20898
|
-
* Get the last captured tree hash.
|
|
20899
|
-
*/
|
|
20900
|
-
getLastTreeHash() {
|
|
20901
|
-
return this.lastTreeHash;
|
|
20902
|
-
}
|
|
20903
|
-
/**
|
|
20904
|
-
* Set the last tree hash (used when resuming).
|
|
20905
|
-
*/
|
|
20906
|
-
setLastTreeHash(hash) {
|
|
20907
|
-
this.lastTreeHash = hash;
|
|
20908
|
-
}
|
|
20909
|
-
};
|
|
20910
|
-
|
|
20911
20273
|
// src/server/cloud-prompt.ts
|
|
20912
20274
|
function normalizeCloudPromptContent(content) {
|
|
20913
20275
|
if (typeof content === "string") {
|
|
@@ -21392,7 +20754,6 @@ var AgentServer = class {
|
|
|
21392
20754
|
});
|
|
21393
20755
|
this.logger.debug("Resume state loaded", {
|
|
21394
20756
|
conversationTurns: this.resumeState.conversation.length,
|
|
21395
|
-
hasSnapshot: !!this.resumeState.latestSnapshot,
|
|
21396
20757
|
hasGitCheckpoint: !!this.resumeState.latestGitCheckpoint,
|
|
21397
20758
|
gitCheckpointBranch: this.resumeState.latestGitCheckpoint?.branch ?? null,
|
|
21398
20759
|
logEntries: this.resumeState.logEntryCount
|
|
@@ -21638,13 +20999,6 @@ var AgentServer = class {
|
|
|
21638
20999
|
getApiKey: () => this.config.apiKey,
|
|
21639
21000
|
userAgent: `posthog/cloud.hog.dev; version: ${this.config.version ?? package_default.version}`
|
|
21640
21001
|
});
|
|
21641
|
-
const treeTracker = this.config.repositoryPath ? new TreeTracker({
|
|
21642
|
-
repositoryPath: this.config.repositoryPath,
|
|
21643
|
-
taskId: payload.task_id,
|
|
21644
|
-
runId: payload.run_id,
|
|
21645
|
-
apiClient: posthogAPI,
|
|
21646
|
-
logger: new Logger({ debug: true, prefix: "[TreeTracker]" })
|
|
21647
|
-
}) : null;
|
|
21648
21002
|
const logWriter = new SessionLogWriter({
|
|
21649
21003
|
posthogAPI,
|
|
21650
21004
|
logger: new Logger({ debug: true, prefix: "[SessionLogWriter]" })
|
|
@@ -21737,7 +21091,6 @@ var AgentServer = class {
|
|
|
21737
21091
|
acpSessionId,
|
|
21738
21092
|
acpConnection,
|
|
21739
21093
|
clientConnection,
|
|
21740
|
-
treeTracker,
|
|
21741
21094
|
sseController,
|
|
21742
21095
|
deviceInfo,
|
|
21743
21096
|
logWriter,
|
|
@@ -21864,31 +21217,7 @@ var AgentServer = class {
|
|
|
21864
21217
|
const conversationSummary = formatConversationForResume(
|
|
21865
21218
|
this.resumeState.conversation
|
|
21866
21219
|
);
|
|
21867
|
-
let
|
|
21868
|
-
if (this.resumeState.latestSnapshot?.archiveUrl && this.config.repositoryPath && this.posthogAPI) {
|
|
21869
|
-
try {
|
|
21870
|
-
const treeTracker = new TreeTracker({
|
|
21871
|
-
repositoryPath: this.config.repositoryPath,
|
|
21872
|
-
taskId: payload.task_id,
|
|
21873
|
-
runId: payload.run_id,
|
|
21874
|
-
apiClient: this.posthogAPI,
|
|
21875
|
-
logger: this.logger.child("TreeTracker")
|
|
21876
|
-
});
|
|
21877
|
-
await treeTracker.applyTreeSnapshot(this.resumeState.latestSnapshot);
|
|
21878
|
-
treeTracker.setLastTreeHash(this.resumeState.latestSnapshot.treeHash);
|
|
21879
|
-
snapshotApplied = true;
|
|
21880
|
-
this.logger.info("Tree snapshot applied", {
|
|
21881
|
-
treeHash: this.resumeState.latestSnapshot.treeHash,
|
|
21882
|
-
changes: this.resumeState.latestSnapshot.changes?.length ?? 0,
|
|
21883
|
-
hasArchiveUrl: !!this.resumeState.latestSnapshot.archiveUrl
|
|
21884
|
-
});
|
|
21885
|
-
} catch (error) {
|
|
21886
|
-
this.logger.warn("Failed to apply tree snapshot", {
|
|
21887
|
-
error: error instanceof Error ? error.message : String(error),
|
|
21888
|
-
treeHash: this.resumeState.latestSnapshot.treeHash
|
|
21889
|
-
});
|
|
21890
|
-
}
|
|
21891
|
-
}
|
|
21220
|
+
let checkpointApplied = false;
|
|
21892
21221
|
if (this.resumeState.latestGitCheckpoint && this.config.repositoryPath && this.posthogAPI) {
|
|
21893
21222
|
try {
|
|
21894
21223
|
const checkpointTracker = new HandoffCheckpointTracker({
|
|
@@ -21901,6 +21230,7 @@ var AgentServer = class {
|
|
|
21901
21230
|
const metrics = await checkpointTracker.applyFromHandoff(
|
|
21902
21231
|
this.resumeState.latestGitCheckpoint
|
|
21903
21232
|
);
|
|
21233
|
+
checkpointApplied = true;
|
|
21904
21234
|
this.logger.info("Git checkpoint applied", {
|
|
21905
21235
|
branch: this.resumeState.latestGitCheckpoint.branch,
|
|
21906
21236
|
head: this.resumeState.latestGitCheckpoint.head,
|
|
@@ -21916,7 +21246,7 @@ var AgentServer = class {
|
|
|
21916
21246
|
}
|
|
21917
21247
|
}
|
|
21918
21248
|
const pendingUserPrompt = await this.getPendingUserPrompt(taskRun);
|
|
21919
|
-
const sandboxContext =
|
|
21249
|
+
const sandboxContext = checkpointApplied ? `The workspace environment (all files, packages, and code changes) has been fully restored from the latest checkpoint.` : `The workspace from the previous session was not restored from a checkpoint, so you are starting with a fresh environment. Your conversation history is fully preserved below.`;
|
|
21920
21250
|
let resumePromptBlocks;
|
|
21921
21251
|
if (pendingUserPrompt?.length) {
|
|
21922
21252
|
resumePromptBlocks = [
|
|
@@ -21957,7 +21287,7 @@ Continue from where you left off. The user is waiting for your response.`
|
|
|
21957
21287
|
conversationTurns: this.resumeState.conversation.length,
|
|
21958
21288
|
promptLength: promptBlocksToText(resumePromptBlocks).length,
|
|
21959
21289
|
hasPendingUserMessage: !!pendingUserPrompt?.length,
|
|
21960
|
-
|
|
21290
|
+
checkpointApplied,
|
|
21961
21291
|
hasGitCheckpoint: !!this.resumeState.latestGitCheckpoint,
|
|
21962
21292
|
gitCheckpointBranch: this.resumeState.latestGitCheckpoint?.branch ?? null
|
|
21963
21293
|
});
|
|
@@ -22103,23 +21433,23 @@ Continue from where you left off. The user is waiting for your response.`
|
|
|
22103
21433
|
throw new Error(`Failed to download artifact ${artifact.name}`);
|
|
22104
21434
|
}
|
|
22105
21435
|
const safeName = this.getSafeArtifactName(artifact.name);
|
|
22106
|
-
const artifactDir = (0,
|
|
21436
|
+
const artifactDir = (0, import_node_path8.join)(
|
|
22107
21437
|
this.config.repositoryPath ?? "/tmp/workspace",
|
|
22108
21438
|
".posthog",
|
|
22109
21439
|
"attachments",
|
|
22110
21440
|
runId,
|
|
22111
21441
|
artifact.id ?? safeName
|
|
22112
21442
|
);
|
|
22113
|
-
await (0,
|
|
22114
|
-
const artifactPath = (0,
|
|
22115
|
-
await (0,
|
|
21443
|
+
await (0, import_promises5.mkdir)(artifactDir, { recursive: true });
|
|
21444
|
+
const artifactPath = (0, import_node_path8.join)(artifactDir, safeName);
|
|
21445
|
+
await (0, import_promises5.writeFile)(artifactPath, Buffer.from(data));
|
|
22116
21446
|
return resourceLink((0, import_node_url2.pathToFileURL)(artifactPath).toString(), artifact.name, {
|
|
22117
21447
|
...artifact.content_type ? { mimeType: artifact.content_type } : {},
|
|
22118
21448
|
...typeof artifact.size === "number" ? { size: artifact.size } : {}
|
|
22119
21449
|
});
|
|
22120
21450
|
}
|
|
22121
21451
|
getSafeArtifactName(name2) {
|
|
22122
|
-
const baseName = (0,
|
|
21452
|
+
const baseName = (0, import_node_path8.basename)(name2).trim();
|
|
22123
21453
|
const normalizedName = baseName.replace(/[^\w.-]/g, "_");
|
|
22124
21454
|
return normalizedName.length > 0 ? normalizedName : "attachment";
|
|
22125
21455
|
}
|
|
@@ -22467,8 +21797,8 @@ ${attributionInstructions}
|
|
|
22467
21797
|
const meta = params.update?._meta?.claudeCode;
|
|
22468
21798
|
const toolName = meta?.toolName;
|
|
22469
21799
|
const toolResponse = meta?.toolResponse;
|
|
22470
|
-
if ((toolName === "Write" || toolName === "Edit") && toolResponse?.filePath) {
|
|
22471
|
-
await this.
|
|
21800
|
+
if ((toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit" || toolName === "Delete" || toolName === "Move") && toolResponse?.filePath) {
|
|
21801
|
+
await this.captureCheckpointState();
|
|
22472
21802
|
}
|
|
22473
21803
|
if (toolName && (toolName.includes("Bash") || toolName.includes("bash"))) {
|
|
22474
21804
|
this.detectAndAttachPrUrl(payload, params.update);
|
|
@@ -22630,14 +21960,9 @@ ${attributionInstructions}
|
|
|
22630
21960
|
if (!this.session) return;
|
|
22631
21961
|
this.logger.debug("Cleaning up session");
|
|
22632
21962
|
try {
|
|
22633
|
-
await this.
|
|
22634
|
-
} catch (error) {
|
|
22635
|
-
this.logger.error("Failed to capture handoff checkpoint", error);
|
|
22636
|
-
}
|
|
22637
|
-
try {
|
|
22638
|
-
await this.captureTreeState();
|
|
21963
|
+
await this.captureCheckpointState(this.session.pendingHandoffGitState);
|
|
22639
21964
|
} catch (error) {
|
|
22640
|
-
this.logger.error("Failed to capture final
|
|
21965
|
+
this.logger.error("Failed to capture final checkpoint state", error);
|
|
22641
21966
|
}
|
|
22642
21967
|
try {
|
|
22643
21968
|
await this.session.logWriter.flush(this.session.payload.run_id, {
|
|
@@ -22665,41 +21990,13 @@ ${attributionInstructions}
|
|
|
22665
21990
|
this.lastReportedBranch = null;
|
|
22666
21991
|
this.session = null;
|
|
22667
21992
|
}
|
|
22668
|
-
async
|
|
22669
|
-
if (!this.session
|
|
22670
|
-
try {
|
|
22671
|
-
const snapshot = await this.session.treeTracker.captureTree({});
|
|
22672
|
-
if (snapshot) {
|
|
22673
|
-
const snapshotWithDevice = {
|
|
22674
|
-
...snapshot,
|
|
22675
|
-
device: this.session.deviceInfo
|
|
22676
|
-
};
|
|
22677
|
-
const notification = {
|
|
22678
|
-
jsonrpc: "2.0",
|
|
22679
|
-
method: POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT,
|
|
22680
|
-
params: snapshotWithDevice
|
|
22681
|
-
};
|
|
22682
|
-
this.broadcastEvent({
|
|
22683
|
-
type: "notification",
|
|
22684
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22685
|
-
notification
|
|
22686
|
-
});
|
|
22687
|
-
this.session.logWriter.appendRawLine(
|
|
22688
|
-
this.session.payload.run_id,
|
|
22689
|
-
JSON.stringify(notification)
|
|
22690
|
-
);
|
|
22691
|
-
}
|
|
22692
|
-
} catch (error) {
|
|
22693
|
-
this.logger.error("Failed to capture tree state", error);
|
|
22694
|
-
}
|
|
22695
|
-
}
|
|
22696
|
-
async captureHandoffCheckpoint() {
|
|
22697
|
-
if (!this.session?.treeTracker || !this.session.pendingHandoffGitState) {
|
|
21993
|
+
async captureCheckpointState(localGitState) {
|
|
21994
|
+
if (!this.session || !this.config.repositoryPath) {
|
|
22698
21995
|
return;
|
|
22699
21996
|
}
|
|
22700
21997
|
if (!this.posthogAPI) {
|
|
22701
21998
|
this.logger.warn(
|
|
22702
|
-
"Skipping
|
|
21999
|
+
"Skipping checkpoint capture: PostHog API client is not configured"
|
|
22703
22000
|
);
|
|
22704
22001
|
return;
|
|
22705
22002
|
}
|
|
@@ -22710,9 +22007,7 @@ ${attributionInstructions}
|
|
|
22710
22007
|
apiClient: this.posthogAPI,
|
|
22711
22008
|
logger: this.logger.child("HandoffCheckpoint")
|
|
22712
22009
|
});
|
|
22713
|
-
const checkpoint = await tracker.captureForHandoff(
|
|
22714
|
-
this.session.pendingHandoffGitState
|
|
22715
|
-
);
|
|
22010
|
+
const checkpoint = await tracker.captureForHandoff(localGitState);
|
|
22716
22011
|
if (!checkpoint) return;
|
|
22717
22012
|
const checkpointWithDevice = {
|
|
22718
22013
|
...checkpoint,
|