@posthog/agent 2.1.35 → 2.1.45

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@posthog/agent",
3
- "version": "2.1.35",
3
+ "version": "2.1.45",
4
4
  "repository": "https://github.com/PostHog/twig",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
@@ -72,8 +72,8 @@
72
72
  "tsx": "^4.20.6",
73
73
  "typescript": "^5.5.0",
74
74
  "vitest": "^2.1.8",
75
- "@posthog/shared": "1.0.0",
76
- "@twig/git": "1.0.0"
75
+ "@twig/git": "1.0.0",
76
+ "@posthog/shared": "1.0.0"
77
77
  },
78
78
  "dependencies": {
79
79
  "@agentclientprotocol/sdk": "^0.14.0",
@@ -145,7 +145,10 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
145
145
  : "default";
146
146
 
147
147
  const mcpServers = parseMcpServers(params);
148
- await fetchMcpToolMetadata(mcpServers, this.logger);
148
+
149
+ // Fire off MCP metadata fetch early — it populates a module-level cache
150
+ // used later during permission checks, not needed by buildSessionOptions or query()
151
+ const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
149
152
 
150
153
  const options = buildSessionOptions({
151
154
  cwd: params.cwd,
@@ -184,14 +187,17 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
184
187
  });
185
188
  }
186
189
 
187
- const modelOptions = await this.getModelConfigOptions();
190
+ // Run model config, slash commands, and MCP metadata fetch in parallel
191
+ const [modelOptions, slashCommands] = await Promise.all([
192
+ this.getModelConfigOptions(),
193
+ getAvailableSlashCommands(q),
194
+ mcpMetadataPromise,
195
+ ]);
196
+
188
197
  session.modelId = modelOptions.currentModelId;
189
198
  await this.trySetModel(q, modelOptions.currentModelId);
190
199
 
191
- this.sendAvailableCommandsUpdate(
192
- sessionId,
193
- await getAvailableSlashCommands(q),
194
- );
200
+ this.sendAvailableCommandsUpdate(sessionId, slashCommands);
195
201
 
196
202
  return {
197
203
  sessionId,
@@ -216,7 +222,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
216
222
  }
217
223
 
218
224
  const mcpServers = parseMcpServers(params);
219
- await fetchMcpToolMetadata(mcpServers, this.logger);
225
+
226
+ // Fire off MCP metadata fetch early — populates cache for permission checks
227
+ const mcpMetadataPromise = fetchMcpToolMetadata(mcpServers, this.logger);
220
228
 
221
229
  const permissionMode: TwigExecutionMode =
222
230
  meta?.permissionMode &&
@@ -238,10 +246,14 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
238
246
  session.taskRunId = meta?.taskRunId;
239
247
 
240
248
  this.registerPersistence(sessionId, meta as Record<string, unknown>);
241
- this.sendAvailableCommandsUpdate(
242
- sessionId,
243
- await getAvailableSlashCommands(q),
244
- );
249
+
250
+ // Run slash commands fetch and MCP metadata in parallel
251
+ const [slashCommands] = await Promise.all([
252
+ getAvailableSlashCommands(q),
253
+ mcpMetadataPromise,
254
+ ]);
255
+
256
+ this.sendAvailableCommandsUpdate(sessionId, slashCommands);
245
257
 
246
258
  return {
247
259
  configOptions: await this.buildConfigOptions(),
@@ -245,11 +245,7 @@ function clearStatsigCache(): void {
245
245
  process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), ".claude"),
246
246
  "statsig",
247
247
  );
248
- try {
249
- if (fs.existsSync(statsigPath)) {
250
- fs.rmSync(statsigPath, { recursive: true, force: true });
251
- }
252
- } catch {
253
- // Ignore errors - cache clearing is best-effort
254
- }
248
+ fs.rm(statsigPath, { recursive: true, force: true }, () => {
249
+ // Best-effort, ignore errors
250
+ });
255
251
  }
@@ -26,6 +26,14 @@ type ArrayModelsResponse =
26
26
  }
27
27
  | Array<{ id?: string; owned_by?: string }>;
28
28
 
29
+ const CACHE_TTL = 10 * 60 * 1000; // 10 minutes
30
+
31
+ let gatewayModelsCache: {
32
+ models: GatewayModel[];
33
+ expiry: number;
34
+ url: string;
35
+ } | null = null;
36
+
29
37
  export async function fetchGatewayModels(
30
38
  options?: FetchGatewayModelsOptions,
31
39
  ): Promise<GatewayModel[]> {
@@ -34,6 +42,14 @@ export async function fetchGatewayModels(
34
42
  return [];
35
43
  }
36
44
 
45
+ if (
46
+ gatewayModelsCache &&
47
+ gatewayModelsCache.url === gatewayUrl &&
48
+ Date.now() < gatewayModelsCache.expiry
49
+ ) {
50
+ return gatewayModelsCache.models;
51
+ }
52
+
37
53
  const modelsUrl = `${gatewayUrl}/v1/models`;
38
54
 
39
55
  try {
@@ -44,8 +60,13 @@ export async function fetchGatewayModels(
44
60
  }
45
61
 
46
62
  const data = (await response.json()) as GatewayModelsResponse;
47
- const models = data.data ?? [];
48
- return models.filter((m) => !BLOCKED_MODELS.has(m.id));
63
+ const models = (data.data ?? []).filter((m) => !BLOCKED_MODELS.has(m.id));
64
+ gatewayModelsCache = {
65
+ models,
66
+ expiry: Date.now() + CACHE_TTL,
67
+ url: gatewayUrl,
68
+ };
69
+ return models;
49
70
  } catch {
50
71
  return [];
51
72
  }
@@ -70,6 +91,12 @@ export interface ArrayModelInfo {
70
91
  owned_by?: string;
71
92
  }
72
93
 
94
+ let arrayModelsCache: {
95
+ models: ArrayModelInfo[];
96
+ expiry: number;
97
+ url: string;
98
+ } | null = null;
99
+
73
100
  export async function fetchArrayModels(
74
101
  options?: FetchGatewayModelsOptions,
75
102
  ): Promise<ArrayModelInfo[]> {
@@ -78,6 +105,14 @@ export async function fetchArrayModels(
78
105
  return [];
79
106
  }
80
107
 
108
+ if (
109
+ arrayModelsCache &&
110
+ arrayModelsCache.url === gatewayUrl &&
111
+ Date.now() < arrayModelsCache.expiry
112
+ ) {
113
+ return arrayModelsCache.models;
114
+ }
115
+
81
116
  try {
82
117
  const base = new URL(gatewayUrl);
83
118
  base.pathname = "/array/v1/models";
@@ -97,6 +132,11 @@ export async function fetchArrayModels(
97
132
  if (!id) continue;
98
133
  results.push({ id, owned_by: model?.owned_by });
99
134
  }
135
+ arrayModelsCache = {
136
+ models: results,
137
+ expiry: Date.now() + CACHE_TTL,
138
+ url: gatewayUrl,
139
+ };
100
140
  return results;
101
141
  } catch {
102
142
  return [];