@posthog/agent 2.3.5 → 2.3.11
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/permissions/permission-options.js +6 -1
- package/dist/adapters/claude/permissions/permission-options.js.map +1 -1
- package/dist/adapters/claude/tools.js +6 -1
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +9 -2
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.d.ts +8 -0
- package/dist/server/agent-server.js +1083 -579
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +1138 -634
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +1 -1
- package/src/acp-extensions.ts +3 -0
- package/src/adapters/claude/tools.ts +6 -1
- package/src/resume.ts +1 -1
- package/src/sagas/resume-saga.ts +10 -2
- package/src/server/agent-server.ts +265 -30
package/dist/server/bin.cjs
CHANGED
|
@@ -509,7 +509,7 @@ var require_has_flag = __commonJS({
|
|
|
509
509
|
var require_supports_color = __commonJS({
|
|
510
510
|
"../../node_modules/supports-color/index.js"(exports2, module2) {
|
|
511
511
|
"use strict";
|
|
512
|
-
var
|
|
512
|
+
var os6 = require("os");
|
|
513
513
|
var tty = require("tty");
|
|
514
514
|
var hasFlag = require_has_flag();
|
|
515
515
|
var { env } = process;
|
|
@@ -557,7 +557,7 @@ var require_supports_color = __commonJS({
|
|
|
557
557
|
return min;
|
|
558
558
|
}
|
|
559
559
|
if (process.platform === "win32") {
|
|
560
|
-
const osRelease =
|
|
560
|
+
const osRelease = os6.release().split(".");
|
|
561
561
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
562
562
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
563
563
|
}
|
|
@@ -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(path11, isFile, isDirectory) {
|
|
809
|
+
log(`checking %s`, path11);
|
|
810
810
|
try {
|
|
811
|
-
const stat = fs_1.statSync(
|
|
811
|
+
const stat = fs_1.statSync(path11);
|
|
812
812
|
if (stat.isFile() && isFile) {
|
|
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(path11, type = exports2.READABLE) {
|
|
832
|
+
return check(path11, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
|
|
833
833
|
}
|
|
834
834
|
exports2.exists = exists2;
|
|
835
835
|
exports2.FILE = 1;
|
|
@@ -904,7 +904,7 @@ var import_hono = require("hono");
|
|
|
904
904
|
// package.json
|
|
905
905
|
var package_default = {
|
|
906
906
|
name: "@posthog/agent",
|
|
907
|
-
version: "2.3.
|
|
907
|
+
version: "2.3.11",
|
|
908
908
|
repository: "https://github.com/PostHog/code",
|
|
909
909
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
910
910
|
exports: {
|
|
@@ -1029,6 +1029,8 @@ var POSTHOG_NOTIFICATIONS = {
|
|
|
1029
1029
|
RUN_STARTED: "_posthog/run_started",
|
|
1030
1030
|
/** Task has completed (success or failure) */
|
|
1031
1031
|
TASK_COMPLETE: "_posthog/task_complete",
|
|
1032
|
+
/** Agent finished processing a turn (prompt returned, waiting for next input) */
|
|
1033
|
+
TURN_COMPLETE: "_posthog/turn_complete",
|
|
1032
1034
|
/** Error occurred during task execution */
|
|
1033
1035
|
ERROR: "_posthog/error",
|
|
1034
1036
|
/** Console/log output from the agent */
|
|
@@ -1570,8 +1572,8 @@ var ToolContentBuilder = class {
|
|
|
1570
1572
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1571
1573
|
return this;
|
|
1572
1574
|
}
|
|
1573
|
-
diff(
|
|
1574
|
-
this.items.push({ type: "diff", path:
|
|
1575
|
+
diff(path11, oldText, newText) {
|
|
1576
|
+
this.items.push({ type: "diff", path: path11, oldText, newText });
|
|
1575
1577
|
return this;
|
|
1576
1578
|
}
|
|
1577
1579
|
build() {
|
|
@@ -2948,7 +2950,12 @@ var BASH_TOOLS = /* @__PURE__ */ new Set([
|
|
|
2948
2950
|
]);
|
|
2949
2951
|
var SEARCH_TOOLS = /* @__PURE__ */ new Set(["Glob", "Grep", "LS"]);
|
|
2950
2952
|
var WEB_TOOLS = /* @__PURE__ */ new Set(["WebSearch", "WebFetch"]);
|
|
2951
|
-
var AGENT_TOOLS = /* @__PURE__ */ new Set([
|
|
2953
|
+
var AGENT_TOOLS = /* @__PURE__ */ new Set([
|
|
2954
|
+
"Task",
|
|
2955
|
+
"Agent",
|
|
2956
|
+
"TodoWrite",
|
|
2957
|
+
"Skill"
|
|
2958
|
+
]);
|
|
2952
2959
|
var BASE_ALLOWED_TOOLS = [
|
|
2953
2960
|
...READ_TOOLS,
|
|
2954
2961
|
...SEARCH_TOOLS,
|
|
@@ -4983,6 +4990,45 @@ function createCodexConnection(config) {
|
|
|
4983
4990
|
};
|
|
4984
4991
|
}
|
|
4985
4992
|
|
|
4993
|
+
// src/adapters/claude/session/jsonl-hydration.ts
|
|
4994
|
+
var import_node_crypto2 = require("crypto");
|
|
4995
|
+
var fs4 = __toESM(require("fs/promises"), 1);
|
|
4996
|
+
var os5 = __toESM(require("os"), 1);
|
|
4997
|
+
var path6 = __toESM(require("path"), 1);
|
|
4998
|
+
var CHARS_PER_TOKEN = 4;
|
|
4999
|
+
var DEFAULT_MAX_TOKENS = 15e4;
|
|
5000
|
+
function estimateTurnTokens(turn) {
|
|
5001
|
+
let chars = 0;
|
|
5002
|
+
for (const block of turn.content) {
|
|
5003
|
+
if ("text" in block && typeof block.text === "string") {
|
|
5004
|
+
chars += block.text.length;
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
if (turn.toolCalls) {
|
|
5008
|
+
for (const tc of turn.toolCalls) {
|
|
5009
|
+
chars += JSON.stringify(tc.input ?? "").length;
|
|
5010
|
+
if (tc.result !== void 0) {
|
|
5011
|
+
chars += typeof tc.result === "string" ? tc.result.length : JSON.stringify(tc.result).length;
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
5014
|
+
}
|
|
5015
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
5016
|
+
}
|
|
5017
|
+
function selectRecentTurns(turns, maxTokens = DEFAULT_MAX_TOKENS) {
|
|
5018
|
+
let budget = maxTokens;
|
|
5019
|
+
let startIndex = turns.length;
|
|
5020
|
+
for (let i = turns.length - 1; i >= 0; i--) {
|
|
5021
|
+
const cost = estimateTurnTokens(turns[i]);
|
|
5022
|
+
if (cost > budget) break;
|
|
5023
|
+
budget -= cost;
|
|
5024
|
+
startIndex = i;
|
|
5025
|
+
}
|
|
5026
|
+
while (startIndex < turns.length && turns[startIndex].role !== "user") {
|
|
5027
|
+
startIndex++;
|
|
5028
|
+
}
|
|
5029
|
+
return turns.slice(startIndex);
|
|
5030
|
+
}
|
|
5031
|
+
|
|
4986
5032
|
// src/utils/gateway.ts
|
|
4987
5033
|
function getLlmGatewayUrl(posthogHost, product = "posthog_code") {
|
|
4988
5034
|
const url = new URL(posthogHost);
|
|
@@ -5166,330 +5212,165 @@ var PostHogAPIClient = class {
|
|
|
5166
5212
|
}
|
|
5167
5213
|
};
|
|
5168
5214
|
|
|
5169
|
-
//
|
|
5170
|
-
var
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
static SESSIONS_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
5179
|
-
posthogAPI;
|
|
5180
|
-
pendingEntries = /* @__PURE__ */ new Map();
|
|
5181
|
-
flushTimeouts = /* @__PURE__ */ new Map();
|
|
5182
|
-
lastFlushAttemptTime = /* @__PURE__ */ new Map();
|
|
5183
|
-
retryCounts = /* @__PURE__ */ new Map();
|
|
5184
|
-
sessions = /* @__PURE__ */ new Map();
|
|
5185
|
-
logger;
|
|
5186
|
-
localCachePath;
|
|
5187
|
-
constructor(options = {}) {
|
|
5188
|
-
this.posthogAPI = options.posthogAPI;
|
|
5189
|
-
this.localCachePath = options.localCachePath;
|
|
5190
|
-
this.logger = options.logger ?? new Logger({ debug: false, prefix: "[SessionLogWriter]" });
|
|
5215
|
+
// ../shared/dist/index.js
|
|
5216
|
+
var consoleLogger = {
|
|
5217
|
+
info: (_message, _data) => {
|
|
5218
|
+
},
|
|
5219
|
+
debug: (_message, _data) => {
|
|
5220
|
+
},
|
|
5221
|
+
error: (_message, _data) => {
|
|
5222
|
+
},
|
|
5223
|
+
warn: (_message, _data) => {
|
|
5191
5224
|
}
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5225
|
+
};
|
|
5226
|
+
var Saga = class {
|
|
5227
|
+
completedSteps = [];
|
|
5228
|
+
currentStepName = "unknown";
|
|
5229
|
+
stepTimings = [];
|
|
5230
|
+
log;
|
|
5231
|
+
constructor(logger) {
|
|
5232
|
+
this.log = logger ?? consoleLogger;
|
|
5199
5233
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5234
|
+
/**
|
|
5235
|
+
* Run the saga with the given input.
|
|
5236
|
+
* Returns a discriminated union result - either success with data or failure with error details.
|
|
5237
|
+
*/
|
|
5238
|
+
async run(input) {
|
|
5239
|
+
this.completedSteps = [];
|
|
5240
|
+
this.currentStepName = "unknown";
|
|
5241
|
+
this.stepTimings = [];
|
|
5242
|
+
const sagaStart = performance.now();
|
|
5243
|
+
this.log.info("Starting saga", { sagaName: this.sagaName });
|
|
5244
|
+
try {
|
|
5245
|
+
const result = await this.execute(input);
|
|
5246
|
+
const totalDuration = performance.now() - sagaStart;
|
|
5247
|
+
this.log.debug("Saga completed successfully", {
|
|
5248
|
+
sagaName: this.sagaName,
|
|
5249
|
+
stepsCompleted: this.completedSteps.length,
|
|
5250
|
+
totalDurationMs: Math.round(totalDuration),
|
|
5251
|
+
stepTimings: this.stepTimings
|
|
5252
|
+
});
|
|
5253
|
+
return { success: true, data: result };
|
|
5254
|
+
} catch (error) {
|
|
5255
|
+
this.log.error("Saga failed, initiating rollback", {
|
|
5256
|
+
sagaName: this.sagaName,
|
|
5257
|
+
failedStep: this.currentStepName,
|
|
5258
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5259
|
+
});
|
|
5260
|
+
await this.rollback();
|
|
5261
|
+
return {
|
|
5262
|
+
success: false,
|
|
5263
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5264
|
+
failedStep: this.currentStepName
|
|
5265
|
+
};
|
|
5203
5266
|
}
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5267
|
+
}
|
|
5268
|
+
/**
|
|
5269
|
+
* Execute a step with its rollback action.
|
|
5270
|
+
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
5271
|
+
* The step name is automatically tracked for error reporting.
|
|
5272
|
+
*
|
|
5273
|
+
* @param config - Step configuration with name, execute, and rollback functions
|
|
5274
|
+
* @returns The result of the execute function
|
|
5275
|
+
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
5276
|
+
*/
|
|
5277
|
+
async step(config) {
|
|
5278
|
+
this.currentStepName = config.name;
|
|
5279
|
+
this.log.debug(`Executing step: ${config.name}`);
|
|
5280
|
+
const stepStart = performance.now();
|
|
5281
|
+
const result = await config.execute();
|
|
5282
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
5283
|
+
this.stepTimings.push({ name: config.name, durationMs });
|
|
5284
|
+
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
5285
|
+
this.completedSteps.push({
|
|
5286
|
+
name: config.name,
|
|
5287
|
+
rollback: () => config.rollback(result)
|
|
5207
5288
|
});
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5289
|
+
return result;
|
|
5290
|
+
}
|
|
5291
|
+
/**
|
|
5292
|
+
* Execute a step that doesn't need rollback.
|
|
5293
|
+
* Useful for read-only operations or operations that are idempotent.
|
|
5294
|
+
* The step name is automatically tracked for error reporting.
|
|
5295
|
+
*
|
|
5296
|
+
* @param name - Step name for logging and error tracking
|
|
5297
|
+
* @param execute - The action to execute
|
|
5298
|
+
* @returns The result of the execute function
|
|
5299
|
+
*/
|
|
5300
|
+
async readOnlyStep(name, execute) {
|
|
5301
|
+
this.currentStepName = name;
|
|
5302
|
+
this.log.debug(`Executing read-only step: ${name}`);
|
|
5303
|
+
const stepStart = performance.now();
|
|
5304
|
+
const result = await execute();
|
|
5305
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
5306
|
+
this.stepTimings.push({ name, durationMs });
|
|
5307
|
+
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
5308
|
+
return result;
|
|
5309
|
+
}
|
|
5310
|
+
/**
|
|
5311
|
+
* Roll back all completed steps in reverse order.
|
|
5312
|
+
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
5313
|
+
*/
|
|
5314
|
+
async rollback() {
|
|
5315
|
+
this.log.info("Rolling back saga", {
|
|
5316
|
+
stepsToRollback: this.completedSteps.length
|
|
5317
|
+
});
|
|
5318
|
+
const stepsReversed = [...this.completedSteps].reverse();
|
|
5319
|
+
for (const step of stepsReversed) {
|
|
5216
5320
|
try {
|
|
5217
|
-
|
|
5321
|
+
this.log.debug(`Rolling back step: ${step.name}`);
|
|
5322
|
+
await step.rollback();
|
|
5323
|
+
this.log.debug(`Step rolled back: ${step.name}`);
|
|
5218
5324
|
} catch (error) {
|
|
5219
|
-
this.
|
|
5220
|
-
|
|
5221
|
-
error
|
|
5325
|
+
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
5326
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5222
5327
|
});
|
|
5223
5328
|
}
|
|
5224
5329
|
}
|
|
5330
|
+
this.log.info("Rollback completed", {
|
|
5331
|
+
stepsAttempted: this.completedSteps.length
|
|
5332
|
+
});
|
|
5225
5333
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5334
|
+
/**
|
|
5335
|
+
* Get the number of completed steps (useful for testing)
|
|
5336
|
+
*/
|
|
5337
|
+
getCompletedStepCount() {
|
|
5338
|
+
return this.completedSteps.length;
|
|
5228
5339
|
}
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
5264
|
-
pending.push(entry);
|
|
5265
|
-
this.pendingEntries.set(sessionId, pending);
|
|
5266
|
-
this.scheduleFlush(sessionId);
|
|
5267
|
-
}
|
|
5268
|
-
} catch {
|
|
5269
|
-
this.logger.warn("Failed to parse raw line for persistence", {
|
|
5270
|
-
taskId: session.context.taskId,
|
|
5271
|
-
runId: session.context.runId,
|
|
5272
|
-
lineLength: line.length
|
|
5273
|
-
});
|
|
5274
|
-
}
|
|
5275
|
-
}
|
|
5276
|
-
async flush(sessionId) {
|
|
5277
|
-
const session = this.sessions.get(sessionId);
|
|
5278
|
-
if (!session) {
|
|
5279
|
-
this.logger.warn("flush: no session found", { sessionId });
|
|
5280
|
-
return;
|
|
5281
|
-
}
|
|
5282
|
-
this.emitCoalescedMessage(sessionId, session);
|
|
5283
|
-
const pending = this.pendingEntries.get(sessionId);
|
|
5284
|
-
if (!this.posthogAPI || !pending?.length) {
|
|
5285
|
-
return;
|
|
5286
|
-
}
|
|
5287
|
-
this.pendingEntries.delete(sessionId);
|
|
5288
|
-
const timeout = this.flushTimeouts.get(sessionId);
|
|
5289
|
-
if (timeout) {
|
|
5290
|
-
clearTimeout(timeout);
|
|
5291
|
-
this.flushTimeouts.delete(sessionId);
|
|
5292
|
-
}
|
|
5293
|
-
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
5294
|
-
try {
|
|
5295
|
-
await this.posthogAPI.appendTaskRunLog(
|
|
5296
|
-
session.context.taskId,
|
|
5297
|
-
session.context.runId,
|
|
5298
|
-
pending
|
|
5299
|
-
);
|
|
5300
|
-
this.retryCounts.set(sessionId, 0);
|
|
5301
|
-
} catch (error) {
|
|
5302
|
-
const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
|
|
5303
|
-
this.retryCounts.set(sessionId, retryCount);
|
|
5304
|
-
if (retryCount >= _SessionLogWriter.MAX_FLUSH_RETRIES) {
|
|
5305
|
-
this.logger.error(
|
|
5306
|
-
`Dropping ${pending.length} session log entries after ${retryCount} failed flush attempts`,
|
|
5307
|
-
{
|
|
5308
|
-
taskId: session.context.taskId,
|
|
5309
|
-
runId: session.context.runId,
|
|
5310
|
-
error
|
|
5311
|
-
}
|
|
5312
|
-
);
|
|
5313
|
-
this.retryCounts.set(sessionId, 0);
|
|
5314
|
-
} else {
|
|
5315
|
-
if (retryCount === 1) {
|
|
5316
|
-
this.logger.warn(
|
|
5317
|
-
`Failed to persist session logs, will retry (up to ${_SessionLogWriter.MAX_FLUSH_RETRIES} attempts)`,
|
|
5318
|
-
{
|
|
5319
|
-
taskId: session.context.taskId,
|
|
5320
|
-
runId: session.context.runId,
|
|
5321
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5322
|
-
}
|
|
5323
|
-
);
|
|
5324
|
-
}
|
|
5325
|
-
const currentPending = this.pendingEntries.get(sessionId) ?? [];
|
|
5326
|
-
this.pendingEntries.set(sessionId, [...pending, ...currentPending]);
|
|
5327
|
-
this.scheduleFlush(sessionId);
|
|
5328
|
-
}
|
|
5329
|
-
}
|
|
5330
|
-
}
|
|
5331
|
-
isAgentMessageChunk(message) {
|
|
5332
|
-
if (message.method !== "session/update") return false;
|
|
5333
|
-
const params = message.params;
|
|
5334
|
-
const update = params?.update;
|
|
5335
|
-
return update?.sessionUpdate === "agent_message_chunk";
|
|
5336
|
-
}
|
|
5337
|
-
extractChunkText(message) {
|
|
5338
|
-
const params = message.params;
|
|
5339
|
-
const update = params?.update;
|
|
5340
|
-
const content = update?.content;
|
|
5341
|
-
if (content?.type === "text" && content.text) {
|
|
5342
|
-
return content.text;
|
|
5343
|
-
}
|
|
5344
|
-
return "";
|
|
5345
|
-
}
|
|
5346
|
-
emitCoalescedMessage(sessionId, session) {
|
|
5347
|
-
if (!session.chunkBuffer) return;
|
|
5348
|
-
const { text: text2, firstTimestamp } = session.chunkBuffer;
|
|
5349
|
-
session.chunkBuffer = void 0;
|
|
5350
|
-
session.lastAgentMessage = text2;
|
|
5351
|
-
const entry = {
|
|
5352
|
-
type: "notification",
|
|
5353
|
-
timestamp: firstTimestamp,
|
|
5354
|
-
notification: {
|
|
5355
|
-
jsonrpc: "2.0",
|
|
5356
|
-
method: "session/update",
|
|
5357
|
-
params: {
|
|
5358
|
-
update: {
|
|
5359
|
-
sessionUpdate: "agent_message",
|
|
5360
|
-
content: { type: "text", text: text2 }
|
|
5361
|
-
}
|
|
5362
|
-
}
|
|
5363
|
-
}
|
|
5364
|
-
};
|
|
5365
|
-
this.writeToLocalCache(sessionId, entry);
|
|
5366
|
-
if (this.posthogAPI) {
|
|
5367
|
-
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
5368
|
-
pending.push(entry);
|
|
5369
|
-
this.pendingEntries.set(sessionId, pending);
|
|
5370
|
-
this.scheduleFlush(sessionId);
|
|
5371
|
-
}
|
|
5372
|
-
}
|
|
5373
|
-
getLastAgentMessage(sessionId) {
|
|
5374
|
-
return this.sessions.get(sessionId)?.lastAgentMessage;
|
|
5375
|
-
}
|
|
5376
|
-
extractAgentMessageText(message) {
|
|
5377
|
-
if (message.method !== "session/update") {
|
|
5378
|
-
return null;
|
|
5379
|
-
}
|
|
5380
|
-
const params = message.params;
|
|
5381
|
-
const update = params?.update;
|
|
5382
|
-
if (update?.sessionUpdate !== "agent_message") {
|
|
5383
|
-
return null;
|
|
5384
|
-
}
|
|
5385
|
-
const content = update.content;
|
|
5386
|
-
if (content?.type === "text" && typeof content.text === "string") {
|
|
5387
|
-
const trimmed2 = content.text.trim();
|
|
5388
|
-
return trimmed2.length > 0 ? trimmed2 : null;
|
|
5389
|
-
}
|
|
5390
|
-
if (typeof update.message === "string") {
|
|
5391
|
-
const trimmed2 = update.message.trim();
|
|
5392
|
-
return trimmed2.length > 0 ? trimmed2 : null;
|
|
5393
|
-
}
|
|
5394
|
-
return null;
|
|
5395
|
-
}
|
|
5396
|
-
scheduleFlush(sessionId) {
|
|
5397
|
-
const existing = this.flushTimeouts.get(sessionId);
|
|
5398
|
-
if (existing) clearTimeout(existing);
|
|
5399
|
-
const retryCount = this.retryCounts.get(sessionId) ?? 0;
|
|
5400
|
-
const lastAttempt = this.lastFlushAttemptTime.get(sessionId) ?? 0;
|
|
5401
|
-
const elapsed = Date.now() - lastAttempt;
|
|
5402
|
-
let delay3;
|
|
5403
|
-
if (retryCount > 0) {
|
|
5404
|
-
delay3 = Math.min(
|
|
5405
|
-
_SessionLogWriter.FLUSH_DEBOUNCE_MS * 2 ** retryCount,
|
|
5406
|
-
_SessionLogWriter.MAX_RETRY_DELAY_MS
|
|
5407
|
-
);
|
|
5408
|
-
} else if (elapsed >= _SessionLogWriter.FLUSH_MAX_INTERVAL_MS) {
|
|
5409
|
-
delay3 = 0;
|
|
5410
|
-
} else {
|
|
5411
|
-
delay3 = _SessionLogWriter.FLUSH_DEBOUNCE_MS;
|
|
5412
|
-
}
|
|
5413
|
-
const timeout = setTimeout(() => this.flush(sessionId), delay3);
|
|
5414
|
-
this.flushTimeouts.set(sessionId, timeout);
|
|
5415
|
-
}
|
|
5416
|
-
writeToLocalCache(sessionId, entry) {
|
|
5417
|
-
if (!this.localCachePath) return;
|
|
5418
|
-
const session = this.sessions.get(sessionId);
|
|
5419
|
-
if (!session) return;
|
|
5420
|
-
const logPath = import_node_path3.default.join(
|
|
5421
|
-
this.localCachePath,
|
|
5422
|
-
"sessions",
|
|
5423
|
-
session.context.runId,
|
|
5424
|
-
"logs.ndjson"
|
|
5425
|
-
);
|
|
5426
|
-
try {
|
|
5427
|
-
import_node_fs2.default.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
5428
|
-
`);
|
|
5429
|
-
} catch (error) {
|
|
5430
|
-
this.logger.warn("Failed to write to local cache", {
|
|
5431
|
-
taskId: session.context.taskId,
|
|
5432
|
-
runId: session.context.runId,
|
|
5433
|
-
logPath,
|
|
5434
|
-
error
|
|
5435
|
-
});
|
|
5436
|
-
}
|
|
5437
|
-
}
|
|
5438
|
-
static async cleanupOldSessions(localCachePath) {
|
|
5439
|
-
const sessionsDir = import_node_path3.default.join(localCachePath, "sessions");
|
|
5440
|
-
let deleted = 0;
|
|
5441
|
-
try {
|
|
5442
|
-
const entries = await import_promises.default.readdir(sessionsDir);
|
|
5443
|
-
const now = Date.now();
|
|
5444
|
-
for (const entry of entries) {
|
|
5445
|
-
const entryPath = import_node_path3.default.join(sessionsDir, entry);
|
|
5446
|
-
try {
|
|
5447
|
-
const stats = await import_promises.default.stat(entryPath);
|
|
5448
|
-
if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {
|
|
5449
|
-
await import_promises.default.rm(entryPath, { recursive: true, force: true });
|
|
5450
|
-
deleted++;
|
|
5451
|
-
}
|
|
5452
|
-
} catch {
|
|
5453
|
-
}
|
|
5454
|
-
}
|
|
5455
|
-
} catch {
|
|
5456
|
-
}
|
|
5457
|
-
return deleted;
|
|
5458
|
-
}
|
|
5459
|
-
};
|
|
5460
|
-
|
|
5461
|
-
// ../git/dist/queries.js
|
|
5462
|
-
var fs6 = __toESM(require("fs/promises"), 1);
|
|
5463
|
-
var path8 = __toESM(require("path"), 1);
|
|
5464
|
-
|
|
5465
|
-
// ../../node_modules/simple-git/dist/esm/index.js
|
|
5466
|
-
var import_node_buffer = require("buffer");
|
|
5467
|
-
var import_file_exists = __toESM(require_dist(), 1);
|
|
5468
|
-
var import_debug = __toESM(require_src(), 1);
|
|
5469
|
-
var import_child_process = require("child_process");
|
|
5470
|
-
var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
5471
|
-
var import_node_path4 = require("path");
|
|
5472
|
-
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
5473
|
-
var import_node_events = require("events");
|
|
5474
|
-
var __defProp2 = Object.defineProperty;
|
|
5475
|
-
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
5476
|
-
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
5477
|
-
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
5478
|
-
var __esm = (fn, res) => function __init() {
|
|
5479
|
-
return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
|
|
5480
|
-
};
|
|
5481
|
-
var __commonJS2 = (cb, mod) => function __require() {
|
|
5482
|
-
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
5483
|
-
};
|
|
5484
|
-
var __export = (target, all) => {
|
|
5485
|
-
for (var name in all)
|
|
5486
|
-
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
5487
|
-
};
|
|
5488
|
-
var __copyProps2 = (to, from, except, desc) => {
|
|
5489
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
5490
|
-
for (let key of __getOwnPropNames2(from))
|
|
5491
|
-
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
5492
|
-
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
5340
|
+
};
|
|
5341
|
+
|
|
5342
|
+
// ../git/dist/queries.js
|
|
5343
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
5344
|
+
var path8 = __toESM(require("path"), 1);
|
|
5345
|
+
|
|
5346
|
+
// ../../node_modules/simple-git/dist/esm/index.js
|
|
5347
|
+
var import_node_buffer = require("buffer");
|
|
5348
|
+
var import_file_exists = __toESM(require_dist(), 1);
|
|
5349
|
+
var import_debug = __toESM(require_src(), 1);
|
|
5350
|
+
var import_child_process = require("child_process");
|
|
5351
|
+
var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
5352
|
+
var import_node_path3 = require("path");
|
|
5353
|
+
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
5354
|
+
var import_node_events = require("events");
|
|
5355
|
+
var __defProp2 = Object.defineProperty;
|
|
5356
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
5357
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
5358
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
5359
|
+
var __esm = (fn, res) => function __init() {
|
|
5360
|
+
return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;
|
|
5361
|
+
};
|
|
5362
|
+
var __commonJS2 = (cb, mod) => function __require() {
|
|
5363
|
+
return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
5364
|
+
};
|
|
5365
|
+
var __export = (target, all) => {
|
|
5366
|
+
for (var name in all)
|
|
5367
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
5368
|
+
};
|
|
5369
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
5370
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
5371
|
+
for (let key of __getOwnPropNames2(from))
|
|
5372
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
5373
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
5493
5374
|
}
|
|
5494
5375
|
return to;
|
|
5495
5376
|
};
|
|
@@ -5499,8 +5380,8 @@ function pathspec(...paths) {
|
|
|
5499
5380
|
cache.set(key, paths);
|
|
5500
5381
|
return key;
|
|
5501
5382
|
}
|
|
5502
|
-
function isPathSpec(
|
|
5503
|
-
return
|
|
5383
|
+
function isPathSpec(path11) {
|
|
5384
|
+
return path11 instanceof String && cache.has(path11);
|
|
5504
5385
|
}
|
|
5505
5386
|
function toPaths(pathSpec) {
|
|
5506
5387
|
return cache.get(pathSpec) || [];
|
|
@@ -5589,8 +5470,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
5589
5470
|
function forEachLineWithContent(input, callback) {
|
|
5590
5471
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
5591
5472
|
}
|
|
5592
|
-
function folderExists(
|
|
5593
|
-
return (0, import_file_exists.exists)(
|
|
5473
|
+
function folderExists(path11) {
|
|
5474
|
+
return (0, import_file_exists.exists)(path11, import_file_exists.FOLDER);
|
|
5594
5475
|
}
|
|
5595
5476
|
function append(target, item) {
|
|
5596
5477
|
if (Array.isArray(target)) {
|
|
@@ -5994,8 +5875,8 @@ function checkIsRepoRootTask() {
|
|
|
5994
5875
|
commands,
|
|
5995
5876
|
format: "utf-8",
|
|
5996
5877
|
onError,
|
|
5997
|
-
parser(
|
|
5998
|
-
return /^\.(git)?$/.test(
|
|
5878
|
+
parser(path11) {
|
|
5879
|
+
return /^\.(git)?$/.test(path11.trim());
|
|
5999
5880
|
}
|
|
6000
5881
|
};
|
|
6001
5882
|
}
|
|
@@ -6429,11 +6310,11 @@ function parseGrep(grep) {
|
|
|
6429
6310
|
const paths = /* @__PURE__ */ new Set();
|
|
6430
6311
|
const results = {};
|
|
6431
6312
|
forEachLineWithContent(grep, (input) => {
|
|
6432
|
-
const [
|
|
6433
|
-
paths.add(
|
|
6434
|
-
(results[
|
|
6313
|
+
const [path11, line, preview] = input.split(NULL);
|
|
6314
|
+
paths.add(path11);
|
|
6315
|
+
(results[path11] = results[path11] || []).push({
|
|
6435
6316
|
line: asNumber(line),
|
|
6436
|
-
path:
|
|
6317
|
+
path: path11,
|
|
6437
6318
|
preview
|
|
6438
6319
|
});
|
|
6439
6320
|
});
|
|
@@ -7198,14 +7079,14 @@ var init_hash_object = __esm({
|
|
|
7198
7079
|
init_task();
|
|
7199
7080
|
}
|
|
7200
7081
|
});
|
|
7201
|
-
function parseInit(bare,
|
|
7082
|
+
function parseInit(bare, path11, text2) {
|
|
7202
7083
|
const response = String(text2).trim();
|
|
7203
7084
|
let result;
|
|
7204
7085
|
if (result = initResponseRegex.exec(response)) {
|
|
7205
|
-
return new InitSummary(bare,
|
|
7086
|
+
return new InitSummary(bare, path11, false, result[1]);
|
|
7206
7087
|
}
|
|
7207
7088
|
if (result = reInitResponseRegex.exec(response)) {
|
|
7208
|
-
return new InitSummary(bare,
|
|
7089
|
+
return new InitSummary(bare, path11, true, result[1]);
|
|
7209
7090
|
}
|
|
7210
7091
|
let gitDir = "";
|
|
7211
7092
|
const tokens = response.split(" ");
|
|
@@ -7216,7 +7097,7 @@ function parseInit(bare, path10, text2) {
|
|
|
7216
7097
|
break;
|
|
7217
7098
|
}
|
|
7218
7099
|
}
|
|
7219
|
-
return new InitSummary(bare,
|
|
7100
|
+
return new InitSummary(bare, path11, /^re/i.test(response), gitDir);
|
|
7220
7101
|
}
|
|
7221
7102
|
var InitSummary;
|
|
7222
7103
|
var initResponseRegex;
|
|
@@ -7225,9 +7106,9 @@ var init_InitSummary = __esm({
|
|
|
7225
7106
|
"src/lib/responses/InitSummary.ts"() {
|
|
7226
7107
|
"use strict";
|
|
7227
7108
|
InitSummary = class {
|
|
7228
|
-
constructor(bare,
|
|
7109
|
+
constructor(bare, path11, existing, gitDir) {
|
|
7229
7110
|
this.bare = bare;
|
|
7230
|
-
this.path =
|
|
7111
|
+
this.path = path11;
|
|
7231
7112
|
this.existing = existing;
|
|
7232
7113
|
this.gitDir = gitDir;
|
|
7233
7114
|
}
|
|
@@ -7239,7 +7120,7 @@ var init_InitSummary = __esm({
|
|
|
7239
7120
|
function hasBareCommand(command) {
|
|
7240
7121
|
return command.includes(bareCommand);
|
|
7241
7122
|
}
|
|
7242
|
-
function initTask(bare = false,
|
|
7123
|
+
function initTask(bare = false, path11, customArgs) {
|
|
7243
7124
|
const commands = ["init", ...customArgs];
|
|
7244
7125
|
if (bare && !hasBareCommand(commands)) {
|
|
7245
7126
|
commands.splice(1, 0, bareCommand);
|
|
@@ -7248,7 +7129,7 @@ function initTask(bare = false, path10, customArgs) {
|
|
|
7248
7129
|
commands,
|
|
7249
7130
|
format: "utf-8",
|
|
7250
7131
|
parser(text2) {
|
|
7251
|
-
return parseInit(commands.includes("--bare"),
|
|
7132
|
+
return parseInit(commands.includes("--bare"), path11, text2);
|
|
7252
7133
|
}
|
|
7253
7134
|
};
|
|
7254
7135
|
}
|
|
@@ -8064,12 +7945,12 @@ var init_FileStatusSummary = __esm({
|
|
|
8064
7945
|
"use strict";
|
|
8065
7946
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
8066
7947
|
FileStatusSummary = class {
|
|
8067
|
-
constructor(
|
|
8068
|
-
this.path =
|
|
7948
|
+
constructor(path11, index, working_dir) {
|
|
7949
|
+
this.path = path11;
|
|
8069
7950
|
this.index = index;
|
|
8070
7951
|
this.working_dir = working_dir;
|
|
8071
7952
|
if (index === "R" || working_dir === "R") {
|
|
8072
|
-
const detail = fromPathRegex.exec(
|
|
7953
|
+
const detail = fromPathRegex.exec(path11) || [null, path11, path11];
|
|
8073
7954
|
this.from = detail[2] || "";
|
|
8074
7955
|
this.path = detail[1] || "";
|
|
8075
7956
|
}
|
|
@@ -8100,14 +7981,14 @@ function splitLine(result, lineStr) {
|
|
|
8100
7981
|
default:
|
|
8101
7982
|
return;
|
|
8102
7983
|
}
|
|
8103
|
-
function data(index, workingDir,
|
|
7984
|
+
function data(index, workingDir, path11) {
|
|
8104
7985
|
const raw = `${index}${workingDir}`;
|
|
8105
7986
|
const handler = parsers6.get(raw);
|
|
8106
7987
|
if (handler) {
|
|
8107
|
-
handler(result,
|
|
7988
|
+
handler(result, path11);
|
|
8108
7989
|
}
|
|
8109
7990
|
if (raw !== "##" && raw !== "!!") {
|
|
8110
|
-
result.files.push(new FileStatusSummary(
|
|
7991
|
+
result.files.push(new FileStatusSummary(path11, index, workingDir));
|
|
8111
7992
|
}
|
|
8112
7993
|
}
|
|
8113
7994
|
}
|
|
@@ -8420,9 +8301,9 @@ var init_simple_git_api = __esm({
|
|
|
8420
8301
|
next
|
|
8421
8302
|
);
|
|
8422
8303
|
}
|
|
8423
|
-
hashObject(
|
|
8304
|
+
hashObject(path11, write) {
|
|
8424
8305
|
return this._runTask(
|
|
8425
|
-
hashObjectTask(
|
|
8306
|
+
hashObjectTask(path11, write === true),
|
|
8426
8307
|
trailingFunctionArgument(arguments)
|
|
8427
8308
|
);
|
|
8428
8309
|
}
|
|
@@ -8775,8 +8656,8 @@ var init_branch = __esm({
|
|
|
8775
8656
|
}
|
|
8776
8657
|
});
|
|
8777
8658
|
function toPath(input) {
|
|
8778
|
-
const
|
|
8779
|
-
return
|
|
8659
|
+
const path11 = input.trim().replace(/^["']|["']$/g, "");
|
|
8660
|
+
return path11 && (0, import_node_path3.normalize)(path11);
|
|
8780
8661
|
}
|
|
8781
8662
|
var parseCheckIgnore;
|
|
8782
8663
|
var init_CheckIgnore = __esm({
|
|
@@ -9090,8 +8971,8 @@ __export(sub_module_exports, {
|
|
|
9090
8971
|
subModuleTask: () => subModuleTask,
|
|
9091
8972
|
updateSubModuleTask: () => updateSubModuleTask
|
|
9092
8973
|
});
|
|
9093
|
-
function addSubModuleTask(repo,
|
|
9094
|
-
return subModuleTask(["add", repo,
|
|
8974
|
+
function addSubModuleTask(repo, path11) {
|
|
8975
|
+
return subModuleTask(["add", repo, path11]);
|
|
9095
8976
|
}
|
|
9096
8977
|
function initSubModuleTask(customArgs) {
|
|
9097
8978
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -9421,8 +9302,8 @@ var require_git = __commonJS2({
|
|
|
9421
9302
|
}
|
|
9422
9303
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
9423
9304
|
};
|
|
9424
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
9425
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
9305
|
+
Git2.prototype.submoduleAdd = function(repo, path11, then) {
|
|
9306
|
+
return this._runTask(addSubModuleTask2(repo, path11), trailingFunctionArgument2(arguments));
|
|
9426
9307
|
};
|
|
9427
9308
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
9428
9309
|
return this._runTask(
|
|
@@ -10023,22 +9904,22 @@ function createGitClient(baseDir, options) {
|
|
|
10023
9904
|
|
|
10024
9905
|
// ../git/dist/lock-detector.js
|
|
10025
9906
|
var import_node_child_process3 = require("child_process");
|
|
10026
|
-
var
|
|
10027
|
-
var
|
|
9907
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
9908
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
10028
9909
|
var import_node_util = require("util");
|
|
10029
9910
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process3.execFile);
|
|
10030
9911
|
async function getIndexLockPath(repoPath) {
|
|
10031
9912
|
try {
|
|
10032
9913
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "index.lock"], { cwd: repoPath });
|
|
10033
|
-
return
|
|
9914
|
+
return import_node_path4.default.resolve(repoPath, stdout.trim());
|
|
10034
9915
|
} catch {
|
|
10035
|
-
return
|
|
9916
|
+
return import_node_path4.default.join(repoPath, ".git", "index.lock");
|
|
10036
9917
|
}
|
|
10037
9918
|
}
|
|
10038
9919
|
async function getLockInfo(repoPath) {
|
|
10039
9920
|
const lockPath = await getIndexLockPath(repoPath);
|
|
10040
9921
|
try {
|
|
10041
|
-
const stat = await
|
|
9922
|
+
const stat = await import_promises.default.stat(lockPath);
|
|
10042
9923
|
return {
|
|
10043
9924
|
path: lockPath,
|
|
10044
9925
|
ageMs: Date.now() - stat.mtimeMs
|
|
@@ -10049,7 +9930,7 @@ async function getLockInfo(repoPath) {
|
|
|
10049
9930
|
}
|
|
10050
9931
|
async function removeLock(repoPath) {
|
|
10051
9932
|
const lockPath = await getIndexLockPath(repoPath);
|
|
10052
|
-
await
|
|
9933
|
+
await import_promises.default.rm(lockPath, { force: true });
|
|
10053
9934
|
}
|
|
10054
9935
|
async function isLocked(repoPath) {
|
|
10055
9936
|
return await getLockInfo(repoPath) !== null;
|
|
@@ -10215,157 +10096,30 @@ async function getHeadSha(baseDir, options) {
|
|
|
10215
10096
|
}
|
|
10216
10097
|
|
|
10217
10098
|
// src/sagas/apply-snapshot-saga.ts
|
|
10218
|
-
var
|
|
10219
|
-
var
|
|
10099
|
+
var import_promises2 = require("fs/promises");
|
|
10100
|
+
var import_node_path5 = require("path");
|
|
10220
10101
|
|
|
10221
10102
|
// ../git/dist/sagas/tree.js
|
|
10222
|
-
var
|
|
10103
|
+
var import_node_fs2 = require("fs");
|
|
10223
10104
|
var fs7 = __toESM(require("fs/promises"), 1);
|
|
10224
10105
|
var path9 = __toESM(require("path"), 1);
|
|
10225
10106
|
var tar = __toESM(require("tar"), 1);
|
|
10226
10107
|
|
|
10227
|
-
// ../
|
|
10228
|
-
var
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
warn: (_message, _data) => {
|
|
10108
|
+
// ../git/dist/git-saga.js
|
|
10109
|
+
var GitSaga = class extends Saga {
|
|
10110
|
+
_git = null;
|
|
10111
|
+
get git() {
|
|
10112
|
+
if (!this._git) {
|
|
10113
|
+
throw new Error("git client accessed before execute() was called");
|
|
10114
|
+
}
|
|
10115
|
+
return this._git;
|
|
10236
10116
|
}
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
constructor(logger) {
|
|
10244
|
-
this.log = logger ?? consoleLogger;
|
|
10245
|
-
}
|
|
10246
|
-
/**
|
|
10247
|
-
* Run the saga with the given input.
|
|
10248
|
-
* Returns a discriminated union result - either success with data or failure with error details.
|
|
10249
|
-
*/
|
|
10250
|
-
async run(input) {
|
|
10251
|
-
this.completedSteps = [];
|
|
10252
|
-
this.currentStepName = "unknown";
|
|
10253
|
-
this.stepTimings = [];
|
|
10254
|
-
const sagaStart = performance.now();
|
|
10255
|
-
this.log.info("Starting saga", { sagaName: this.sagaName });
|
|
10256
|
-
try {
|
|
10257
|
-
const result = await this.execute(input);
|
|
10258
|
-
const totalDuration = performance.now() - sagaStart;
|
|
10259
|
-
this.log.debug("Saga completed successfully", {
|
|
10260
|
-
sagaName: this.sagaName,
|
|
10261
|
-
stepsCompleted: this.completedSteps.length,
|
|
10262
|
-
totalDurationMs: Math.round(totalDuration),
|
|
10263
|
-
stepTimings: this.stepTimings
|
|
10264
|
-
});
|
|
10265
|
-
return { success: true, data: result };
|
|
10266
|
-
} catch (error) {
|
|
10267
|
-
this.log.error("Saga failed, initiating rollback", {
|
|
10268
|
-
sagaName: this.sagaName,
|
|
10269
|
-
failedStep: this.currentStepName,
|
|
10270
|
-
error: error instanceof Error ? error.message : String(error)
|
|
10271
|
-
});
|
|
10272
|
-
await this.rollback();
|
|
10273
|
-
return {
|
|
10274
|
-
success: false,
|
|
10275
|
-
error: error instanceof Error ? error.message : String(error),
|
|
10276
|
-
failedStep: this.currentStepName
|
|
10277
|
-
};
|
|
10278
|
-
}
|
|
10279
|
-
}
|
|
10280
|
-
/**
|
|
10281
|
-
* Execute a step with its rollback action.
|
|
10282
|
-
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
10283
|
-
* The step name is automatically tracked for error reporting.
|
|
10284
|
-
*
|
|
10285
|
-
* @param config - Step configuration with name, execute, and rollback functions
|
|
10286
|
-
* @returns The result of the execute function
|
|
10287
|
-
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
10288
|
-
*/
|
|
10289
|
-
async step(config) {
|
|
10290
|
-
this.currentStepName = config.name;
|
|
10291
|
-
this.log.debug(`Executing step: ${config.name}`);
|
|
10292
|
-
const stepStart = performance.now();
|
|
10293
|
-
const result = await config.execute();
|
|
10294
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
10295
|
-
this.stepTimings.push({ name: config.name, durationMs });
|
|
10296
|
-
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
10297
|
-
this.completedSteps.push({
|
|
10298
|
-
name: config.name,
|
|
10299
|
-
rollback: () => config.rollback(result)
|
|
10300
|
-
});
|
|
10301
|
-
return result;
|
|
10302
|
-
}
|
|
10303
|
-
/**
|
|
10304
|
-
* Execute a step that doesn't need rollback.
|
|
10305
|
-
* Useful for read-only operations or operations that are idempotent.
|
|
10306
|
-
* The step name is automatically tracked for error reporting.
|
|
10307
|
-
*
|
|
10308
|
-
* @param name - Step name for logging and error tracking
|
|
10309
|
-
* @param execute - The action to execute
|
|
10310
|
-
* @returns The result of the execute function
|
|
10311
|
-
*/
|
|
10312
|
-
async readOnlyStep(name, execute) {
|
|
10313
|
-
this.currentStepName = name;
|
|
10314
|
-
this.log.debug(`Executing read-only step: ${name}`);
|
|
10315
|
-
const stepStart = performance.now();
|
|
10316
|
-
const result = await execute();
|
|
10317
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
10318
|
-
this.stepTimings.push({ name, durationMs });
|
|
10319
|
-
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
10320
|
-
return result;
|
|
10321
|
-
}
|
|
10322
|
-
/**
|
|
10323
|
-
* Roll back all completed steps in reverse order.
|
|
10324
|
-
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
10325
|
-
*/
|
|
10326
|
-
async rollback() {
|
|
10327
|
-
this.log.info("Rolling back saga", {
|
|
10328
|
-
stepsToRollback: this.completedSteps.length
|
|
10329
|
-
});
|
|
10330
|
-
const stepsReversed = [...this.completedSteps].reverse();
|
|
10331
|
-
for (const step of stepsReversed) {
|
|
10332
|
-
try {
|
|
10333
|
-
this.log.debug(`Rolling back step: ${step.name}`);
|
|
10334
|
-
await step.rollback();
|
|
10335
|
-
this.log.debug(`Step rolled back: ${step.name}`);
|
|
10336
|
-
} catch (error) {
|
|
10337
|
-
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
10338
|
-
error: error instanceof Error ? error.message : String(error)
|
|
10339
|
-
});
|
|
10340
|
-
}
|
|
10341
|
-
}
|
|
10342
|
-
this.log.info("Rollback completed", {
|
|
10343
|
-
stepsAttempted: this.completedSteps.length
|
|
10344
|
-
});
|
|
10345
|
-
}
|
|
10346
|
-
/**
|
|
10347
|
-
* Get the number of completed steps (useful for testing)
|
|
10348
|
-
*/
|
|
10349
|
-
getCompletedStepCount() {
|
|
10350
|
-
return this.completedSteps.length;
|
|
10351
|
-
}
|
|
10352
|
-
};
|
|
10353
|
-
|
|
10354
|
-
// ../git/dist/git-saga.js
|
|
10355
|
-
var GitSaga = class extends Saga {
|
|
10356
|
-
_git = null;
|
|
10357
|
-
get git() {
|
|
10358
|
-
if (!this._git) {
|
|
10359
|
-
throw new Error("git client accessed before execute() was called");
|
|
10360
|
-
}
|
|
10361
|
-
return this._git;
|
|
10362
|
-
}
|
|
10363
|
-
async execute(input) {
|
|
10364
|
-
const manager = getGitOperationManager();
|
|
10365
|
-
return manager.executeWrite(input.baseDir, async (git) => {
|
|
10366
|
-
this._git = git;
|
|
10367
|
-
return this.executeGitOperations(input);
|
|
10368
|
-
}, { signal: input.signal });
|
|
10117
|
+
async execute(input) {
|
|
10118
|
+
const manager = getGitOperationManager();
|
|
10119
|
+
return manager.executeWrite(input.baseDir, async (git) => {
|
|
10120
|
+
this._git = git;
|
|
10121
|
+
return this.executeGitOperations(input);
|
|
10122
|
+
}, { signal: input.signal });
|
|
10369
10123
|
}
|
|
10370
10124
|
};
|
|
10371
10125
|
|
|
@@ -10437,7 +10191,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
10437
10191
|
if (filesToArchive.length === 0) {
|
|
10438
10192
|
return void 0;
|
|
10439
10193
|
}
|
|
10440
|
-
const existingFiles = filesToArchive.filter((f) => (0,
|
|
10194
|
+
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs2.existsSync)(path9.join(baseDir, f)));
|
|
10441
10195
|
if (existingFiles.length === 0) {
|
|
10442
10196
|
return void 0;
|
|
10443
10197
|
}
|
|
@@ -10631,18 +10385,18 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
10631
10385
|
archivePath = null;
|
|
10632
10386
|
async execute(input) {
|
|
10633
10387
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
10634
|
-
const tmpDir = (0,
|
|
10388
|
+
const tmpDir = (0, import_node_path5.join)(repositoryPath, ".posthog", "tmp");
|
|
10635
10389
|
if (!snapshot.archiveUrl) {
|
|
10636
10390
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
10637
10391
|
}
|
|
10638
10392
|
const archiveUrl = snapshot.archiveUrl;
|
|
10639
10393
|
await this.step({
|
|
10640
10394
|
name: "create_tmp_dir",
|
|
10641
|
-
execute: () => (0,
|
|
10395
|
+
execute: () => (0, import_promises2.mkdir)(tmpDir, { recursive: true }),
|
|
10642
10396
|
rollback: async () => {
|
|
10643
10397
|
}
|
|
10644
10398
|
});
|
|
10645
|
-
const archivePath = (0,
|
|
10399
|
+
const archivePath = (0, import_node_path5.join)(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
10646
10400
|
this.archivePath = archivePath;
|
|
10647
10401
|
await this.step({
|
|
10648
10402
|
name: "download_archive",
|
|
@@ -10657,11 +10411,11 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
10657
10411
|
}
|
|
10658
10412
|
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
10659
10413
|
const binaryContent = Buffer.from(base64Content, "base64");
|
|
10660
|
-
await (0,
|
|
10414
|
+
await (0, import_promises2.writeFile)(archivePath, binaryContent);
|
|
10661
10415
|
},
|
|
10662
10416
|
rollback: async () => {
|
|
10663
10417
|
if (this.archivePath) {
|
|
10664
|
-
await (0,
|
|
10418
|
+
await (0, import_promises2.rm)(this.archivePath, { force: true }).catch(() => {
|
|
10665
10419
|
});
|
|
10666
10420
|
}
|
|
10667
10421
|
}
|
|
@@ -10677,7 +10431,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
10677
10431
|
if (!applyResult.success) {
|
|
10678
10432
|
throw new Error(`Failed to apply tree: ${applyResult.error}`);
|
|
10679
10433
|
}
|
|
10680
|
-
await (0,
|
|
10434
|
+
await (0, import_promises2.rm)(this.archivePath, { force: true }).catch(() => {
|
|
10681
10435
|
});
|
|
10682
10436
|
this.log.info("Tree snapshot applied", {
|
|
10683
10437
|
treeHash: snapshot.treeHash,
|
|
@@ -10689,9 +10443,9 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
10689
10443
|
};
|
|
10690
10444
|
|
|
10691
10445
|
// src/sagas/capture-tree-saga.ts
|
|
10692
|
-
var
|
|
10693
|
-
var
|
|
10694
|
-
var
|
|
10446
|
+
var import_node_fs3 = require("fs");
|
|
10447
|
+
var import_promises3 = require("fs/promises");
|
|
10448
|
+
var import_node_path6 = require("path");
|
|
10695
10449
|
var CaptureTreeSaga2 = class extends Saga {
|
|
10696
10450
|
sagaName = "CaptureTreeSaga";
|
|
10697
10451
|
async execute(input) {
|
|
@@ -10703,14 +10457,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
10703
10457
|
taskId,
|
|
10704
10458
|
runId
|
|
10705
10459
|
} = input;
|
|
10706
|
-
const tmpDir = (0,
|
|
10707
|
-
if ((0,
|
|
10460
|
+
const tmpDir = (0, import_node_path6.join)(repositoryPath, ".posthog", "tmp");
|
|
10461
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path6.join)(repositoryPath, ".gitmodules"))) {
|
|
10708
10462
|
this.log.warn(
|
|
10709
10463
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
10710
10464
|
);
|
|
10711
10465
|
}
|
|
10712
10466
|
const shouldArchive = !!apiClient;
|
|
10713
|
-
const archivePath = shouldArchive ? (0,
|
|
10467
|
+
const archivePath = shouldArchive ? (0, import_node_path6.join)(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
10714
10468
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
10715
10469
|
const captureResult = await gitCaptureSaga.run({
|
|
10716
10470
|
baseDir: repositoryPath,
|
|
@@ -10740,7 +10494,7 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
10740
10494
|
runId
|
|
10741
10495
|
);
|
|
10742
10496
|
} finally {
|
|
10743
|
-
await (0,
|
|
10497
|
+
await (0, import_promises3.rm)(createdArchivePath, { force: true }).catch(() => {
|
|
10744
10498
|
});
|
|
10745
10499
|
}
|
|
10746
10500
|
}
|
|
@@ -10764,7 +10518,7 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
10764
10518
|
const archiveUrl = await this.step({
|
|
10765
10519
|
name: "upload_archive",
|
|
10766
10520
|
execute: async () => {
|
|
10767
|
-
const archiveContent = await (0,
|
|
10521
|
+
const archiveContent = await (0, import_promises3.readFile)(archivePath);
|
|
10768
10522
|
const base64Content = archiveContent.toString("base64");
|
|
10769
10523
|
const artifacts = await apiClient.uploadTaskArtifacts(taskId, runId, [
|
|
10770
10524
|
{
|
|
@@ -10784,104 +10538,679 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
10784
10538
|
return void 0;
|
|
10785
10539
|
},
|
|
10786
10540
|
rollback: async () => {
|
|
10787
|
-
await (0,
|
|
10541
|
+
await (0, import_promises3.rm)(archivePath, { force: true }).catch(() => {
|
|
10788
10542
|
});
|
|
10789
10543
|
}
|
|
10790
10544
|
});
|
|
10791
10545
|
return archiveUrl;
|
|
10792
10546
|
}
|
|
10793
|
-
};
|
|
10794
|
-
|
|
10795
|
-
// src/tree-tracker.ts
|
|
10796
|
-
var TreeTracker = class {
|
|
10797
|
-
repositoryPath;
|
|
10798
|
-
taskId;
|
|
10799
|
-
runId;
|
|
10800
|
-
apiClient;
|
|
10801
|
-
logger;
|
|
10802
|
-
lastTreeHash = null;
|
|
10803
|
-
constructor(config) {
|
|
10804
|
-
this.repositoryPath = config.repositoryPath;
|
|
10805
|
-
this.taskId = config.taskId;
|
|
10806
|
-
this.runId = config.runId;
|
|
10807
|
-
this.apiClient = config.apiClient;
|
|
10808
|
-
this.logger = config.logger || new Logger({ debug: false, prefix: "[TreeTracker]" });
|
|
10547
|
+
};
|
|
10548
|
+
|
|
10549
|
+
// src/tree-tracker.ts
|
|
10550
|
+
var TreeTracker = class {
|
|
10551
|
+
repositoryPath;
|
|
10552
|
+
taskId;
|
|
10553
|
+
runId;
|
|
10554
|
+
apiClient;
|
|
10555
|
+
logger;
|
|
10556
|
+
lastTreeHash = null;
|
|
10557
|
+
constructor(config) {
|
|
10558
|
+
this.repositoryPath = config.repositoryPath;
|
|
10559
|
+
this.taskId = config.taskId;
|
|
10560
|
+
this.runId = config.runId;
|
|
10561
|
+
this.apiClient = config.apiClient;
|
|
10562
|
+
this.logger = config.logger || new Logger({ debug: false, prefix: "[TreeTracker]" });
|
|
10563
|
+
}
|
|
10564
|
+
/**
|
|
10565
|
+
* Capture current working tree state as a snapshot.
|
|
10566
|
+
* Uses a temporary index to avoid modifying user's staging area.
|
|
10567
|
+
* Uses Saga pattern for atomic operation with automatic cleanup on failure.
|
|
10568
|
+
*/
|
|
10569
|
+
async captureTree(options) {
|
|
10570
|
+
const saga = new CaptureTreeSaga2(this.logger);
|
|
10571
|
+
const result = await saga.run({
|
|
10572
|
+
repositoryPath: this.repositoryPath,
|
|
10573
|
+
taskId: this.taskId,
|
|
10574
|
+
runId: this.runId,
|
|
10575
|
+
apiClient: this.apiClient,
|
|
10576
|
+
lastTreeHash: this.lastTreeHash,
|
|
10577
|
+
interrupted: options?.interrupted
|
|
10578
|
+
});
|
|
10579
|
+
if (!result.success) {
|
|
10580
|
+
this.logger.error("Failed to capture tree", {
|
|
10581
|
+
error: result.error,
|
|
10582
|
+
failedStep: result.failedStep
|
|
10583
|
+
});
|
|
10584
|
+
throw new Error(
|
|
10585
|
+
`Failed to capture tree at step '${result.failedStep}': ${result.error}`
|
|
10586
|
+
);
|
|
10587
|
+
}
|
|
10588
|
+
if (result.data.newTreeHash !== null) {
|
|
10589
|
+
this.lastTreeHash = result.data.newTreeHash;
|
|
10590
|
+
}
|
|
10591
|
+
return result.data.snapshot;
|
|
10592
|
+
}
|
|
10593
|
+
/**
|
|
10594
|
+
* Download and apply a tree snapshot.
|
|
10595
|
+
* Uses Saga pattern for atomic operation with rollback on failure.
|
|
10596
|
+
*/
|
|
10597
|
+
async applyTreeSnapshot(snapshot) {
|
|
10598
|
+
if (!this.apiClient) {
|
|
10599
|
+
throw new Error("Cannot apply snapshot: API client not configured");
|
|
10600
|
+
}
|
|
10601
|
+
if (!snapshot.archiveUrl) {
|
|
10602
|
+
this.logger.warn("Cannot apply snapshot: no archive URL", {
|
|
10603
|
+
treeHash: snapshot.treeHash,
|
|
10604
|
+
changes: snapshot.changes.length
|
|
10605
|
+
});
|
|
10606
|
+
throw new Error("Cannot apply snapshot: no archive URL");
|
|
10607
|
+
}
|
|
10608
|
+
const saga = new ApplySnapshotSaga(this.logger);
|
|
10609
|
+
const result = await saga.run({
|
|
10610
|
+
snapshot,
|
|
10611
|
+
repositoryPath: this.repositoryPath,
|
|
10612
|
+
apiClient: this.apiClient,
|
|
10613
|
+
taskId: this.taskId,
|
|
10614
|
+
runId: this.runId
|
|
10615
|
+
});
|
|
10616
|
+
if (!result.success) {
|
|
10617
|
+
this.logger.error("Failed to apply tree snapshot", {
|
|
10618
|
+
error: result.error,
|
|
10619
|
+
failedStep: result.failedStep,
|
|
10620
|
+
treeHash: snapshot.treeHash
|
|
10621
|
+
});
|
|
10622
|
+
throw new Error(
|
|
10623
|
+
`Failed to apply snapshot at step '${result.failedStep}': ${result.error}`
|
|
10624
|
+
);
|
|
10625
|
+
}
|
|
10626
|
+
this.lastTreeHash = result.data.treeHash;
|
|
10627
|
+
}
|
|
10628
|
+
/**
|
|
10629
|
+
* Get the last captured tree hash.
|
|
10630
|
+
*/
|
|
10631
|
+
getLastTreeHash() {
|
|
10632
|
+
return this.lastTreeHash;
|
|
10633
|
+
}
|
|
10634
|
+
/**
|
|
10635
|
+
* Set the last tree hash (used when resuming).
|
|
10636
|
+
*/
|
|
10637
|
+
setLastTreeHash(hash) {
|
|
10638
|
+
this.lastTreeHash = hash;
|
|
10639
|
+
}
|
|
10640
|
+
};
|
|
10641
|
+
|
|
10642
|
+
// src/sagas/resume-saga.ts
|
|
10643
|
+
var ResumeSaga = class extends Saga {
|
|
10644
|
+
sagaName = "ResumeSaga";
|
|
10645
|
+
async execute(input) {
|
|
10646
|
+
const { taskId, runId, repositoryPath, apiClient } = input;
|
|
10647
|
+
const logger = input.logger || new Logger({ debug: false, prefix: "[Resume]" });
|
|
10648
|
+
const taskRun = await this.readOnlyStep(
|
|
10649
|
+
"fetch_task_run",
|
|
10650
|
+
() => apiClient.getTaskRun(taskId, runId)
|
|
10651
|
+
);
|
|
10652
|
+
if (!taskRun.log_url) {
|
|
10653
|
+
this.log.info("No log URL found, starting fresh");
|
|
10654
|
+
return this.emptyResult();
|
|
10655
|
+
}
|
|
10656
|
+
const entries = await this.readOnlyStep(
|
|
10657
|
+
"fetch_logs",
|
|
10658
|
+
() => apiClient.fetchTaskRunLogs(taskRun)
|
|
10659
|
+
);
|
|
10660
|
+
if (entries.length === 0) {
|
|
10661
|
+
this.log.info("No log entries found, starting fresh");
|
|
10662
|
+
return this.emptyResult();
|
|
10663
|
+
}
|
|
10664
|
+
this.log.info("Fetched log entries", { count: entries.length });
|
|
10665
|
+
const latestSnapshot = await this.readOnlyStep(
|
|
10666
|
+
"find_snapshot",
|
|
10667
|
+
() => Promise.resolve(this.findLatestTreeSnapshot(entries))
|
|
10668
|
+
);
|
|
10669
|
+
let snapshotApplied = false;
|
|
10670
|
+
if (latestSnapshot?.archiveUrl && repositoryPath) {
|
|
10671
|
+
this.log.info("Found tree snapshot", {
|
|
10672
|
+
treeHash: latestSnapshot.treeHash,
|
|
10673
|
+
hasArchiveUrl: true,
|
|
10674
|
+
changes: latestSnapshot.changes?.length ?? 0,
|
|
10675
|
+
interrupted: latestSnapshot.interrupted
|
|
10676
|
+
});
|
|
10677
|
+
await this.step({
|
|
10678
|
+
name: "apply_snapshot",
|
|
10679
|
+
execute: async () => {
|
|
10680
|
+
const treeTracker = new TreeTracker({
|
|
10681
|
+
repositoryPath,
|
|
10682
|
+
taskId,
|
|
10683
|
+
runId,
|
|
10684
|
+
apiClient,
|
|
10685
|
+
logger: logger.child("TreeTracker")
|
|
10686
|
+
});
|
|
10687
|
+
try {
|
|
10688
|
+
await treeTracker.applyTreeSnapshot(latestSnapshot);
|
|
10689
|
+
treeTracker.setLastTreeHash(latestSnapshot.treeHash);
|
|
10690
|
+
snapshotApplied = true;
|
|
10691
|
+
this.log.info("Tree snapshot applied successfully", {
|
|
10692
|
+
treeHash: latestSnapshot.treeHash
|
|
10693
|
+
});
|
|
10694
|
+
} catch (error) {
|
|
10695
|
+
this.log.warn(
|
|
10696
|
+
"Failed to apply tree snapshot, continuing without it",
|
|
10697
|
+
{
|
|
10698
|
+
error: error instanceof Error ? error.message : String(error),
|
|
10699
|
+
treeHash: latestSnapshot.treeHash
|
|
10700
|
+
}
|
|
10701
|
+
);
|
|
10702
|
+
}
|
|
10703
|
+
},
|
|
10704
|
+
rollback: async () => {
|
|
10705
|
+
}
|
|
10706
|
+
});
|
|
10707
|
+
} else if (latestSnapshot?.archiveUrl && !repositoryPath) {
|
|
10708
|
+
this.log.warn(
|
|
10709
|
+
"Snapshot found but no repositoryPath configured - files cannot be restored",
|
|
10710
|
+
{
|
|
10711
|
+
treeHash: latestSnapshot.treeHash,
|
|
10712
|
+
changes: latestSnapshot.changes?.length ?? 0
|
|
10713
|
+
}
|
|
10714
|
+
);
|
|
10715
|
+
} else if (latestSnapshot) {
|
|
10716
|
+
this.log.warn(
|
|
10717
|
+
"Snapshot found but has no archive URL - files cannot be restored",
|
|
10718
|
+
{
|
|
10719
|
+
treeHash: latestSnapshot.treeHash,
|
|
10720
|
+
changes: latestSnapshot.changes?.length ?? 0
|
|
10721
|
+
}
|
|
10722
|
+
);
|
|
10723
|
+
}
|
|
10724
|
+
const conversation = await this.readOnlyStep(
|
|
10725
|
+
"rebuild_conversation",
|
|
10726
|
+
() => Promise.resolve(this.rebuildConversation(entries))
|
|
10727
|
+
);
|
|
10728
|
+
const lastDevice = await this.readOnlyStep(
|
|
10729
|
+
"find_device",
|
|
10730
|
+
() => Promise.resolve(this.findLastDeviceInfo(entries))
|
|
10731
|
+
);
|
|
10732
|
+
this.log.info("Resume state rebuilt", {
|
|
10733
|
+
turns: conversation.length,
|
|
10734
|
+
hasSnapshot: !!latestSnapshot,
|
|
10735
|
+
snapshotApplied,
|
|
10736
|
+
interrupted: latestSnapshot?.interrupted ?? false
|
|
10737
|
+
});
|
|
10738
|
+
return {
|
|
10739
|
+
conversation,
|
|
10740
|
+
latestSnapshot,
|
|
10741
|
+
snapshotApplied,
|
|
10742
|
+
interrupted: latestSnapshot?.interrupted ?? false,
|
|
10743
|
+
lastDevice,
|
|
10744
|
+
logEntryCount: entries.length
|
|
10745
|
+
};
|
|
10746
|
+
}
|
|
10747
|
+
emptyResult() {
|
|
10748
|
+
return {
|
|
10749
|
+
conversation: [],
|
|
10750
|
+
latestSnapshot: null,
|
|
10751
|
+
snapshotApplied: false,
|
|
10752
|
+
interrupted: false,
|
|
10753
|
+
logEntryCount: 0
|
|
10754
|
+
};
|
|
10755
|
+
}
|
|
10756
|
+
findLatestTreeSnapshot(entries) {
|
|
10757
|
+
const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT}`;
|
|
10758
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
10759
|
+
const entry = entries[i];
|
|
10760
|
+
const method = entry.notification?.method;
|
|
10761
|
+
if (method === sdkPrefixedMethod || method === POSTHOG_NOTIFICATIONS.TREE_SNAPSHOT) {
|
|
10762
|
+
const params = entry.notification.params;
|
|
10763
|
+
if (params?.treeHash) {
|
|
10764
|
+
return params;
|
|
10765
|
+
}
|
|
10766
|
+
}
|
|
10767
|
+
}
|
|
10768
|
+
return null;
|
|
10769
|
+
}
|
|
10770
|
+
findLastDeviceInfo(entries) {
|
|
10771
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
10772
|
+
const entry = entries[i];
|
|
10773
|
+
const params = entry.notification?.params;
|
|
10774
|
+
if (params?.device) {
|
|
10775
|
+
return params.device;
|
|
10776
|
+
}
|
|
10777
|
+
}
|
|
10778
|
+
return void 0;
|
|
10779
|
+
}
|
|
10780
|
+
rebuildConversation(entries) {
|
|
10781
|
+
const turns = [];
|
|
10782
|
+
let currentAssistantContent = [];
|
|
10783
|
+
let currentToolCalls = [];
|
|
10784
|
+
for (const entry of entries) {
|
|
10785
|
+
const method = entry.notification?.method;
|
|
10786
|
+
const params = entry.notification?.params;
|
|
10787
|
+
if (method === "session/update" && params?.update) {
|
|
10788
|
+
const update = params.update;
|
|
10789
|
+
const sessionUpdate = update.sessionUpdate;
|
|
10790
|
+
switch (sessionUpdate) {
|
|
10791
|
+
case "user_message":
|
|
10792
|
+
case "user_message_chunk": {
|
|
10793
|
+
if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {
|
|
10794
|
+
turns.push({
|
|
10795
|
+
role: "assistant",
|
|
10796
|
+
content: currentAssistantContent,
|
|
10797
|
+
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0
|
|
10798
|
+
});
|
|
10799
|
+
currentAssistantContent = [];
|
|
10800
|
+
currentToolCalls = [];
|
|
10801
|
+
}
|
|
10802
|
+
const content = update.content;
|
|
10803
|
+
const contentArray = Array.isArray(content) ? content : [content];
|
|
10804
|
+
turns.push({
|
|
10805
|
+
role: "user",
|
|
10806
|
+
content: contentArray
|
|
10807
|
+
});
|
|
10808
|
+
break;
|
|
10809
|
+
}
|
|
10810
|
+
case "agent_message": {
|
|
10811
|
+
const content = update.content;
|
|
10812
|
+
if (content) {
|
|
10813
|
+
if (content.type === "text" && currentAssistantContent.length > 0 && currentAssistantContent[currentAssistantContent.length - 1].type === "text") {
|
|
10814
|
+
const lastBlock = currentAssistantContent[currentAssistantContent.length - 1];
|
|
10815
|
+
lastBlock.text += content.text;
|
|
10816
|
+
} else {
|
|
10817
|
+
currentAssistantContent.push(content);
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
break;
|
|
10821
|
+
}
|
|
10822
|
+
case "agent_message_chunk": {
|
|
10823
|
+
const content = update.content;
|
|
10824
|
+
if (content) {
|
|
10825
|
+
if (content.type === "text" && currentAssistantContent.length > 0 && currentAssistantContent[currentAssistantContent.length - 1].type === "text") {
|
|
10826
|
+
const lastBlock = currentAssistantContent[currentAssistantContent.length - 1];
|
|
10827
|
+
lastBlock.text += content.text;
|
|
10828
|
+
} else {
|
|
10829
|
+
currentAssistantContent.push(content);
|
|
10830
|
+
}
|
|
10831
|
+
}
|
|
10832
|
+
break;
|
|
10833
|
+
}
|
|
10834
|
+
case "tool_call":
|
|
10835
|
+
case "tool_call_update": {
|
|
10836
|
+
const meta = update._meta?.claudeCode;
|
|
10837
|
+
if (meta) {
|
|
10838
|
+
const toolCallId = meta.toolCallId;
|
|
10839
|
+
const toolName = meta.toolName;
|
|
10840
|
+
const toolInput = meta.toolInput;
|
|
10841
|
+
const toolResponse = meta.toolResponse;
|
|
10842
|
+
if (toolCallId && toolName) {
|
|
10843
|
+
let toolCall = currentToolCalls.find(
|
|
10844
|
+
(tc) => tc.toolCallId === toolCallId
|
|
10845
|
+
);
|
|
10846
|
+
if (!toolCall) {
|
|
10847
|
+
toolCall = {
|
|
10848
|
+
toolCallId,
|
|
10849
|
+
toolName,
|
|
10850
|
+
input: toolInput
|
|
10851
|
+
};
|
|
10852
|
+
currentToolCalls.push(toolCall);
|
|
10853
|
+
}
|
|
10854
|
+
if (toolResponse !== void 0) {
|
|
10855
|
+
toolCall.result = toolResponse;
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
}
|
|
10859
|
+
break;
|
|
10860
|
+
}
|
|
10861
|
+
case "tool_result": {
|
|
10862
|
+
const meta = update._meta?.claudeCode;
|
|
10863
|
+
if (meta) {
|
|
10864
|
+
const toolCallId = meta.toolCallId;
|
|
10865
|
+
const toolResponse = meta.toolResponse;
|
|
10866
|
+
if (toolCallId) {
|
|
10867
|
+
const toolCall = currentToolCalls.find(
|
|
10868
|
+
(tc) => tc.toolCallId === toolCallId
|
|
10869
|
+
);
|
|
10870
|
+
if (toolCall && toolResponse !== void 0) {
|
|
10871
|
+
toolCall.result = toolResponse;
|
|
10872
|
+
}
|
|
10873
|
+
}
|
|
10874
|
+
}
|
|
10875
|
+
break;
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
if (currentAssistantContent.length > 0 || currentToolCalls.length > 0) {
|
|
10881
|
+
turns.push({
|
|
10882
|
+
role: "assistant",
|
|
10883
|
+
content: currentAssistantContent,
|
|
10884
|
+
toolCalls: currentToolCalls.length > 0 ? currentToolCalls : void 0
|
|
10885
|
+
});
|
|
10886
|
+
}
|
|
10887
|
+
return turns;
|
|
10888
|
+
}
|
|
10889
|
+
};
|
|
10890
|
+
|
|
10891
|
+
// src/resume.ts
|
|
10892
|
+
async function resumeFromLog(config) {
|
|
10893
|
+
const logger = config.logger || new Logger({ debug: false, prefix: "[Resume]" });
|
|
10894
|
+
logger.info("Resuming from log", {
|
|
10895
|
+
taskId: config.taskId,
|
|
10896
|
+
runId: config.runId
|
|
10897
|
+
});
|
|
10898
|
+
const saga = new ResumeSaga(logger);
|
|
10899
|
+
const result = await saga.run({
|
|
10900
|
+
taskId: config.taskId,
|
|
10901
|
+
runId: config.runId,
|
|
10902
|
+
repositoryPath: config.repositoryPath,
|
|
10903
|
+
apiClient: config.apiClient,
|
|
10904
|
+
logger
|
|
10905
|
+
});
|
|
10906
|
+
if (!result.success) {
|
|
10907
|
+
logger.error("Failed to resume from log", {
|
|
10908
|
+
error: result.error,
|
|
10909
|
+
failedStep: result.failedStep
|
|
10910
|
+
});
|
|
10911
|
+
throw new Error(
|
|
10912
|
+
`Failed to resume at step '${result.failedStep}': ${result.error}`
|
|
10913
|
+
);
|
|
10914
|
+
}
|
|
10915
|
+
return {
|
|
10916
|
+
conversation: result.data.conversation,
|
|
10917
|
+
latestSnapshot: result.data.latestSnapshot,
|
|
10918
|
+
snapshotApplied: result.data.snapshotApplied,
|
|
10919
|
+
interrupted: result.data.interrupted,
|
|
10920
|
+
lastDevice: result.data.lastDevice,
|
|
10921
|
+
logEntryCount: result.data.logEntryCount
|
|
10922
|
+
};
|
|
10923
|
+
}
|
|
10924
|
+
|
|
10925
|
+
// src/session-log-writer.ts
|
|
10926
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
10927
|
+
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
10928
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
10929
|
+
var SessionLogWriter = class _SessionLogWriter {
|
|
10930
|
+
static FLUSH_DEBOUNCE_MS = 500;
|
|
10931
|
+
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
10932
|
+
static MAX_FLUSH_RETRIES = 10;
|
|
10933
|
+
static MAX_RETRY_DELAY_MS = 3e4;
|
|
10934
|
+
static SESSIONS_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
10935
|
+
posthogAPI;
|
|
10936
|
+
pendingEntries = /* @__PURE__ */ new Map();
|
|
10937
|
+
flushTimeouts = /* @__PURE__ */ new Map();
|
|
10938
|
+
lastFlushAttemptTime = /* @__PURE__ */ new Map();
|
|
10939
|
+
retryCounts = /* @__PURE__ */ new Map();
|
|
10940
|
+
sessions = /* @__PURE__ */ new Map();
|
|
10941
|
+
logger;
|
|
10942
|
+
localCachePath;
|
|
10943
|
+
constructor(options = {}) {
|
|
10944
|
+
this.posthogAPI = options.posthogAPI;
|
|
10945
|
+
this.localCachePath = options.localCachePath;
|
|
10946
|
+
this.logger = options.logger ?? new Logger({ debug: false, prefix: "[SessionLogWriter]" });
|
|
10947
|
+
}
|
|
10948
|
+
async flushAll() {
|
|
10949
|
+
const sessionIds = [...this.sessions.keys()];
|
|
10950
|
+
const flushPromises = [];
|
|
10951
|
+
for (const sessionId of sessionIds) {
|
|
10952
|
+
flushPromises.push(this.flush(sessionId));
|
|
10953
|
+
}
|
|
10954
|
+
await Promise.all(flushPromises);
|
|
10955
|
+
}
|
|
10956
|
+
register(sessionId, context) {
|
|
10957
|
+
if (this.sessions.has(sessionId)) {
|
|
10958
|
+
return;
|
|
10959
|
+
}
|
|
10960
|
+
this.logger.info("Session registered", {
|
|
10961
|
+
taskId: context.taskId,
|
|
10962
|
+
runId: context.runId
|
|
10963
|
+
});
|
|
10964
|
+
this.sessions.set(sessionId, { context });
|
|
10965
|
+
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
10966
|
+
if (this.localCachePath) {
|
|
10967
|
+
const sessionDir = import_node_path7.default.join(
|
|
10968
|
+
this.localCachePath,
|
|
10969
|
+
"sessions",
|
|
10970
|
+
context.runId
|
|
10971
|
+
);
|
|
10972
|
+
try {
|
|
10973
|
+
import_node_fs4.default.mkdirSync(sessionDir, { recursive: true });
|
|
10974
|
+
} catch (error) {
|
|
10975
|
+
this.logger.warn("Failed to create local cache directory", {
|
|
10976
|
+
sessionDir,
|
|
10977
|
+
error
|
|
10978
|
+
});
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
}
|
|
10982
|
+
isRegistered(sessionId) {
|
|
10983
|
+
return this.sessions.has(sessionId);
|
|
10984
|
+
}
|
|
10985
|
+
appendRawLine(sessionId, line) {
|
|
10986
|
+
const session = this.sessions.get(sessionId);
|
|
10987
|
+
if (!session) {
|
|
10988
|
+
this.logger.warn("appendRawLine called for unregistered session", {
|
|
10989
|
+
sessionId
|
|
10990
|
+
});
|
|
10991
|
+
return;
|
|
10992
|
+
}
|
|
10993
|
+
try {
|
|
10994
|
+
const message = JSON.parse(line);
|
|
10995
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10996
|
+
if (this.isAgentMessageChunk(message)) {
|
|
10997
|
+
const text2 = this.extractChunkText(message);
|
|
10998
|
+
if (text2) {
|
|
10999
|
+
if (!session.chunkBuffer) {
|
|
11000
|
+
session.chunkBuffer = { text: text2, firstTimestamp: timestamp };
|
|
11001
|
+
} else {
|
|
11002
|
+
session.chunkBuffer.text += text2;
|
|
11003
|
+
}
|
|
11004
|
+
}
|
|
11005
|
+
return;
|
|
11006
|
+
}
|
|
11007
|
+
this.emitCoalescedMessage(sessionId, session);
|
|
11008
|
+
const nonChunkAgentText = this.extractAgentMessageText(message);
|
|
11009
|
+
if (nonChunkAgentText) {
|
|
11010
|
+
session.lastAgentMessage = nonChunkAgentText;
|
|
11011
|
+
}
|
|
11012
|
+
const entry = {
|
|
11013
|
+
type: "notification",
|
|
11014
|
+
timestamp,
|
|
11015
|
+
notification: message
|
|
11016
|
+
};
|
|
11017
|
+
this.writeToLocalCache(sessionId, entry);
|
|
11018
|
+
if (this.posthogAPI) {
|
|
11019
|
+
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
11020
|
+
pending.push(entry);
|
|
11021
|
+
this.pendingEntries.set(sessionId, pending);
|
|
11022
|
+
this.scheduleFlush(sessionId);
|
|
11023
|
+
}
|
|
11024
|
+
} catch {
|
|
11025
|
+
this.logger.warn("Failed to parse raw line for persistence", {
|
|
11026
|
+
taskId: session.context.taskId,
|
|
11027
|
+
runId: session.context.runId,
|
|
11028
|
+
lineLength: line.length
|
|
11029
|
+
});
|
|
11030
|
+
}
|
|
11031
|
+
}
|
|
11032
|
+
async flush(sessionId) {
|
|
11033
|
+
const session = this.sessions.get(sessionId);
|
|
11034
|
+
if (!session) {
|
|
11035
|
+
this.logger.warn("flush: no session found", { sessionId });
|
|
11036
|
+
return;
|
|
11037
|
+
}
|
|
11038
|
+
this.emitCoalescedMessage(sessionId, session);
|
|
11039
|
+
const pending = this.pendingEntries.get(sessionId);
|
|
11040
|
+
if (!this.posthogAPI || !pending?.length) {
|
|
11041
|
+
return;
|
|
11042
|
+
}
|
|
11043
|
+
this.pendingEntries.delete(sessionId);
|
|
11044
|
+
const timeout = this.flushTimeouts.get(sessionId);
|
|
11045
|
+
if (timeout) {
|
|
11046
|
+
clearTimeout(timeout);
|
|
11047
|
+
this.flushTimeouts.delete(sessionId);
|
|
11048
|
+
}
|
|
11049
|
+
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
11050
|
+
try {
|
|
11051
|
+
await this.posthogAPI.appendTaskRunLog(
|
|
11052
|
+
session.context.taskId,
|
|
11053
|
+
session.context.runId,
|
|
11054
|
+
pending
|
|
11055
|
+
);
|
|
11056
|
+
this.retryCounts.set(sessionId, 0);
|
|
11057
|
+
} catch (error) {
|
|
11058
|
+
const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
|
|
11059
|
+
this.retryCounts.set(sessionId, retryCount);
|
|
11060
|
+
if (retryCount >= _SessionLogWriter.MAX_FLUSH_RETRIES) {
|
|
11061
|
+
this.logger.error(
|
|
11062
|
+
`Dropping ${pending.length} session log entries after ${retryCount} failed flush attempts`,
|
|
11063
|
+
{
|
|
11064
|
+
taskId: session.context.taskId,
|
|
11065
|
+
runId: session.context.runId,
|
|
11066
|
+
error
|
|
11067
|
+
}
|
|
11068
|
+
);
|
|
11069
|
+
this.retryCounts.set(sessionId, 0);
|
|
11070
|
+
} else {
|
|
11071
|
+
if (retryCount === 1) {
|
|
11072
|
+
this.logger.warn(
|
|
11073
|
+
`Failed to persist session logs, will retry (up to ${_SessionLogWriter.MAX_FLUSH_RETRIES} attempts)`,
|
|
11074
|
+
{
|
|
11075
|
+
taskId: session.context.taskId,
|
|
11076
|
+
runId: session.context.runId,
|
|
11077
|
+
error: error instanceof Error ? error.message : String(error)
|
|
11078
|
+
}
|
|
11079
|
+
);
|
|
11080
|
+
}
|
|
11081
|
+
const currentPending = this.pendingEntries.get(sessionId) ?? [];
|
|
11082
|
+
this.pendingEntries.set(sessionId, [...pending, ...currentPending]);
|
|
11083
|
+
this.scheduleFlush(sessionId);
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
}
|
|
11087
|
+
isAgentMessageChunk(message) {
|
|
11088
|
+
if (message.method !== "session/update") return false;
|
|
11089
|
+
const params = message.params;
|
|
11090
|
+
const update = params?.update;
|
|
11091
|
+
return update?.sessionUpdate === "agent_message_chunk";
|
|
11092
|
+
}
|
|
11093
|
+
extractChunkText(message) {
|
|
11094
|
+
const params = message.params;
|
|
11095
|
+
const update = params?.update;
|
|
11096
|
+
const content = update?.content;
|
|
11097
|
+
if (content?.type === "text" && content.text) {
|
|
11098
|
+
return content.text;
|
|
11099
|
+
}
|
|
11100
|
+
return "";
|
|
11101
|
+
}
|
|
11102
|
+
emitCoalescedMessage(sessionId, session) {
|
|
11103
|
+
if (!session.chunkBuffer) return;
|
|
11104
|
+
const { text: text2, firstTimestamp } = session.chunkBuffer;
|
|
11105
|
+
session.chunkBuffer = void 0;
|
|
11106
|
+
session.lastAgentMessage = text2;
|
|
11107
|
+
const entry = {
|
|
11108
|
+
type: "notification",
|
|
11109
|
+
timestamp: firstTimestamp,
|
|
11110
|
+
notification: {
|
|
11111
|
+
jsonrpc: "2.0",
|
|
11112
|
+
method: "session/update",
|
|
11113
|
+
params: {
|
|
11114
|
+
update: {
|
|
11115
|
+
sessionUpdate: "agent_message",
|
|
11116
|
+
content: { type: "text", text: text2 }
|
|
11117
|
+
}
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
};
|
|
11121
|
+
this.writeToLocalCache(sessionId, entry);
|
|
11122
|
+
if (this.posthogAPI) {
|
|
11123
|
+
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
11124
|
+
pending.push(entry);
|
|
11125
|
+
this.pendingEntries.set(sessionId, pending);
|
|
11126
|
+
this.scheduleFlush(sessionId);
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
getLastAgentMessage(sessionId) {
|
|
11130
|
+
return this.sessions.get(sessionId)?.lastAgentMessage;
|
|
10809
11131
|
}
|
|
10810
|
-
|
|
10811
|
-
|
|
10812
|
-
|
|
10813
|
-
* Uses Saga pattern for atomic operation with automatic cleanup on failure.
|
|
10814
|
-
*/
|
|
10815
|
-
async captureTree(options) {
|
|
10816
|
-
const saga = new CaptureTreeSaga2(this.logger);
|
|
10817
|
-
const result = await saga.run({
|
|
10818
|
-
repositoryPath: this.repositoryPath,
|
|
10819
|
-
taskId: this.taskId,
|
|
10820
|
-
runId: this.runId,
|
|
10821
|
-
apiClient: this.apiClient,
|
|
10822
|
-
lastTreeHash: this.lastTreeHash,
|
|
10823
|
-
interrupted: options?.interrupted
|
|
10824
|
-
});
|
|
10825
|
-
if (!result.success) {
|
|
10826
|
-
this.logger.error("Failed to capture tree", {
|
|
10827
|
-
error: result.error,
|
|
10828
|
-
failedStep: result.failedStep
|
|
10829
|
-
});
|
|
10830
|
-
throw new Error(
|
|
10831
|
-
`Failed to capture tree at step '${result.failedStep}': ${result.error}`
|
|
10832
|
-
);
|
|
11132
|
+
extractAgentMessageText(message) {
|
|
11133
|
+
if (message.method !== "session/update") {
|
|
11134
|
+
return null;
|
|
10833
11135
|
}
|
|
10834
|
-
|
|
10835
|
-
|
|
11136
|
+
const params = message.params;
|
|
11137
|
+
const update = params?.update;
|
|
11138
|
+
if (update?.sessionUpdate !== "agent_message") {
|
|
11139
|
+
return null;
|
|
10836
11140
|
}
|
|
10837
|
-
|
|
10838
|
-
|
|
10839
|
-
|
|
10840
|
-
|
|
10841
|
-
* Uses Saga pattern for atomic operation with rollback on failure.
|
|
10842
|
-
*/
|
|
10843
|
-
async applyTreeSnapshot(snapshot) {
|
|
10844
|
-
if (!this.apiClient) {
|
|
10845
|
-
throw new Error("Cannot apply snapshot: API client not configured");
|
|
11141
|
+
const content = update.content;
|
|
11142
|
+
if (content?.type === "text" && typeof content.text === "string") {
|
|
11143
|
+
const trimmed2 = content.text.trim();
|
|
11144
|
+
return trimmed2.length > 0 ? trimmed2 : null;
|
|
10846
11145
|
}
|
|
10847
|
-
if (
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
changes: snapshot.changes.length
|
|
10851
|
-
});
|
|
10852
|
-
throw new Error("Cannot apply snapshot: no archive URL");
|
|
11146
|
+
if (typeof update.message === "string") {
|
|
11147
|
+
const trimmed2 = update.message.trim();
|
|
11148
|
+
return trimmed2.length > 0 ? trimmed2 : null;
|
|
10853
11149
|
}
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
});
|
|
10868
|
-
throw new Error(
|
|
10869
|
-
`Failed to apply snapshot at step '${result.failedStep}': ${result.error}`
|
|
11150
|
+
return null;
|
|
11151
|
+
}
|
|
11152
|
+
scheduleFlush(sessionId) {
|
|
11153
|
+
const existing = this.flushTimeouts.get(sessionId);
|
|
11154
|
+
if (existing) clearTimeout(existing);
|
|
11155
|
+
const retryCount = this.retryCounts.get(sessionId) ?? 0;
|
|
11156
|
+
const lastAttempt = this.lastFlushAttemptTime.get(sessionId) ?? 0;
|
|
11157
|
+
const elapsed = Date.now() - lastAttempt;
|
|
11158
|
+
let delay3;
|
|
11159
|
+
if (retryCount > 0) {
|
|
11160
|
+
delay3 = Math.min(
|
|
11161
|
+
_SessionLogWriter.FLUSH_DEBOUNCE_MS * 2 ** retryCount,
|
|
11162
|
+
_SessionLogWriter.MAX_RETRY_DELAY_MS
|
|
10870
11163
|
);
|
|
11164
|
+
} else if (elapsed >= _SessionLogWriter.FLUSH_MAX_INTERVAL_MS) {
|
|
11165
|
+
delay3 = 0;
|
|
11166
|
+
} else {
|
|
11167
|
+
delay3 = _SessionLogWriter.FLUSH_DEBOUNCE_MS;
|
|
10871
11168
|
}
|
|
10872
|
-
|
|
11169
|
+
const timeout = setTimeout(() => this.flush(sessionId), delay3);
|
|
11170
|
+
this.flushTimeouts.set(sessionId, timeout);
|
|
10873
11171
|
}
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
11172
|
+
writeToLocalCache(sessionId, entry) {
|
|
11173
|
+
if (!this.localCachePath) return;
|
|
11174
|
+
const session = this.sessions.get(sessionId);
|
|
11175
|
+
if (!session) return;
|
|
11176
|
+
const logPath = import_node_path7.default.join(
|
|
11177
|
+
this.localCachePath,
|
|
11178
|
+
"sessions",
|
|
11179
|
+
session.context.runId,
|
|
11180
|
+
"logs.ndjson"
|
|
11181
|
+
);
|
|
11182
|
+
try {
|
|
11183
|
+
import_node_fs4.default.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
11184
|
+
`);
|
|
11185
|
+
} catch (error) {
|
|
11186
|
+
this.logger.warn("Failed to write to local cache", {
|
|
11187
|
+
taskId: session.context.taskId,
|
|
11188
|
+
runId: session.context.runId,
|
|
11189
|
+
logPath,
|
|
11190
|
+
error
|
|
11191
|
+
});
|
|
11192
|
+
}
|
|
10879
11193
|
}
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
11194
|
+
static async cleanupOldSessions(localCachePath) {
|
|
11195
|
+
const sessionsDir = import_node_path7.default.join(localCachePath, "sessions");
|
|
11196
|
+
let deleted = 0;
|
|
11197
|
+
try {
|
|
11198
|
+
const entries = await import_promises4.default.readdir(sessionsDir);
|
|
11199
|
+
const now = Date.now();
|
|
11200
|
+
for (const entry of entries) {
|
|
11201
|
+
const entryPath = import_node_path7.default.join(sessionsDir, entry);
|
|
11202
|
+
try {
|
|
11203
|
+
const stats = await import_promises4.default.stat(entryPath);
|
|
11204
|
+
if (stats.isDirectory() && now - stats.birthtimeMs > _SessionLogWriter.SESSIONS_MAX_AGE_MS) {
|
|
11205
|
+
await import_promises4.default.rm(entryPath, { recursive: true, force: true });
|
|
11206
|
+
deleted++;
|
|
11207
|
+
}
|
|
11208
|
+
} catch {
|
|
11209
|
+
}
|
|
11210
|
+
}
|
|
11211
|
+
} catch {
|
|
11212
|
+
}
|
|
11213
|
+
return deleted;
|
|
10885
11214
|
}
|
|
10886
11215
|
};
|
|
10887
11216
|
|
|
@@ -11094,7 +11423,7 @@ function createTappedWritableStream2(underlying, onMessage, logger) {
|
|
|
11094
11423
|
}
|
|
11095
11424
|
});
|
|
11096
11425
|
}
|
|
11097
|
-
var AgentServer = class {
|
|
11426
|
+
var AgentServer = class _AgentServer {
|
|
11098
11427
|
config;
|
|
11099
11428
|
logger;
|
|
11100
11429
|
server = null;
|
|
@@ -11103,6 +11432,7 @@ var AgentServer = class {
|
|
|
11103
11432
|
posthogAPI;
|
|
11104
11433
|
questionRelayedToSlack = false;
|
|
11105
11434
|
detectedPrUrl = null;
|
|
11435
|
+
resumeState = null;
|
|
11106
11436
|
emitConsoleLog = (level, _scope, message, data) => {
|
|
11107
11437
|
if (!this.session) return;
|
|
11108
11438
|
const formatted = data !== void 0 ? `${message} ${JSON.stringify(data)}` : message;
|
|
@@ -11285,6 +11615,32 @@ var AgentServer = class {
|
|
|
11285
11615
|
async autoInitializeSession() {
|
|
11286
11616
|
const { taskId, runId, mode, projectId } = this.config;
|
|
11287
11617
|
this.logger.info("Auto-initializing session", { taskId, runId, mode });
|
|
11618
|
+
const resumeRunId = process.env.POSTHOG_RESUME_RUN_ID;
|
|
11619
|
+
if (resumeRunId) {
|
|
11620
|
+
this.logger.info("Resuming from previous run", {
|
|
11621
|
+
resumeRunId,
|
|
11622
|
+
currentRunId: runId
|
|
11623
|
+
});
|
|
11624
|
+
try {
|
|
11625
|
+
this.resumeState = await resumeFromLog({
|
|
11626
|
+
taskId,
|
|
11627
|
+
runId: resumeRunId,
|
|
11628
|
+
repositoryPath: this.config.repositoryPath,
|
|
11629
|
+
apiClient: this.posthogAPI,
|
|
11630
|
+
logger: new Logger({ debug: true, prefix: "[Resume]" })
|
|
11631
|
+
});
|
|
11632
|
+
this.logger.info("Resume state loaded", {
|
|
11633
|
+
conversationTurns: this.resumeState.conversation.length,
|
|
11634
|
+
snapshotApplied: this.resumeState.snapshotApplied,
|
|
11635
|
+
logEntries: this.resumeState.logEntryCount
|
|
11636
|
+
});
|
|
11637
|
+
} catch (error) {
|
|
11638
|
+
this.logger.warn("Failed to load resume state, starting fresh", {
|
|
11639
|
+
error
|
|
11640
|
+
});
|
|
11641
|
+
this.resumeState = null;
|
|
11642
|
+
}
|
|
11643
|
+
}
|
|
11288
11644
|
const payload = {
|
|
11289
11645
|
task_id: taskId,
|
|
11290
11646
|
run_id: runId,
|
|
@@ -11349,6 +11705,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11349
11705
|
}
|
|
11350
11706
|
}
|
|
11351
11707
|
});
|
|
11708
|
+
this.broadcastTurnComplete(result.stopReason);
|
|
11352
11709
|
return { stopReason: result.stopReason };
|
|
11353
11710
|
}
|
|
11354
11711
|
case POSTHOG_NOTIFICATIONS.CANCEL:
|
|
@@ -11384,18 +11741,19 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11384
11741
|
name: process.env.HOSTNAME || "cloud-sandbox"
|
|
11385
11742
|
};
|
|
11386
11743
|
this.configureEnvironment();
|
|
11387
|
-
const treeTracker = this.config.repositoryPath ? new TreeTracker({
|
|
11388
|
-
repositoryPath: this.config.repositoryPath,
|
|
11389
|
-
taskId: payload.task_id,
|
|
11390
|
-
runId: payload.run_id,
|
|
11391
|
-
logger: new Logger({ debug: true, prefix: "[TreeTracker]" })
|
|
11392
|
-
}) : null;
|
|
11393
11744
|
const posthogAPI = new PostHogAPIClient({
|
|
11394
11745
|
apiUrl: this.config.apiUrl,
|
|
11395
11746
|
projectId: this.config.projectId,
|
|
11396
11747
|
getApiKey: () => this.config.apiKey,
|
|
11397
11748
|
userAgent: `posthog/cloud.hog.dev; version: ${this.config.version ?? package_default.version}`
|
|
11398
11749
|
});
|
|
11750
|
+
const treeTracker = this.config.repositoryPath ? new TreeTracker({
|
|
11751
|
+
repositoryPath: this.config.repositoryPath,
|
|
11752
|
+
taskId: payload.task_id,
|
|
11753
|
+
runId: payload.run_id,
|
|
11754
|
+
apiClient: posthogAPI,
|
|
11755
|
+
logger: new Logger({ debug: true, prefix: "[TreeTracker]" })
|
|
11756
|
+
}) : null;
|
|
11399
11757
|
const logWriter = new SessionLogWriter({
|
|
11400
11758
|
posthogAPI,
|
|
11401
11759
|
logger: new Logger({ debug: true, prefix: "[SessionLogWriter]" })
|
|
@@ -11490,26 +11848,55 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11490
11848
|
}
|
|
11491
11849
|
async sendInitialTaskMessage(payload, prefetchedRun) {
|
|
11492
11850
|
if (!this.session) return;
|
|
11493
|
-
|
|
11494
|
-
|
|
11495
|
-
|
|
11496
|
-
|
|
11851
|
+
let taskRun = prefetchedRun ?? null;
|
|
11852
|
+
if (!taskRun) {
|
|
11853
|
+
try {
|
|
11854
|
+
taskRun = await this.posthogAPI.getTaskRun(
|
|
11855
|
+
payload.task_id,
|
|
11856
|
+
payload.run_id
|
|
11857
|
+
);
|
|
11858
|
+
} catch (error) {
|
|
11859
|
+
this.logger.warn("Failed to fetch task run", {
|
|
11860
|
+
taskId: payload.task_id,
|
|
11861
|
+
runId: payload.run_id,
|
|
11862
|
+
error
|
|
11863
|
+
});
|
|
11864
|
+
}
|
|
11865
|
+
}
|
|
11866
|
+
if (!this.resumeState) {
|
|
11867
|
+
const resumeRunId = this.getResumeRunId(taskRun);
|
|
11868
|
+
if (resumeRunId) {
|
|
11869
|
+
this.logger.info("Resuming from previous run (via TaskRun state)", {
|
|
11870
|
+
resumeRunId,
|
|
11871
|
+
currentRunId: payload.run_id
|
|
11872
|
+
});
|
|
11497
11873
|
try {
|
|
11498
|
-
|
|
11499
|
-
payload.task_id,
|
|
11500
|
-
|
|
11501
|
-
|
|
11874
|
+
this.resumeState = await resumeFromLog({
|
|
11875
|
+
taskId: payload.task_id,
|
|
11876
|
+
runId: resumeRunId,
|
|
11877
|
+
repositoryPath: this.config.repositoryPath,
|
|
11878
|
+
apiClient: this.posthogAPI,
|
|
11879
|
+
logger: new Logger({ debug: true, prefix: "[Resume]" })
|
|
11880
|
+
});
|
|
11881
|
+
this.logger.info("Resume state loaded (via TaskRun state)", {
|
|
11882
|
+
conversationTurns: this.resumeState.conversation.length,
|
|
11883
|
+
snapshotApplied: this.resumeState.snapshotApplied,
|
|
11884
|
+
logEntries: this.resumeState.logEntryCount
|
|
11885
|
+
});
|
|
11502
11886
|
} catch (error) {
|
|
11503
|
-
this.logger.warn(
|
|
11504
|
-
|
|
11505
|
-
|
|
11506
|
-
|
|
11507
|
-
runId: payload.run_id,
|
|
11508
|
-
error
|
|
11509
|
-
}
|
|
11510
|
-
);
|
|
11887
|
+
this.logger.warn("Failed to load resume state, starting fresh", {
|
|
11888
|
+
error
|
|
11889
|
+
});
|
|
11890
|
+
this.resumeState = null;
|
|
11511
11891
|
}
|
|
11512
11892
|
}
|
|
11893
|
+
}
|
|
11894
|
+
if (this.resumeState && this.resumeState.conversation.length > 0) {
|
|
11895
|
+
await this.sendResumeMessage(payload, taskRun);
|
|
11896
|
+
return;
|
|
11897
|
+
}
|
|
11898
|
+
try {
|
|
11899
|
+
const task = await this.posthogAPI.getTask(payload.task_id);
|
|
11513
11900
|
const initialPromptOverride = taskRun ? this.getInitialPromptOverride(taskRun) : null;
|
|
11514
11901
|
const initialPrompt = initialPromptOverride ?? task.description;
|
|
11515
11902
|
if (!initialPrompt) {
|
|
@@ -11528,6 +11915,7 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11528
11915
|
this.logger.info("Initial task message completed", {
|
|
11529
11916
|
stopReason: result.stopReason
|
|
11530
11917
|
});
|
|
11918
|
+
this.broadcastTurnComplete(result.stopReason);
|
|
11531
11919
|
if (result.stopReason === "end_turn") {
|
|
11532
11920
|
await this.relayAgentResponse(payload);
|
|
11533
11921
|
}
|
|
@@ -11539,6 +11927,94 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11539
11927
|
await this.signalTaskComplete(payload, "error");
|
|
11540
11928
|
}
|
|
11541
11929
|
}
|
|
11930
|
+
async sendResumeMessage(payload, taskRun) {
|
|
11931
|
+
if (!this.session || !this.resumeState) return;
|
|
11932
|
+
try {
|
|
11933
|
+
const conversationSummary = this.formatConversationForResume(
|
|
11934
|
+
this.resumeState.conversation
|
|
11935
|
+
);
|
|
11936
|
+
const pendingUserMessage = this.getPendingUserMessage(taskRun);
|
|
11937
|
+
const sandboxContext = this.resumeState.snapshotApplied ? `The sandbox environment (all files, packages, and code changes) has been fully restored from a snapshot.` : `The sandbox could not be restored from a snapshot (it may have expired). You are starting with a fresh environment but have the full conversation history below.`;
|
|
11938
|
+
let resumePrompt;
|
|
11939
|
+
if (pendingUserMessage) {
|
|
11940
|
+
resumePrompt = `You are resuming a previous conversation. ${sandboxContext}
|
|
11941
|
+
|
|
11942
|
+
Here is the conversation history from the previous session:
|
|
11943
|
+
|
|
11944
|
+
${conversationSummary}
|
|
11945
|
+
|
|
11946
|
+
The user has sent a new message:
|
|
11947
|
+
|
|
11948
|
+
${pendingUserMessage}
|
|
11949
|
+
|
|
11950
|
+
Respond to the user's new message above. You have full context from the previous session.`;
|
|
11951
|
+
} else {
|
|
11952
|
+
resumePrompt = `You are resuming a previous conversation. ${sandboxContext}
|
|
11953
|
+
|
|
11954
|
+
Here is the conversation history from the previous session:
|
|
11955
|
+
|
|
11956
|
+
${conversationSummary}
|
|
11957
|
+
|
|
11958
|
+
Continue from where you left off. The user is waiting for your response.`;
|
|
11959
|
+
}
|
|
11960
|
+
this.logger.info("Sending resume message", {
|
|
11961
|
+
taskId: payload.task_id,
|
|
11962
|
+
conversationTurns: this.resumeState.conversation.length,
|
|
11963
|
+
promptLength: resumePrompt.length,
|
|
11964
|
+
hasPendingUserMessage: !!pendingUserMessage,
|
|
11965
|
+
snapshotApplied: this.resumeState.snapshotApplied
|
|
11966
|
+
});
|
|
11967
|
+
this.resumeState = null;
|
|
11968
|
+
const result = await this.session.clientConnection.prompt({
|
|
11969
|
+
sessionId: this.session.acpSessionId,
|
|
11970
|
+
prompt: [{ type: "text", text: resumePrompt }]
|
|
11971
|
+
});
|
|
11972
|
+
this.logger.info("Resume message completed", {
|
|
11973
|
+
stopReason: result.stopReason
|
|
11974
|
+
});
|
|
11975
|
+
this.broadcastTurnComplete(result.stopReason);
|
|
11976
|
+
} catch (error) {
|
|
11977
|
+
this.logger.error("Failed to send resume message", error);
|
|
11978
|
+
if (this.session) {
|
|
11979
|
+
await this.session.logWriter.flushAll();
|
|
11980
|
+
}
|
|
11981
|
+
await this.signalTaskComplete(payload, "error");
|
|
11982
|
+
}
|
|
11983
|
+
}
|
|
11984
|
+
static RESUME_HISTORY_TOKEN_BUDGET = 5e4;
|
|
11985
|
+
static TOOL_RESULT_MAX_CHARS = 2e3;
|
|
11986
|
+
formatConversationForResume(conversation) {
|
|
11987
|
+
const selected = selectRecentTurns(
|
|
11988
|
+
conversation,
|
|
11989
|
+
_AgentServer.RESUME_HISTORY_TOKEN_BUDGET
|
|
11990
|
+
);
|
|
11991
|
+
const parts = [];
|
|
11992
|
+
if (selected.length < conversation.length) {
|
|
11993
|
+
parts.push(
|
|
11994
|
+
`*(${conversation.length - selected.length} earlier turns omitted)*`
|
|
11995
|
+
);
|
|
11996
|
+
}
|
|
11997
|
+
for (const turn of selected) {
|
|
11998
|
+
const role = turn.role === "user" ? "User" : "Assistant";
|
|
11999
|
+
const textParts = turn.content.filter((block) => block.type === "text").map((block) => block.text);
|
|
12000
|
+
if (textParts.length > 0) {
|
|
12001
|
+
parts.push(`**${role}**: ${textParts.join("\n")}`);
|
|
12002
|
+
}
|
|
12003
|
+
if (turn.toolCalls?.length) {
|
|
12004
|
+
const toolSummary = turn.toolCalls.map((tc) => {
|
|
12005
|
+
let resultStr = "";
|
|
12006
|
+
if (tc.result !== void 0) {
|
|
12007
|
+
const raw = typeof tc.result === "string" ? tc.result : JSON.stringify(tc.result);
|
|
12008
|
+
resultStr = raw.length > _AgentServer.TOOL_RESULT_MAX_CHARS ? ` \u2192 ${raw.substring(0, _AgentServer.TOOL_RESULT_MAX_CHARS)}...(truncated)` : ` \u2192 ${raw}`;
|
|
12009
|
+
}
|
|
12010
|
+
return ` - ${tc.toolName}${resultStr}`;
|
|
12011
|
+
}).join("\n");
|
|
12012
|
+
parts.push(`**${role} (tools)**:
|
|
12013
|
+
${toolSummary}`);
|
|
12014
|
+
}
|
|
12015
|
+
}
|
|
12016
|
+
return parts.join("\n\n");
|
|
12017
|
+
}
|
|
11542
12018
|
getInitialPromptOverride(taskRun) {
|
|
11543
12019
|
const state = taskRun.state;
|
|
11544
12020
|
const override = state?.initial_prompt_override;
|
|
@@ -11548,6 +12024,24 @@ You MUST NOT create a new branch, close the existing PR, or create a new PR.`
|
|
|
11548
12024
|
const trimmed2 = override.trim();
|
|
11549
12025
|
return trimmed2.length > 0 ? trimmed2 : null;
|
|
11550
12026
|
}
|
|
12027
|
+
getPendingUserMessage(taskRun) {
|
|
12028
|
+
if (!taskRun) return null;
|
|
12029
|
+
const state = taskRun.state;
|
|
12030
|
+
const message = state?.pending_user_message;
|
|
12031
|
+
if (typeof message !== "string") {
|
|
12032
|
+
return null;
|
|
12033
|
+
}
|
|
12034
|
+
const trimmed2 = message.trim();
|
|
12035
|
+
return trimmed2.length > 0 ? trimmed2 : null;
|
|
12036
|
+
}
|
|
12037
|
+
getResumeRunId(taskRun) {
|
|
12038
|
+
const envRunId = process.env.POSTHOG_RESUME_RUN_ID;
|
|
12039
|
+
if (envRunId) return envRunId;
|
|
12040
|
+
if (!taskRun) return null;
|
|
12041
|
+
const state = taskRun.state;
|
|
12042
|
+
const stateRunId = state?.resume_from_run_id;
|
|
12043
|
+
return typeof stateRunId === "string" && stateRunId.trim().length > 0 ? stateRunId.trim() : null;
|
|
12044
|
+
}
|
|
11551
12045
|
buildCloudSystemPrompt(prUrl) {
|
|
11552
12046
|
if (prUrl) {
|
|
11553
12047
|
return `
|
|
@@ -11872,20 +12366,30 @@ Important:
|
|
|
11872
12366
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11873
12367
|
notification
|
|
11874
12368
|
});
|
|
11875
|
-
const { archiveUrl: _, ...paramsWithoutArchive } = snapshotWithDevice;
|
|
11876
|
-
const logNotification = {
|
|
11877
|
-
...notification,
|
|
11878
|
-
params: paramsWithoutArchive
|
|
11879
|
-
};
|
|
11880
12369
|
this.session.logWriter.appendRawLine(
|
|
11881
12370
|
this.session.payload.run_id,
|
|
11882
|
-
JSON.stringify(
|
|
12371
|
+
JSON.stringify(notification)
|
|
11883
12372
|
);
|
|
11884
12373
|
}
|
|
11885
12374
|
} catch (error) {
|
|
11886
12375
|
this.logger.error("Failed to capture tree state", error);
|
|
11887
12376
|
}
|
|
11888
12377
|
}
|
|
12378
|
+
broadcastTurnComplete(stopReason) {
|
|
12379
|
+
if (!this.session) return;
|
|
12380
|
+
this.broadcastEvent({
|
|
12381
|
+
type: "notification",
|
|
12382
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12383
|
+
notification: {
|
|
12384
|
+
jsonrpc: "2.0",
|
|
12385
|
+
method: POSTHOG_NOTIFICATIONS.TURN_COMPLETE,
|
|
12386
|
+
params: {
|
|
12387
|
+
sessionId: this.session.acpSessionId,
|
|
12388
|
+
stopReason
|
|
12389
|
+
}
|
|
12390
|
+
}
|
|
12391
|
+
});
|
|
12392
|
+
}
|
|
11889
12393
|
broadcastEvent(event) {
|
|
11890
12394
|
if (this.session?.sseController) {
|
|
11891
12395
|
this.sendSseEvent(this.session.sseController, event);
|