@posthog/agent 2.1.62 → 2.1.69

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.62",
3
+ "version": "2.1.69",
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": {
@@ -33,7 +33,7 @@ import { v7 as uuidv7 } from "uuid";
33
33
  import packageJson from "../../../package.json" with { type: "json" };
34
34
  import type { SessionContext } from "../../otel-log-writer.js";
35
35
  import type { SessionLogWriter } from "../../session-log-writer.js";
36
- import { unreachable } from "../../utils/common.js";
36
+ import { unreachable, withTimeout } from "../../utils/common.js";
37
37
  import { Logger } from "../../utils/logger.js";
38
38
  import { Pushable } from "../../utils/streams.js";
39
39
  import { BaseAcpAgent } from "../base-acp-agent.js";
@@ -48,7 +48,7 @@ import { fetchMcpToolMetadata } from "./mcp/tool-metadata.js";
48
48
  import { canUseTool } from "./permissions/permission-handlers.js";
49
49
  import { getAvailableSlashCommands } from "./session/commands.js";
50
50
  import { parseMcpServers } from "./session/mcp-config.js";
51
- import { toSdkModelId } from "./session/models.js";
51
+ import { DEFAULT_MODEL, toSdkModelId } from "./session/models.js";
52
52
  import {
53
53
  buildSessionOptions,
54
54
  buildSystemPrompt,
@@ -66,6 +66,8 @@ import type {
66
66
  ToolUseCache,
67
67
  } from "./types.js";
68
68
 
69
+ const SESSION_VALIDATION_TIMEOUT_MS = 10_000;
70
+
69
71
  export interface ClaudeAcpAgentOptions {
70
72
  onProcessSpawned?: (info: ProcessSpawnedInfo) => void;
71
73
  onProcessExited?: (pid: number) => void;
@@ -162,6 +164,8 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
162
164
  });
163
165
 
164
166
  const input = new Pushable<SDKUserMessage>();
167
+ // Pass default model at construction to avoid expensive post-hoc setModel IPC
168
+ options.model = DEFAULT_MODEL;
165
169
  const q = query({ prompt: input, options });
166
170
 
167
171
  const session = this.createSession(
@@ -191,7 +195,12 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
191
195
  this.deferBackgroundFetches(q, sessionId, mcpServers);
192
196
 
193
197
  session.modelId = modelOptions.currentModelId;
194
- await this.trySetModel(q, modelOptions.currentModelId);
198
+ // Only call setModel if the resolved model differs from the default we
199
+ // already baked into the query options — avoids a ~2s IPC round-trip.
200
+ const resolvedSdkModel = toSdkModelId(modelOptions.currentModelId);
201
+ if (resolvedSdkModel !== DEFAULT_MODEL) {
202
+ await this.trySetModel(q, modelOptions.currentModelId);
203
+ }
195
204
 
196
205
  const configOptions = await this.buildConfigOptions(modelOptions);
197
206
 
@@ -240,6 +249,16 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
240
249
 
241
250
  this.registerPersistence(sessionId, meta as Record<string, unknown>);
242
251
 
252
+ // Validate the resumed session is alive. For stale sessions this throws
253
+ // (e.g. "No conversation found"), preventing a broken session.
254
+ const validation = await withTimeout(
255
+ q.initializationResult(),
256
+ SESSION_VALIDATION_TIMEOUT_MS,
257
+ );
258
+ if (validation.result === "timeout") {
259
+ throw new Error("Session validation timed out");
260
+ }
261
+
243
262
  // Deferred: slash commands + MCP metadata (not needed to return configOptions)
244
263
  this.deferBackgroundFetches(q, sessionId, mcpServers);
245
264
 
package/src/agent.ts CHANGED
@@ -18,12 +18,10 @@ export class Agent {
18
18
  private acpConnection?: InProcessAcpConnection;
19
19
  private taskRunId?: string;
20
20
  private sessionLogWriter?: SessionLogWriter;
21
- public debug: boolean;
22
21
 
23
22
  constructor(config: AgentConfig) {
24
- this.debug = config.debug || false;
25
23
  this.logger = new Logger({
26
- debug: this.debug,
24
+ debug: config.debug || false,
27
25
  prefix: "[PostHog Agent]",
28
26
  onLog: config.onLog,
29
27
  });
@@ -1,5 +1,24 @@
1
1
  import type { Logger } from "./logger.js";
2
2
 
3
+ /**
4
+ * Races an operation against a timeout.
5
+ * Returns success with the value if the operation completes in time,
6
+ * or timeout if the operation takes longer than the specified duration.
7
+ */
8
+ export async function withTimeout<T>(
9
+ operation: Promise<T>,
10
+ timeoutMs: number,
11
+ ): Promise<{ result: "success"; value: T } | { result: "timeout" }> {
12
+ const timeoutPromise = new Promise<{ result: "timeout" }>((resolve) =>
13
+ setTimeout(() => resolve({ result: "timeout" }), timeoutMs),
14
+ );
15
+ const operationPromise = operation.then((value) => ({
16
+ result: "success" as const,
17
+ value,
18
+ }));
19
+ return Promise.race([operationPromise, timeoutPromise]);
20
+ }
21
+
3
22
  export const IS_ROOT =
4
23
  typeof process !== "undefined" &&
5
24
  (process.geteuid?.() ?? process.getuid?.()) === 0;