@posthog/agent 2.1.45 → 2.1.47

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.
@@ -1170,12 +1170,174 @@ var os3 = __toESM(require("os"), 1);
1170
1170
  var path3 = __toESM(require("path"), 1);
1171
1171
  var import_sdk2 = require("@agentclientprotocol/sdk");
1172
1172
  var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
1173
+
1174
+ // ../shared/dist/index.js
1175
+ var consoleLogger = {
1176
+ info: (_message, _data) => {
1177
+ },
1178
+ debug: (_message, _data) => {
1179
+ },
1180
+ error: (_message, _data) => {
1181
+ },
1182
+ warn: (_message, _data) => {
1183
+ }
1184
+ };
1185
+ var Saga = class {
1186
+ completedSteps = [];
1187
+ currentStepName = "unknown";
1188
+ stepTimings = [];
1189
+ log;
1190
+ constructor(logger) {
1191
+ this.log = logger ?? consoleLogger;
1192
+ }
1193
+ /**
1194
+ * Run the saga with the given input.
1195
+ * Returns a discriminated union result - either success with data or failure with error details.
1196
+ */
1197
+ async run(input) {
1198
+ this.completedSteps = [];
1199
+ this.currentStepName = "unknown";
1200
+ this.stepTimings = [];
1201
+ const sagaStart = performance.now();
1202
+ this.log.info("Starting saga", { sagaName: this.constructor.name });
1203
+ try {
1204
+ const result = await this.execute(input);
1205
+ const totalDuration = performance.now() - sagaStart;
1206
+ this.log.debug("Saga completed successfully", {
1207
+ sagaName: this.constructor.name,
1208
+ stepsCompleted: this.completedSteps.length,
1209
+ totalDurationMs: Math.round(totalDuration),
1210
+ stepTimings: this.stepTimings
1211
+ });
1212
+ return { success: true, data: result };
1213
+ } catch (error) {
1214
+ this.log.error("Saga failed, initiating rollback", {
1215
+ sagaName: this.constructor.name,
1216
+ failedStep: this.currentStepName,
1217
+ error: error instanceof Error ? error.message : String(error)
1218
+ });
1219
+ await this.rollback();
1220
+ return {
1221
+ success: false,
1222
+ error: error instanceof Error ? error.message : String(error),
1223
+ failedStep: this.currentStepName
1224
+ };
1225
+ }
1226
+ }
1227
+ /**
1228
+ * Execute a step with its rollback action.
1229
+ * If the step succeeds, its rollback action is stored for potential rollback.
1230
+ * The step name is automatically tracked for error reporting.
1231
+ *
1232
+ * @param config - Step configuration with name, execute, and rollback functions
1233
+ * @returns The result of the execute function
1234
+ * @throws Re-throws any error from the execute function (triggers rollback)
1235
+ */
1236
+ async step(config) {
1237
+ this.currentStepName = config.name;
1238
+ this.log.debug(`Executing step: ${config.name}`);
1239
+ const stepStart = performance.now();
1240
+ const result = await config.execute();
1241
+ const durationMs = Math.round(performance.now() - stepStart);
1242
+ this.stepTimings.push({ name: config.name, durationMs });
1243
+ this.log.debug(`Step completed: ${config.name}`, { durationMs });
1244
+ this.completedSteps.push({
1245
+ name: config.name,
1246
+ rollback: () => config.rollback(result)
1247
+ });
1248
+ return result;
1249
+ }
1250
+ /**
1251
+ * Execute a step that doesn't need rollback.
1252
+ * Useful for read-only operations or operations that are idempotent.
1253
+ * The step name is automatically tracked for error reporting.
1254
+ *
1255
+ * @param name - Step name for logging and error tracking
1256
+ * @param execute - The action to execute
1257
+ * @returns The result of the execute function
1258
+ */
1259
+ async readOnlyStep(name, execute) {
1260
+ this.currentStepName = name;
1261
+ this.log.debug(`Executing read-only step: ${name}`);
1262
+ const stepStart = performance.now();
1263
+ const result = await execute();
1264
+ const durationMs = Math.round(performance.now() - stepStart);
1265
+ this.stepTimings.push({ name, durationMs });
1266
+ this.log.debug(`Read-only step completed: ${name}`, { durationMs });
1267
+ return result;
1268
+ }
1269
+ /**
1270
+ * Roll back all completed steps in reverse order.
1271
+ * Rollback errors are logged but don't stop the rollback of other steps.
1272
+ */
1273
+ async rollback() {
1274
+ this.log.info("Rolling back saga", {
1275
+ stepsToRollback: this.completedSteps.length
1276
+ });
1277
+ const stepsReversed = [...this.completedSteps].reverse();
1278
+ for (const step of stepsReversed) {
1279
+ try {
1280
+ this.log.debug(`Rolling back step: ${step.name}`);
1281
+ await step.rollback();
1282
+ this.log.debug(`Step rolled back: ${step.name}`);
1283
+ } catch (error) {
1284
+ this.log.error(`Failed to rollback step: ${step.name}`, {
1285
+ error: error instanceof Error ? error.message : String(error)
1286
+ });
1287
+ }
1288
+ }
1289
+ this.log.info("Rollback completed", {
1290
+ stepsAttempted: this.completedSteps.length
1291
+ });
1292
+ }
1293
+ /**
1294
+ * Get the number of completed steps (useful for testing)
1295
+ */
1296
+ getCompletedStepCount() {
1297
+ return this.completedSteps.length;
1298
+ }
1299
+ };
1300
+ var NOOP_COLLECTOR = {
1301
+ time: (_label, fn) => fn(),
1302
+ timeSync: (_label, fn) => fn(),
1303
+ record: () => {
1304
+ },
1305
+ summarize: () => {
1306
+ }
1307
+ };
1308
+ function createTimingCollector(enabled, log) {
1309
+ if (!enabled) return NOOP_COLLECTOR;
1310
+ const steps = {};
1311
+ return {
1312
+ async time(label, fn) {
1313
+ const start = Date.now();
1314
+ const result = await fn();
1315
+ steps[label] = Date.now() - start;
1316
+ return result;
1317
+ },
1318
+ timeSync(label, fn) {
1319
+ const start = Date.now();
1320
+ const result = fn();
1321
+ steps[label] = Date.now() - start;
1322
+ return result;
1323
+ },
1324
+ record(label, ms) {
1325
+ steps[label] = ms;
1326
+ },
1327
+ summarize(label) {
1328
+ const total = Object.values(steps).reduce((a, b) => a + b, 0);
1329
+ log(`[timing] ${label}: ${total}ms`, steps);
1330
+ }
1331
+ };
1332
+ }
1333
+
1334
+ // src/adapters/claude/claude-agent.ts
1173
1335
  var import_uuid = require("uuid");
1174
1336
 
1175
1337
  // package.json
1176
1338
  var package_default = {
1177
1339
  name: "@posthog/agent",
1178
- version: "2.1.45",
1340
+ version: "2.1.47",
1179
1341
  repository: "https://github.com/PostHog/twig",
1180
1342
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
1181
1343
  exports: {
@@ -3248,12 +3410,14 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3248
3410
  backgroundTerminals = {};
3249
3411
  clientCapabilities;
3250
3412
  logWriter;
3251
- processCallbacks;
3413
+ options;
3252
3414
  lastSentConfigOptions;
3253
- constructor(client, logWriter, processCallbacks) {
3415
+ debug;
3416
+ constructor(client, logWriter, options) {
3254
3417
  super(client);
3255
3418
  this.logWriter = logWriter;
3256
- this.processCallbacks = processCallbacks;
3419
+ this.options = options;
3420
+ this.debug = options?.debug ?? false;
3257
3421
  this.toolUseCache = {};
3258
3422
  this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
3259
3423
  }
@@ -3296,27 +3460,36 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3296
3460
  }
3297
3461
  async newSession(params) {
3298
3462
  this.checkAuthStatus();
3463
+ const tc = createTimingCollector(
3464
+ this.debug,
3465
+ (msg, data) => this.logger.info(msg, data)
3466
+ );
3299
3467
  const meta = params._meta;
3300
3468
  const sessionId = (0, import_uuid.v7)();
3301
3469
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
3302
- const mcpServers = parseMcpServers(params);
3303
- const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
3304
- const options = buildSessionOptions({
3305
- cwd: params.cwd,
3306
- mcpServers,
3307
- permissionMode,
3308
- canUseTool: this.createCanUseTool(sessionId),
3309
- logger: this.logger,
3310
- systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3311
- userProvidedOptions: meta?.claudeCode?.options,
3312
- sessionId,
3313
- isResume: false,
3314
- onModeChange: this.createOnModeChange(sessionId),
3315
- onProcessSpawned: this.processCallbacks?.onProcessSpawned,
3316
- onProcessExited: this.processCallbacks?.onProcessExited
3317
- });
3470
+ const mcpServers = tc.timeSync(
3471
+ "parseMcpServers",
3472
+ () => parseMcpServers(params)
3473
+ );
3474
+ const options = tc.timeSync(
3475
+ "buildSessionOptions",
3476
+ () => buildSessionOptions({
3477
+ cwd: params.cwd,
3478
+ mcpServers,
3479
+ permissionMode,
3480
+ canUseTool: this.createCanUseTool(sessionId),
3481
+ logger: this.logger,
3482
+ systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3483
+ userProvidedOptions: meta?.claudeCode?.options,
3484
+ sessionId,
3485
+ isResume: false,
3486
+ onModeChange: this.createOnModeChange(sessionId),
3487
+ onProcessSpawned: this.options?.onProcessSpawned,
3488
+ onProcessExited: this.options?.onProcessExited
3489
+ })
3490
+ );
3318
3491
  const input = new Pushable();
3319
- const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
3492
+ const q = tc.timeSync("sdkQuery", () => (0, import_claude_agent_sdk.query)({ prompt: input, options }));
3320
3493
  const session = this.createSession(
3321
3494
  sessionId,
3322
3495
  q,
@@ -3328,29 +3501,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3328
3501
  session.taskRunId = meta?.taskRunId;
3329
3502
  this.registerPersistence(sessionId, meta);
3330
3503
  if (meta?.taskRunId) {
3331
- await this.client.extNotification("_posthog/sdk_session", {
3332
- taskRunId: meta.taskRunId,
3333
- sessionId,
3334
- adapter: "claude"
3335
- });
3504
+ await tc.time(
3505
+ "extNotification",
3506
+ () => this.client.extNotification("_posthog/sdk_session", {
3507
+ taskRunId: meta.taskRunId,
3508
+ sessionId,
3509
+ adapter: "claude"
3510
+ })
3511
+ );
3336
3512
  }
3337
- const [modelOptions, slashCommands] = await Promise.all([
3338
- this.getModelConfigOptions(),
3339
- getAvailableSlashCommands(q),
3340
- mcpMetadataPromise
3341
- ]);
3513
+ const modelOptions = await tc.time(
3514
+ "fetchModels",
3515
+ () => this.getModelConfigOptions()
3516
+ );
3517
+ this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
3342
3518
  session.modelId = modelOptions.currentModelId;
3343
3519
  await this.trySetModel(q, modelOptions.currentModelId);
3344
- this.sendAvailableCommandsUpdate(sessionId, slashCommands);
3520
+ const configOptions = await tc.time(
3521
+ "buildConfigOptions",
3522
+ () => this.buildConfigOptions(modelOptions)
3523
+ );
3524
+ tc.summarize("newSession");
3345
3525
  return {
3346
3526
  sessionId,
3347
- configOptions: await this.buildConfigOptions(modelOptions)
3527
+ configOptions
3348
3528
  };
3349
3529
  }
3350
3530
  async loadSession(params) {
3351
3531
  return this.resumeSession(params);
3352
3532
  }
3353
3533
  async resumeSession(params) {
3534
+ const tc = createTimingCollector(
3535
+ this.debug,
3536
+ (msg, data) => this.logger.info(msg, data)
3537
+ );
3354
3538
  const meta = params._meta;
3355
3539
  const sessionId = meta?.sessionId;
3356
3540
  if (!sessionId) {
@@ -3359,29 +3543,33 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3359
3543
  if (this.sessionId === sessionId) {
3360
3544
  return {};
3361
3545
  }
3362
- const mcpServers = parseMcpServers(params);
3363
- const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
3546
+ const mcpServers = tc.timeSync(
3547
+ "parseMcpServers",
3548
+ () => parseMcpServers(params)
3549
+ );
3364
3550
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
3365
- const { query: q, session } = await this.initializeQuery({
3366
- cwd: params.cwd,
3367
- permissionMode,
3368
- mcpServers,
3369
- systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3370
- userProvidedOptions: meta?.claudeCode?.options,
3371
- sessionId,
3372
- isResume: true,
3373
- additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
3374
- });
3551
+ const { query: q, session } = await tc.time(
3552
+ "initializeQuery",
3553
+ () => this.initializeQuery({
3554
+ cwd: params.cwd,
3555
+ permissionMode,
3556
+ mcpServers,
3557
+ systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3558
+ userProvidedOptions: meta?.claudeCode?.options,
3559
+ sessionId,
3560
+ isResume: true,
3561
+ additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
3562
+ })
3563
+ );
3375
3564
  session.taskRunId = meta?.taskRunId;
3376
3565
  this.registerPersistence(sessionId, meta);
3377
- const [slashCommands] = await Promise.all([
3378
- getAvailableSlashCommands(q),
3379
- mcpMetadataPromise
3380
- ]);
3381
- this.sendAvailableCommandsUpdate(sessionId, slashCommands);
3382
- return {
3383
- configOptions: await this.buildConfigOptions()
3384
- };
3566
+ this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
3567
+ const configOptions = await tc.time(
3568
+ "buildConfigOptions",
3569
+ () => this.buildConfigOptions()
3570
+ );
3571
+ tc.summarize("resumeSession");
3572
+ return { configOptions };
3385
3573
  }
3386
3574
  async prompt(params) {
3387
3575
  this.session.cancelled = false;
@@ -3453,8 +3641,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3453
3641
  isResume: config.isResume,
3454
3642
  additionalDirectories: config.additionalDirectories,
3455
3643
  onModeChange: this.createOnModeChange(config.sessionId),
3456
- onProcessSpawned: this.processCallbacks?.onProcessSpawned,
3457
- onProcessExited: this.processCallbacks?.onProcessExited
3644
+ onProcessSpawned: this.options?.onProcessSpawned,
3645
+ onProcessExited: this.options?.onProcessExited
3458
3646
  });
3459
3647
  const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
3460
3648
  const abortController = options.abortController;
@@ -3564,6 +3752,23 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3564
3752
  await q.setModel(fallback);
3565
3753
  }
3566
3754
  }
3755
+ /**
3756
+ * Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
3757
+ * Both populate caches used later — neither is needed to return configOptions.
3758
+ */
3759
+ deferBackgroundFetches(tc, q, sessionId, mcpServers) {
3760
+ Promise.all([
3761
+ tc.time("slashCommands", () => getAvailableSlashCommands(q)),
3762
+ tc.time(
3763
+ "mcpMetadata",
3764
+ () => fetchMcpToolMetadata(mcpServers, this.logger)
3765
+ )
3766
+ ]).then(([slashCommands]) => {
3767
+ this.sendAvailableCommandsUpdate(sessionId, slashCommands);
3768
+ }).catch((err) => {
3769
+ this.logger.warn("Failed to fetch deferred session data", { err });
3770
+ });
3771
+ }
3567
3772
  registerPersistence(sessionId, meta) {
3568
3773
  const persistence = meta?.persistence;
3569
3774
  if (persistence && this.logWriter) {
@@ -3851,7 +4056,10 @@ function createClaudeConnection(config) {
3851
4056
  const agentStream = (0, import_sdk3.ndJsonStream)(agentWritable, streams.agent.readable);
3852
4057
  let agent = null;
3853
4058
  const agentConnection = new import_sdk3.AgentSideConnection((client) => {
3854
- agent = new ClaudeAcpAgent(client, logWriter, config.processCallbacks);
4059
+ agent = new ClaudeAcpAgent(client, logWriter, {
4060
+ ...config.processCallbacks,
4061
+ debug: config.debug
4062
+ });
3855
4063
  logger.info(`Created ${agent.adapterName} agent`);
3856
4064
  return agent;
3857
4065
  }, agentStream);
@@ -9234,133 +9442,6 @@ async function getHeadSha(baseDir, options) {
9234
9442
  var import_promises2 = require("fs/promises");
9235
9443
  var import_node_path3 = require("path");
9236
9444
 
9237
- // ../shared/dist/index.js
9238
- var consoleLogger = {
9239
- info: (_message, _data) => {
9240
- },
9241
- debug: (_message, _data) => {
9242
- },
9243
- error: (_message, _data) => {
9244
- },
9245
- warn: (_message, _data) => {
9246
- }
9247
- };
9248
- var Saga = class {
9249
- completedSteps = [];
9250
- currentStepName = "unknown";
9251
- stepTimings = [];
9252
- log;
9253
- constructor(logger) {
9254
- this.log = logger ?? consoleLogger;
9255
- }
9256
- /**
9257
- * Run the saga with the given input.
9258
- * Returns a discriminated union result - either success with data or failure with error details.
9259
- */
9260
- async run(input) {
9261
- this.completedSteps = [];
9262
- this.currentStepName = "unknown";
9263
- this.stepTimings = [];
9264
- const sagaStart = performance.now();
9265
- this.log.info("Starting saga", { sagaName: this.constructor.name });
9266
- try {
9267
- const result = await this.execute(input);
9268
- const totalDuration = performance.now() - sagaStart;
9269
- this.log.debug("Saga completed successfully", {
9270
- sagaName: this.constructor.name,
9271
- stepsCompleted: this.completedSteps.length,
9272
- totalDurationMs: Math.round(totalDuration),
9273
- stepTimings: this.stepTimings
9274
- });
9275
- return { success: true, data: result };
9276
- } catch (error) {
9277
- this.log.error("Saga failed, initiating rollback", {
9278
- sagaName: this.constructor.name,
9279
- failedStep: this.currentStepName,
9280
- error: error instanceof Error ? error.message : String(error)
9281
- });
9282
- await this.rollback();
9283
- return {
9284
- success: false,
9285
- error: error instanceof Error ? error.message : String(error),
9286
- failedStep: this.currentStepName
9287
- };
9288
- }
9289
- }
9290
- /**
9291
- * Execute a step with its rollback action.
9292
- * If the step succeeds, its rollback action is stored for potential rollback.
9293
- * The step name is automatically tracked for error reporting.
9294
- *
9295
- * @param config - Step configuration with name, execute, and rollback functions
9296
- * @returns The result of the execute function
9297
- * @throws Re-throws any error from the execute function (triggers rollback)
9298
- */
9299
- async step(config) {
9300
- this.currentStepName = config.name;
9301
- this.log.debug(`Executing step: ${config.name}`);
9302
- const stepStart = performance.now();
9303
- const result = await config.execute();
9304
- const durationMs = Math.round(performance.now() - stepStart);
9305
- this.stepTimings.push({ name: config.name, durationMs });
9306
- this.log.debug(`Step completed: ${config.name}`, { durationMs });
9307
- this.completedSteps.push({
9308
- name: config.name,
9309
- rollback: () => config.rollback(result)
9310
- });
9311
- return result;
9312
- }
9313
- /**
9314
- * Execute a step that doesn't need rollback.
9315
- * Useful for read-only operations or operations that are idempotent.
9316
- * The step name is automatically tracked for error reporting.
9317
- *
9318
- * @param name - Step name for logging and error tracking
9319
- * @param execute - The action to execute
9320
- * @returns The result of the execute function
9321
- */
9322
- async readOnlyStep(name, execute) {
9323
- this.currentStepName = name;
9324
- this.log.debug(`Executing read-only step: ${name}`);
9325
- const stepStart = performance.now();
9326
- const result = await execute();
9327
- const durationMs = Math.round(performance.now() - stepStart);
9328
- this.stepTimings.push({ name, durationMs });
9329
- this.log.debug(`Read-only step completed: ${name}`, { durationMs });
9330
- return result;
9331
- }
9332
- /**
9333
- * Roll back all completed steps in reverse order.
9334
- * Rollback errors are logged but don't stop the rollback of other steps.
9335
- */
9336
- async rollback() {
9337
- this.log.info("Rolling back saga", {
9338
- stepsToRollback: this.completedSteps.length
9339
- });
9340
- const stepsReversed = [...this.completedSteps].reverse();
9341
- for (const step of stepsReversed) {
9342
- try {
9343
- this.log.debug(`Rolling back step: ${step.name}`);
9344
- await step.rollback();
9345
- this.log.debug(`Step rolled back: ${step.name}`);
9346
- } catch (error) {
9347
- this.log.error(`Failed to rollback step: ${step.name}`, {
9348
- error: error instanceof Error ? error.message : String(error)
9349
- });
9350
- }
9351
- }
9352
- this.log.info("Rollback completed", {
9353
- stepsAttempted: this.completedSteps.length
9354
- });
9355
- }
9356
- /**
9357
- * Get the number of completed steps (useful for testing)
9358
- */
9359
- getCompletedStepCount() {
9360
- return this.completedSteps.length;
9361
- }
9362
- };
9363
-
9364
9445
  // ../git/dist/sagas/tree.js
9365
9446
  var import_node_fs2 = require("fs");
9366
9447
  var fs5 = __toESM(require("fs/promises"), 1);