@posthog/agent 2.1.17 → 2.1.29

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as AcpConnection, a as AcpConnectionConfig, b as Agent, c as AgentAdapter, C as CodexProcessOptions, I as InProcessAcpConnection, O as OtelLogConfig, d as OtelLogWriter, S as SessionContext, e as SessionLogWriter, f as SessionLogWriterOptions, g as createAcpConnection } from './agent-LrKyX9KN.js';
1
+ export { A as AcpConnection, a as AcpConnectionConfig, b as Agent, c as AgentAdapter, C as CodexProcessOptions, I as InProcessAcpConnection, O as OtelLogConfig, d as OtelLogWriter, S as SessionContext, e as SessionLogWriter, f as SessionLogWriterOptions, g as createAcpConnection } from './agent-DcBmoTR4.js';
2
2
  import { McpServerConfig } from '@anthropic-ai/claude-agent-sdk';
3
3
  import { L as Logger } from './logger-DDBiMOOD.js';
4
4
  export { a as LoggerConfig } from './logger-DDBiMOOD.js';
package/dist/index.js CHANGED
@@ -1174,7 +1174,7 @@ import { v7 as uuidv7 } from "uuid";
1174
1174
  // package.json
1175
1175
  var package_default = {
1176
1176
  name: "@posthog/agent",
1177
- version: "2.1.17",
1177
+ version: "2.1.29",
1178
1178
  repository: "https://github.com/PostHog/twig",
1179
1179
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
1180
1180
  exports: {
@@ -1246,11 +1246,11 @@ var package_default = {
1246
1246
  },
1247
1247
  devDependencies: {
1248
1248
  "@changesets/cli": "^2.27.8",
1249
+ "@posthog/shared": "workspace:*",
1250
+ "@twig/git": "workspace:*",
1249
1251
  "@types/bun": "latest",
1250
1252
  "@types/tar": "^6.1.13",
1251
1253
  minimatch: "^10.0.3",
1252
- "@posthog/shared": "workspace:*",
1253
- "@twig/git": "workspace:*",
1254
1254
  msw: "^2.12.7",
1255
1255
  tsup: "^8.5.1",
1256
1256
  tsx: "^4.20.6",
@@ -1258,16 +1258,16 @@ var package_default = {
1258
1258
  vitest: "^2.1.8"
1259
1259
  },
1260
1260
  dependencies: {
1261
+ "@agentclientprotocol/sdk": "^0.14.0",
1262
+ "@anthropic-ai/claude-agent-sdk": "0.2.42",
1263
+ "@anthropic-ai/sdk": "^0.71.0",
1264
+ "@hono/node-server": "^1.19.9",
1265
+ "@modelcontextprotocol/sdk": "^1.25.3",
1261
1266
  "@opentelemetry/api-logs": "^0.208.0",
1262
1267
  "@opentelemetry/exporter-logs-otlp-http": "^0.208.0",
1263
1268
  "@opentelemetry/resources": "^2.0.0",
1264
1269
  "@opentelemetry/sdk-logs": "^0.208.0",
1265
1270
  "@opentelemetry/semantic-conventions": "^1.28.0",
1266
- "@agentclientprotocol/sdk": "^0.14.0",
1267
- "@anthropic-ai/claude-agent-sdk": "0.2.12",
1268
- "@anthropic-ai/sdk": "^0.71.0",
1269
- "@hono/node-server": "^1.19.9",
1270
- "@modelcontextprotocol/sdk": "^1.25.3",
1271
1271
  "@types/jsonwebtoken": "^9.0.10",
1272
1272
  commander: "^14.0.2",
1273
1273
  diff: "^8.0.2",
@@ -2268,19 +2268,9 @@ function streamEventToAcpNotifications(message, sessionId, toolUseCache, fileCon
2268
2268
  }
2269
2269
  }
2270
2270
  async function handleSystemMessage(message, context) {
2271
- const { session, sessionId, client, logger } = context;
2271
+ const { sessionId, client, logger } = context;
2272
2272
  switch (message.subtype) {
2273
2273
  case "init":
2274
- if (message.session_id && session && !session.sessionId) {
2275
- session.sessionId = message.session_id;
2276
- if (session.taskRunId) {
2277
- await client.extNotification("_posthog/sdk_session", {
2278
- taskRunId: session.taskRunId,
2279
- sessionId: message.session_id,
2280
- adapter: "claude"
2281
- });
2282
- }
2283
- }
2284
2274
  break;
2285
2275
  case "compact_boundary":
2286
2276
  await client.extNotification("_posthog/compact_boundary", {
@@ -3245,7 +3235,7 @@ function buildSessionOptions(params) {
3245
3235
  ),
3246
3236
  ...params.onProcessSpawned && {
3247
3237
  spawnClaudeCodeProcess: buildSpawnWrapper(
3248
- params.sessionId ?? "unknown",
3238
+ params.sessionId,
3249
3239
  params.onProcessSpawned,
3250
3240
  params.onProcessExited
3251
3241
  )
@@ -3254,8 +3244,11 @@ function buildSessionOptions(params) {
3254
3244
  if (process.env.CLAUDE_CODE_EXECUTABLE) {
3255
3245
  options.pathToClaudeCodeExecutable = process.env.CLAUDE_CODE_EXECUTABLE;
3256
3246
  }
3257
- if (params.sessionId) {
3247
+ if (params.isResume) {
3258
3248
  options.resume = params.sessionId;
3249
+ options.forkSession = false;
3250
+ } else {
3251
+ options.sessionId = params.sessionId;
3259
3252
  }
3260
3253
  if (params.additionalDirectories) {
3261
3254
  options.additionalDirectories = params.additionalDirectories;
@@ -3332,7 +3325,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3332
3325
  async newSession(params) {
3333
3326
  this.checkAuthStatus();
3334
3327
  const meta = params._meta;
3335
- const internalSessionId = uuidv7();
3328
+ const sessionId = uuidv7();
3336
3329
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
3337
3330
  const mcpServers = parseMcpServers(params);
3338
3331
  await fetchMcpToolMetadata(mcpServers, this.logger);
@@ -3340,18 +3333,20 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3340
3333
  cwd: params.cwd,
3341
3334
  mcpServers,
3342
3335
  permissionMode,
3343
- canUseTool: this.createCanUseTool(internalSessionId),
3336
+ canUseTool: this.createCanUseTool(sessionId),
3344
3337
  logger: this.logger,
3345
3338
  systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3346
3339
  userProvidedOptions: meta?.claudeCode?.options,
3347
- onModeChange: this.createOnModeChange(internalSessionId),
3340
+ sessionId,
3341
+ isResume: false,
3342
+ onModeChange: this.createOnModeChange(sessionId),
3348
3343
  onProcessSpawned: this.processCallbacks?.onProcessSpawned,
3349
3344
  onProcessExited: this.processCallbacks?.onProcessExited
3350
3345
  });
3351
3346
  const input = new Pushable();
3352
3347
  const q = query({ prompt: input, options });
3353
3348
  const session = this.createSession(
3354
- internalSessionId,
3349
+ sessionId,
3355
3350
  q,
3356
3351
  input,
3357
3352
  permissionMode,
@@ -3359,19 +3354,23 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3359
3354
  options.abortController
3360
3355
  );
3361
3356
  session.taskRunId = meta?.taskRunId;
3362
- this.registerPersistence(
3363
- internalSessionId,
3364
- meta
3365
- );
3357
+ this.registerPersistence(sessionId, meta);
3358
+ if (meta?.taskRunId) {
3359
+ await this.client.extNotification("_posthog/sdk_session", {
3360
+ taskRunId: meta.taskRunId,
3361
+ sessionId,
3362
+ adapter: "claude"
3363
+ });
3364
+ }
3366
3365
  const modelOptions = await this.getModelConfigOptions();
3367
3366
  session.modelId = modelOptions.currentModelId;
3368
3367
  await this.trySetModel(q, modelOptions.currentModelId);
3369
3368
  this.sendAvailableCommandsUpdate(
3370
- internalSessionId,
3369
+ sessionId,
3371
3370
  await getAvailableSlashCommands(q)
3372
3371
  );
3373
3372
  return {
3374
- sessionId: internalSessionId,
3373
+ sessionId,
3375
3374
  configOptions: await this.buildConfigOptions(modelOptions)
3376
3375
  };
3377
3376
  }
@@ -3379,34 +3378,31 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3379
3378
  return this.resumeSession(params);
3380
3379
  }
3381
3380
  async resumeSession(params) {
3382
- const { sessionId: internalSessionId } = params;
3383
- if (this.sessionId === internalSessionId) {
3381
+ const meta = params._meta;
3382
+ const sessionId = meta?.sessionId;
3383
+ if (!sessionId) {
3384
+ throw new Error("Cannot resume session without sessionId");
3385
+ }
3386
+ if (this.sessionId === sessionId) {
3384
3387
  return {};
3385
3388
  }
3386
- const meta = params._meta;
3387
3389
  const mcpServers = parseMcpServers(params);
3388
3390
  await fetchMcpToolMetadata(mcpServers, this.logger);
3389
3391
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
3390
3392
  const { query: q, session } = await this.initializeQuery({
3391
- internalSessionId,
3392
3393
  cwd: params.cwd,
3393
3394
  permissionMode,
3394
3395
  mcpServers,
3395
3396
  systemPrompt: buildSystemPrompt(meta?.systemPrompt),
3396
3397
  userProvidedOptions: meta?.claudeCode?.options,
3397
- sessionId: meta?.sessionId,
3398
+ sessionId,
3399
+ isResume: true,
3398
3400
  additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
3399
3401
  });
3400
3402
  session.taskRunId = meta?.taskRunId;
3401
- if (meta?.sessionId) {
3402
- session.sessionId = meta.sessionId;
3403
- }
3404
- this.registerPersistence(
3405
- internalSessionId,
3406
- meta
3407
- );
3403
+ this.registerPersistence(sessionId, meta);
3408
3404
  this.sendAvailableCommandsUpdate(
3409
- internalSessionId,
3405
+ sessionId,
3410
3406
  await getAvailableSlashCommands(q)
3411
3407
  );
3412
3408
  return {
@@ -3475,20 +3471,21 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3475
3471
  cwd: config.cwd,
3476
3472
  mcpServers: config.mcpServers,
3477
3473
  permissionMode: config.permissionMode,
3478
- canUseTool: this.createCanUseTool(config.internalSessionId),
3474
+ canUseTool: this.createCanUseTool(config.sessionId),
3479
3475
  logger: this.logger,
3480
3476
  systemPrompt: config.systemPrompt,
3481
3477
  userProvidedOptions: config.userProvidedOptions,
3482
3478
  sessionId: config.sessionId,
3479
+ isResume: config.isResume,
3483
3480
  additionalDirectories: config.additionalDirectories,
3484
- onModeChange: this.createOnModeChange(config.internalSessionId),
3481
+ onModeChange: this.createOnModeChange(config.sessionId),
3485
3482
  onProcessSpawned: this.processCallbacks?.onProcessSpawned,
3486
3483
  onProcessExited: this.processCallbacks?.onProcessExited
3487
3484
  });
3488
3485
  const q = query({ prompt: input, options });
3489
3486
  const abortController = options.abortController;
3490
3487
  const session = this.createSession(
3491
- config.internalSessionId,
3488
+ config.sessionId,
3492
3489
  q,
3493
3490
  input,
3494
3491
  config.permissionMode,
@@ -3681,6 +3678,7 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
3681
3678
  }
3682
3679
  case "tool_progress":
3683
3680
  case "auth_status":
3681
+ case "tool_use_summary":
3684
3682
  return null;
3685
3683
  default:
3686
3684
  unreachable(message, this.logger);
@@ -4294,19 +4292,36 @@ var PostHogAPIClient = class {
4294
4292
  };
4295
4293
 
4296
4294
  // src/session-log-writer.ts
4297
- var SessionLogWriter = class {
4295
+ var SessionLogWriter = class _SessionLogWriter {
4296
+ static FLUSH_DEBOUNCE_MS = 500;
4297
+ static FLUSH_MAX_INTERVAL_MS = 5e3;
4298
+ static MAX_FLUSH_RETRIES = 10;
4299
+ static MAX_RETRY_DELAY_MS = 3e4;
4298
4300
  posthogAPI;
4299
4301
  pendingEntries = /* @__PURE__ */ new Map();
4300
4302
  flushTimeouts = /* @__PURE__ */ new Map();
4303
+ lastFlushAttemptTime = /* @__PURE__ */ new Map();
4304
+ retryCounts = /* @__PURE__ */ new Map();
4301
4305
  sessions = /* @__PURE__ */ new Map();
4306
+ messageCounts = /* @__PURE__ */ new Map();
4302
4307
  logger;
4303
4308
  constructor(options = {}) {
4304
4309
  this.posthogAPI = options.posthogAPI;
4305
4310
  this.logger = options.logger ?? new Logger({ debug: false, prefix: "[SessionLogWriter]" });
4306
4311
  }
4307
4312
  async flushAll() {
4313
+ const sessionIds = [...this.sessions.keys()];
4314
+ const pendingCounts = sessionIds.map((id) => ({
4315
+ id,
4316
+ pending: this.pendingEntries.get(id)?.length ?? 0,
4317
+ messages: this.messageCounts.get(id) ?? 0
4318
+ }));
4319
+ this.logger.info("flushAll called", {
4320
+ sessions: sessionIds.length,
4321
+ pending: pendingCounts
4322
+ });
4308
4323
  const flushPromises = [];
4309
- for (const sessionId of this.sessions.keys()) {
4324
+ for (const sessionId of sessionIds) {
4310
4325
  flushPromises.push(this.flush(sessionId));
4311
4326
  }
4312
4327
  await Promise.all(flushPromises);
@@ -4315,7 +4330,12 @@ var SessionLogWriter = class {
4315
4330
  if (this.sessions.has(sessionId)) {
4316
4331
  return;
4317
4332
  }
4333
+ this.logger.info("Session registered", {
4334
+ sessionId,
4335
+ taskId: context.taskId
4336
+ });
4318
4337
  this.sessions.set(sessionId, { context });
4338
+ this.lastFlushAttemptTime.set(sessionId, Date.now());
4319
4339
  }
4320
4340
  isRegistered(sessionId) {
4321
4341
  return this.sessions.has(sessionId);
@@ -4323,8 +4343,16 @@ var SessionLogWriter = class {
4323
4343
  appendRawLine(sessionId, line) {
4324
4344
  const session = this.sessions.get(sessionId);
4325
4345
  if (!session) {
4346
+ this.logger.warn("appendRawLine called for unregistered session", {
4347
+ sessionId
4348
+ });
4326
4349
  return;
4327
4350
  }
4351
+ const count = (this.messageCounts.get(sessionId) ?? 0) + 1;
4352
+ this.messageCounts.set(sessionId, count);
4353
+ if (count % 10 === 1) {
4354
+ this.logger.info("Messages received", { count, sessionId });
4355
+ }
4328
4356
  try {
4329
4357
  const message = JSON.parse(line);
4330
4358
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -4360,24 +4388,56 @@ var SessionLogWriter = class {
4360
4388
  }
4361
4389
  async flush(sessionId) {
4362
4390
  const session = this.sessions.get(sessionId);
4363
- if (!session) return;
4391
+ if (!session) {
4392
+ this.logger.warn("flush: no session found", { sessionId });
4393
+ return;
4394
+ }
4364
4395
  this.emitCoalescedMessage(sessionId, session);
4365
4396
  const pending = this.pendingEntries.get(sessionId);
4366
- if (!this.posthogAPI || !pending?.length) return;
4397
+ if (!this.posthogAPI || !pending?.length) {
4398
+ this.logger.info("flush: nothing to persist", {
4399
+ sessionId,
4400
+ hasPosthogAPI: !!this.posthogAPI,
4401
+ pendingCount: pending?.length ?? 0
4402
+ });
4403
+ return;
4404
+ }
4367
4405
  this.pendingEntries.delete(sessionId);
4368
4406
  const timeout = this.flushTimeouts.get(sessionId);
4369
4407
  if (timeout) {
4370
4408
  clearTimeout(timeout);
4371
4409
  this.flushTimeouts.delete(sessionId);
4372
4410
  }
4411
+ this.lastFlushAttemptTime.set(sessionId, Date.now());
4373
4412
  try {
4374
4413
  await this.posthogAPI.appendTaskRunLog(
4375
4414
  session.context.taskId,
4376
4415
  session.context.runId,
4377
4416
  pending
4378
4417
  );
4418
+ this.retryCounts.set(sessionId, 0);
4419
+ this.logger.info("Flushed session logs", {
4420
+ sessionId,
4421
+ entryCount: pending.length
4422
+ });
4379
4423
  } catch (error) {
4380
- this.logger.error("Failed to persist session logs:", error);
4424
+ const retryCount = (this.retryCounts.get(sessionId) ?? 0) + 1;
4425
+ this.retryCounts.set(sessionId, retryCount);
4426
+ if (retryCount >= _SessionLogWriter.MAX_FLUSH_RETRIES) {
4427
+ this.logger.error(
4428
+ `Dropping ${pending.length} session log entries after ${retryCount} failed flush attempts`,
4429
+ { sessionId, error }
4430
+ );
4431
+ this.retryCounts.set(sessionId, 0);
4432
+ } else {
4433
+ this.logger.error(
4434
+ `Failed to persist session logs (attempt ${retryCount}/${_SessionLogWriter.MAX_FLUSH_RETRIES}):`,
4435
+ error
4436
+ );
4437
+ const currentPending = this.pendingEntries.get(sessionId) ?? [];
4438
+ this.pendingEntries.set(sessionId, [...pending, ...currentPending]);
4439
+ this.scheduleFlush(sessionId);
4440
+ }
4381
4441
  }
4382
4442
  }
4383
4443
  isAgentMessageChunk(message) {
@@ -4423,7 +4483,21 @@ var SessionLogWriter = class {
4423
4483
  scheduleFlush(sessionId) {
4424
4484
  const existing = this.flushTimeouts.get(sessionId);
4425
4485
  if (existing) clearTimeout(existing);
4426
- const timeout = setTimeout(() => this.flush(sessionId), 500);
4486
+ const retryCount = this.retryCounts.get(sessionId) ?? 0;
4487
+ const lastAttempt = this.lastFlushAttemptTime.get(sessionId) ?? 0;
4488
+ const elapsed = Date.now() - lastAttempt;
4489
+ let delay2;
4490
+ if (retryCount > 0) {
4491
+ delay2 = Math.min(
4492
+ _SessionLogWriter.FLUSH_DEBOUNCE_MS * 2 ** retryCount,
4493
+ _SessionLogWriter.MAX_RETRY_DELAY_MS
4494
+ );
4495
+ } else if (elapsed >= _SessionLogWriter.FLUSH_MAX_INTERVAL_MS) {
4496
+ delay2 = 0;
4497
+ } else {
4498
+ delay2 = _SessionLogWriter.FLUSH_DEBOUNCE_MS;
4499
+ }
4500
+ const timeout = setTimeout(() => this.flush(sessionId), delay2);
4427
4501
  this.flushTimeouts.set(sessionId, timeout);
4428
4502
  }
4429
4503
  };