@posthog/agent 2.1.35 → 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.
@@ -94,6 +94,8 @@ type AcpConnectionConfig = {
94
94
  /** Deployment environment - "local" for desktop, "cloud" for cloud sandbox */
95
95
  deviceType?: "local" | "cloud";
96
96
  logger?: Logger;
97
+ /** Enable dev-only instrumentation (timing, verbose logging) */
98
+ debug?: boolean;
97
99
  processCallbacks?: ProcessSpawnedCallback;
98
100
  codexOptions?: CodexProcessOptions;
99
101
  allowedModelIds?: Set<string>;
package/dist/agent.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { b as Agent } from './agent-DcBmoTR4.js';
1
+ export { b as Agent } from './agent-BJ7Uacyp.js';
2
2
  import './types.js';
3
3
  import '@agentclientprotocol/sdk';
4
4
  import './logger-DDBiMOOD.js';
package/dist/agent.js CHANGED
@@ -271,12 +271,49 @@ import {
271
271
  import {
272
272
  query
273
273
  } from "@anthropic-ai/claude-agent-sdk";
274
+
275
+ // ../shared/dist/index.js
276
+ var NOOP_COLLECTOR = {
277
+ time: (_label, fn) => fn(),
278
+ timeSync: (_label, fn) => fn(),
279
+ record: () => {
280
+ },
281
+ summarize: () => {
282
+ }
283
+ };
284
+ function createTimingCollector(enabled, log) {
285
+ if (!enabled) return NOOP_COLLECTOR;
286
+ const steps = {};
287
+ return {
288
+ async time(label, fn) {
289
+ const start = Date.now();
290
+ const result = await fn();
291
+ steps[label] = Date.now() - start;
292
+ return result;
293
+ },
294
+ timeSync(label, fn) {
295
+ const start = Date.now();
296
+ const result = fn();
297
+ steps[label] = Date.now() - start;
298
+ return result;
299
+ },
300
+ record(label, ms) {
301
+ steps[label] = ms;
302
+ },
303
+ summarize(label) {
304
+ const total = Object.values(steps).reduce((a, b) => a + b, 0);
305
+ log(`[timing] ${label}: ${total}ms`, steps);
306
+ }
307
+ };
308
+ }
309
+
310
+ // src/adapters/claude/claude-agent.ts
274
311
  import { v7 as uuidv7 } from "uuid";
275
312
 
276
313
  // package.json
277
314
  var package_default = {
278
315
  name: "@posthog/agent",
279
- version: "2.1.35",
316
+ version: "2.1.47",
280
317
  repository: "https://github.com/PostHog/twig",
281
318
  description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
282
319
  exports: {
@@ -407,11 +444,16 @@ function unreachable(value, logger) {
407
444
  // src/gateway-models.ts
408
445
  var DEFAULT_GATEWAY_MODEL = "claude-opus-4-6";
409
446
  var BLOCKED_MODELS = /* @__PURE__ */ new Set(["gpt-5-mini", "openai/gpt-5-mini"]);
447
+ var CACHE_TTL = 10 * 60 * 1e3;
448
+ var gatewayModelsCache = null;
410
449
  async function fetchGatewayModels(options) {
411
450
  const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
412
451
  if (!gatewayUrl) {
413
452
  return [];
414
453
  }
454
+ if (gatewayModelsCache && gatewayModelsCache.url === gatewayUrl && Date.now() < gatewayModelsCache.expiry) {
455
+ return gatewayModelsCache.models;
456
+ }
415
457
  const modelsUrl = `${gatewayUrl}/v1/models`;
416
458
  try {
417
459
  const response = await fetch(modelsUrl);
@@ -419,8 +461,13 @@ async function fetchGatewayModels(options) {
419
461
  return [];
420
462
  }
421
463
  const data = await response.json();
422
- const models = data.data ?? [];
423
- return models.filter((m) => !BLOCKED_MODELS.has(m.id));
464
+ const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
465
+ gatewayModelsCache = {
466
+ models,
467
+ expiry: Date.now() + CACHE_TTL,
468
+ url: gatewayUrl
469
+ };
470
+ return models;
424
471
  } catch {
425
472
  return [];
426
473
  }
@@ -431,11 +478,15 @@ function isAnthropicModel(model) {
431
478
  }
432
479
  return model.id.startsWith("claude-") || model.id.startsWith("anthropic/");
433
480
  }
481
+ var arrayModelsCache = null;
434
482
  async function fetchArrayModels(options) {
435
483
  const gatewayUrl = options?.gatewayUrl ?? process.env.ANTHROPIC_BASE_URL;
436
484
  if (!gatewayUrl) {
437
485
  return [];
438
486
  }
487
+ if (arrayModelsCache && arrayModelsCache.url === gatewayUrl && Date.now() < arrayModelsCache.expiry) {
488
+ return arrayModelsCache.models;
489
+ }
439
490
  try {
440
491
  const base = new URL(gatewayUrl);
441
492
  base.pathname = "/array/v1/models";
@@ -453,6 +504,11 @@ async function fetchArrayModels(options) {
453
504
  if (!id) continue;
454
505
  results.push({ id, owned_by: model?.owned_by });
455
506
  }
507
+ arrayModelsCache = {
508
+ models: results,
509
+ expiry: Date.now() + CACHE_TTL,
510
+ url: gatewayUrl
511
+ };
456
512
  return results;
457
513
  } catch {
458
514
  return [];
@@ -2355,12 +2411,8 @@ function clearStatsigCache() {
2355
2411
  process.env.CLAUDE_CONFIG_DIR || path2.join(os2.homedir(), ".claude"),
2356
2412
  "statsig"
2357
2413
  );
2358
- try {
2359
- if (fs.existsSync(statsigPath)) {
2360
- fs.rmSync(statsigPath, { recursive: true, force: true });
2361
- }
2362
- } catch {
2363
- }
2414
+ fs.rm(statsigPath, { recursive: true, force: true }, () => {
2415
+ });
2364
2416
  }
2365
2417
 
2366
2418
  // src/adapters/claude/claude-agent.ts
@@ -2370,12 +2422,14 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2370
2422
  backgroundTerminals = {};
2371
2423
  clientCapabilities;
2372
2424
  logWriter;
2373
- processCallbacks;
2425
+ options;
2374
2426
  lastSentConfigOptions;
2375
- constructor(client, logWriter, processCallbacks) {
2427
+ debug;
2428
+ constructor(client, logWriter, options) {
2376
2429
  super(client);
2377
2430
  this.logWriter = logWriter;
2378
- this.processCallbacks = processCallbacks;
2431
+ this.options = options;
2432
+ this.debug = options?.debug ?? false;
2379
2433
  this.toolUseCache = {};
2380
2434
  this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
2381
2435
  }
@@ -2418,27 +2472,36 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2418
2472
  }
2419
2473
  async newSession(params) {
2420
2474
  this.checkAuthStatus();
2475
+ const tc = createTimingCollector(
2476
+ this.debug,
2477
+ (msg, data) => this.logger.info(msg, data)
2478
+ );
2421
2479
  const meta = params._meta;
2422
2480
  const sessionId = uuidv7();
2423
2481
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
2424
- const mcpServers = parseMcpServers(params);
2425
- await fetchMcpToolMetadata(mcpServers, this.logger);
2426
- const options = buildSessionOptions({
2427
- cwd: params.cwd,
2428
- mcpServers,
2429
- permissionMode,
2430
- canUseTool: this.createCanUseTool(sessionId),
2431
- logger: this.logger,
2432
- systemPrompt: buildSystemPrompt(meta?.systemPrompt),
2433
- userProvidedOptions: meta?.claudeCode?.options,
2434
- sessionId,
2435
- isResume: false,
2436
- onModeChange: this.createOnModeChange(sessionId),
2437
- onProcessSpawned: this.processCallbacks?.onProcessSpawned,
2438
- onProcessExited: this.processCallbacks?.onProcessExited
2439
- });
2482
+ const mcpServers = tc.timeSync(
2483
+ "parseMcpServers",
2484
+ () => parseMcpServers(params)
2485
+ );
2486
+ const options = tc.timeSync(
2487
+ "buildSessionOptions",
2488
+ () => buildSessionOptions({
2489
+ cwd: params.cwd,
2490
+ mcpServers,
2491
+ permissionMode,
2492
+ canUseTool: this.createCanUseTool(sessionId),
2493
+ logger: this.logger,
2494
+ systemPrompt: buildSystemPrompt(meta?.systemPrompt),
2495
+ userProvidedOptions: meta?.claudeCode?.options,
2496
+ sessionId,
2497
+ isResume: false,
2498
+ onModeChange: this.createOnModeChange(sessionId),
2499
+ onProcessSpawned: this.options?.onProcessSpawned,
2500
+ onProcessExited: this.options?.onProcessExited
2501
+ })
2502
+ );
2440
2503
  const input = new Pushable();
2441
- const q = query({ prompt: input, options });
2504
+ const q = tc.timeSync("sdkQuery", () => query({ prompt: input, options }));
2442
2505
  const session = this.createSession(
2443
2506
  sessionId,
2444
2507
  q,
@@ -2450,28 +2513,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2450
2513
  session.taskRunId = meta?.taskRunId;
2451
2514
  this.registerPersistence(sessionId, meta);
2452
2515
  if (meta?.taskRunId) {
2453
- await this.client.extNotification("_posthog/sdk_session", {
2454
- taskRunId: meta.taskRunId,
2455
- sessionId,
2456
- adapter: "claude"
2457
- });
2516
+ await tc.time(
2517
+ "extNotification",
2518
+ () => this.client.extNotification("_posthog/sdk_session", {
2519
+ taskRunId: meta.taskRunId,
2520
+ sessionId,
2521
+ adapter: "claude"
2522
+ })
2523
+ );
2458
2524
  }
2459
- const modelOptions = await this.getModelConfigOptions();
2525
+ const modelOptions = await tc.time(
2526
+ "fetchModels",
2527
+ () => this.getModelConfigOptions()
2528
+ );
2529
+ this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
2460
2530
  session.modelId = modelOptions.currentModelId;
2461
2531
  await this.trySetModel(q, modelOptions.currentModelId);
2462
- this.sendAvailableCommandsUpdate(
2463
- sessionId,
2464
- await getAvailableSlashCommands(q)
2532
+ const configOptions = await tc.time(
2533
+ "buildConfigOptions",
2534
+ () => this.buildConfigOptions(modelOptions)
2465
2535
  );
2536
+ tc.summarize("newSession");
2466
2537
  return {
2467
2538
  sessionId,
2468
- configOptions: await this.buildConfigOptions(modelOptions)
2539
+ configOptions
2469
2540
  };
2470
2541
  }
2471
2542
  async loadSession(params) {
2472
2543
  return this.resumeSession(params);
2473
2544
  }
2474
2545
  async resumeSession(params) {
2546
+ const tc = createTimingCollector(
2547
+ this.debug,
2548
+ (msg, data) => this.logger.info(msg, data)
2549
+ );
2475
2550
  const meta = params._meta;
2476
2551
  const sessionId = meta?.sessionId;
2477
2552
  if (!sessionId) {
@@ -2480,28 +2555,33 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2480
2555
  if (this.sessionId === sessionId) {
2481
2556
  return {};
2482
2557
  }
2483
- const mcpServers = parseMcpServers(params);
2484
- await fetchMcpToolMetadata(mcpServers, this.logger);
2558
+ const mcpServers = tc.timeSync(
2559
+ "parseMcpServers",
2560
+ () => parseMcpServers(params)
2561
+ );
2485
2562
  const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
2486
- const { query: q, session } = await this.initializeQuery({
2487
- cwd: params.cwd,
2488
- permissionMode,
2489
- mcpServers,
2490
- systemPrompt: buildSystemPrompt(meta?.systemPrompt),
2491
- userProvidedOptions: meta?.claudeCode?.options,
2492
- sessionId,
2493
- isResume: true,
2494
- additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
2495
- });
2563
+ const { query: q, session } = await tc.time(
2564
+ "initializeQuery",
2565
+ () => this.initializeQuery({
2566
+ cwd: params.cwd,
2567
+ permissionMode,
2568
+ mcpServers,
2569
+ systemPrompt: buildSystemPrompt(meta?.systemPrompt),
2570
+ userProvidedOptions: meta?.claudeCode?.options,
2571
+ sessionId,
2572
+ isResume: true,
2573
+ additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
2574
+ })
2575
+ );
2496
2576
  session.taskRunId = meta?.taskRunId;
2497
2577
  this.registerPersistence(sessionId, meta);
2498
- this.sendAvailableCommandsUpdate(
2499
- sessionId,
2500
- await getAvailableSlashCommands(q)
2578
+ this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
2579
+ const configOptions = await tc.time(
2580
+ "buildConfigOptions",
2581
+ () => this.buildConfigOptions()
2501
2582
  );
2502
- return {
2503
- configOptions: await this.buildConfigOptions()
2504
- };
2583
+ tc.summarize("resumeSession");
2584
+ return { configOptions };
2505
2585
  }
2506
2586
  async prompt(params) {
2507
2587
  this.session.cancelled = false;
@@ -2573,8 +2653,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2573
2653
  isResume: config.isResume,
2574
2654
  additionalDirectories: config.additionalDirectories,
2575
2655
  onModeChange: this.createOnModeChange(config.sessionId),
2576
- onProcessSpawned: this.processCallbacks?.onProcessSpawned,
2577
- onProcessExited: this.processCallbacks?.onProcessExited
2656
+ onProcessSpawned: this.options?.onProcessSpawned,
2657
+ onProcessExited: this.options?.onProcessExited
2578
2658
  });
2579
2659
  const q = query({ prompt: input, options });
2580
2660
  const abortController = options.abortController;
@@ -2684,6 +2764,23 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
2684
2764
  await q.setModel(fallback);
2685
2765
  }
2686
2766
  }
2767
+ /**
2768
+ * Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
2769
+ * Both populate caches used later — neither is needed to return configOptions.
2770
+ */
2771
+ deferBackgroundFetches(tc, q, sessionId, mcpServers) {
2772
+ Promise.all([
2773
+ tc.time("slashCommands", () => getAvailableSlashCommands(q)),
2774
+ tc.time(
2775
+ "mcpMetadata",
2776
+ () => fetchMcpToolMetadata(mcpServers, this.logger)
2777
+ )
2778
+ ]).then(([slashCommands]) => {
2779
+ this.sendAvailableCommandsUpdate(sessionId, slashCommands);
2780
+ }).catch((err) => {
2781
+ this.logger.warn("Failed to fetch deferred session data", { err });
2782
+ });
2783
+ }
2687
2784
  registerPersistence(sessionId, meta) {
2688
2785
  const persistence = meta?.persistence;
2689
2786
  if (persistence && this.logWriter) {
@@ -2971,7 +3068,10 @@ function createClaudeConnection(config) {
2971
3068
  const agentStream = ndJsonStream(agentWritable, streams.agent.readable);
2972
3069
  let agent = null;
2973
3070
  const agentConnection = new AgentSideConnection((client) => {
2974
- agent = new ClaudeAcpAgent(client, logWriter, config.processCallbacks);
3071
+ agent = new ClaudeAcpAgent(client, logWriter, {
3072
+ ...config.processCallbacks,
3073
+ debug: config.debug
3074
+ });
2975
3075
  logger.info(`Created ${agent.adapterName} agent`);
2976
3076
  return agent;
2977
3077
  }, agentStream);
@@ -3671,6 +3771,7 @@ var Agent = class {
3671
3771
  taskId,
3672
3772
  deviceType: "local",
3673
3773
  logger: this.logger,
3774
+ debug: this.debug,
3674
3775
  processCallbacks: options.processCallbacks,
3675
3776
  allowedModelIds,
3676
3777
  codexOptions: options.adapter === "codex" && gatewayConfig ? {