@posthog/agent 2.3.385 → 2.3.387

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.
Files changed (45) hide show
  1. package/dist/adapters/claude/session/jsonl-hydration.d.ts +1 -0
  2. package/dist/agent.d.ts +1 -0
  3. package/dist/agent.js +21 -2
  4. package/dist/agent.js.map +1 -1
  5. package/dist/handoff-checkpoint.d.ts +39 -0
  6. package/dist/handoff-checkpoint.js +6679 -0
  7. package/dist/handoff-checkpoint.js.map +1 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +2 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/logger-RC7sPv0S.d.ts +24 -0
  12. package/dist/posthog-api.d.ts +1 -0
  13. package/dist/posthog-api.js +19 -2
  14. package/dist/posthog-api.js.map +1 -1
  15. package/dist/resume.d.ts +71 -0
  16. package/dist/resume.js +6838 -0
  17. package/dist/resume.js.map +1 -0
  18. package/dist/server/agent-server.d.ts +5 -18
  19. package/dist/server/agent-server.js +1373 -432
  20. package/dist/server/agent-server.js.map +1 -1
  21. package/dist/server/bin.cjs +1370 -429
  22. package/dist/server/bin.cjs.map +1 -1
  23. package/dist/server/schemas.d.ts +191 -0
  24. package/dist/server/schemas.js +108 -0
  25. package/dist/server/schemas.js.map +1 -0
  26. package/dist/tree-tracker.d.ts +68 -0
  27. package/dist/tree-tracker.js +6431 -0
  28. package/dist/tree-tracker.js.map +1 -0
  29. package/dist/types.d.ts +18 -1
  30. package/dist/types.js +5 -0
  31. package/dist/types.js.map +1 -1
  32. package/package.json +17 -1
  33. package/src/acp-extensions.ts +3 -0
  34. package/src/handoff-checkpoint.test.ts +183 -0
  35. package/src/handoff-checkpoint.ts +361 -0
  36. package/src/posthog-api.test.ts +29 -0
  37. package/src/posthog-api.ts +5 -1
  38. package/src/resume.ts +58 -1
  39. package/src/sagas/apply-snapshot-saga.ts +7 -0
  40. package/src/sagas/capture-tree-saga.ts +10 -3
  41. package/src/sagas/resume-saga.ts +32 -0
  42. package/src/sagas/test-fixtures.ts +46 -0
  43. package/src/server/agent-server.ts +76 -57
  44. package/src/server/schemas.ts +21 -2
  45. package/src/types.ts +24 -0
package/src/resume.ts CHANGED
@@ -16,14 +16,20 @@
16
16
  */
17
17
 
18
18
  import type { ContentBlock } from "@agentclientprotocol/sdk";
19
+ import { selectRecentTurns } from "./adapters/claude/session/jsonl-hydration";
19
20
  import type { PostHogAPIClient } from "./posthog-api";
20
21
  import { ResumeSaga } from "./sagas/resume-saga";
21
- import type { DeviceInfo, TreeSnapshotEvent } from "./types";
22
+ import type {
23
+ DeviceInfo,
24
+ GitCheckpointEvent,
25
+ TreeSnapshotEvent,
26
+ } from "./types";
22
27
  import { Logger } from "./utils/logger";
23
28
 
24
29
  export interface ResumeState {
25
30
  conversation: ConversationTurn[];
26
31
  latestSnapshot: TreeSnapshotEvent | null;
32
+ latestGitCheckpoint: GitCheckpointEvent | null;
27
33
  /** Whether the tree snapshot was successfully applied (files restored) */
28
34
  snapshotApplied: boolean;
29
35
  interrupted: boolean;
@@ -95,6 +101,7 @@ export async function resumeFromLog(
95
101
  return {
96
102
  conversation: result.data.conversation as ConversationTurn[],
97
103
  latestSnapshot: result.data.latestSnapshot,
104
+ latestGitCheckpoint: result.data.latestGitCheckpoint,
98
105
  snapshotApplied: result.data.snapshotApplied,
99
106
  interrupted: result.data.interrupted,
100
107
  lastDevice: result.data.lastDevice,
@@ -113,3 +120,53 @@ export function conversationToPromptHistory(
113
120
  content: turn.content,
114
121
  }));
115
122
  }
123
+
124
+ const RESUME_HISTORY_TOKEN_BUDGET = 50_000;
125
+ const TOOL_RESULT_MAX_CHARS = 2000;
126
+
127
+ export function formatConversationForResume(
128
+ conversation: ConversationTurn[],
129
+ ): string {
130
+ const selected = selectRecentTurns(conversation, RESUME_HISTORY_TOKEN_BUDGET);
131
+ const parts: string[] = [];
132
+
133
+ if (selected.length < conversation.length) {
134
+ parts.push(
135
+ `*(${conversation.length - selected.length} earlier turns omitted)*`,
136
+ );
137
+ }
138
+
139
+ for (const turn of selected) {
140
+ const role = turn.role === "user" ? "User" : "Assistant";
141
+
142
+ const textParts = turn.content
143
+ .filter((block) => block.type === "text")
144
+ .map((block) => (block as { type: "text"; text: string }).text);
145
+
146
+ if (textParts.length > 0) {
147
+ parts.push(`**${role}**: ${textParts.join("\n")}`);
148
+ }
149
+
150
+ if (turn.toolCalls?.length) {
151
+ const toolSummary = turn.toolCalls
152
+ .map((tc) => {
153
+ let resultStr = "";
154
+ if (tc.result !== undefined) {
155
+ const raw =
156
+ typeof tc.result === "string"
157
+ ? tc.result
158
+ : JSON.stringify(tc.result);
159
+ resultStr =
160
+ raw.length > TOOL_RESULT_MAX_CHARS
161
+ ? ` → ${raw.substring(0, TOOL_RESULT_MAX_CHARS)}...(truncated)`
162
+ : ` → ${raw}`;
163
+ }
164
+ return ` - ${tc.toolName}${resultStr}`;
165
+ })
166
+ .join("\n");
167
+ parts.push(`**${role} (tools)**:\n${toolSummary}`);
168
+ }
169
+ }
170
+
171
+ return parts.join("\n\n");
172
+ }
@@ -59,6 +59,13 @@ export class ApplySnapshotSaga extends Saga<
59
59
  const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
60
60
  const binaryContent = Buffer.from(base64Content, "base64");
61
61
  await writeFile(archivePath, binaryContent);
62
+ this.log.info("Tree archive downloaded", {
63
+ treeHash: snapshot.treeHash,
64
+ snapshotBytes: binaryContent.byteLength,
65
+ snapshotWireBytes: arrayBuffer.byteLength,
66
+ totalBytes: binaryContent.byteLength,
67
+ totalWireBytes: arrayBuffer.byteLength,
68
+ });
62
69
  },
63
70
  rollback: async () => {
64
71
  if (this.archivePath) {
@@ -113,6 +113,8 @@ export class CaptureTreeSaga extends Saga<CaptureTreeInput, CaptureTreeOutput> {
113
113
  execute: async () => {
114
114
  const archiveContent = await readFile(archivePath);
115
115
  const base64Content = archiveContent.toString("base64");
116
+ const snapshotBytes = archiveContent.byteLength;
117
+ const snapshotWireBytes = Buffer.byteLength(base64Content, "utf-8");
116
118
 
117
119
  const artifacts = await apiClient.uploadTaskArtifacts(taskId, runId, [
118
120
  {
@@ -123,12 +125,17 @@ export class CaptureTreeSaga extends Saga<CaptureTreeInput, CaptureTreeOutput> {
123
125
  },
124
126
  ]);
125
127
 
126
- if (artifacts.length > 0 && artifacts[0].storage_path) {
128
+ const uploadedArtifact = artifacts[0];
129
+ if (uploadedArtifact?.storage_path) {
127
130
  this.log.info("Tree archive uploaded", {
128
- storagePath: artifacts[0].storage_path,
131
+ storagePath: uploadedArtifact.storage_path,
129
132
  treeHash,
133
+ snapshotBytes,
134
+ snapshotWireBytes,
135
+ totalBytes: snapshotBytes,
136
+ totalWireBytes: snapshotWireBytes,
130
137
  });
131
- return artifacts[0].storage_path;
138
+ return uploadedArtifact.storage_path;
132
139
  }
133
140
 
134
141
  return undefined;
@@ -5,6 +5,7 @@ import type { PostHogAPIClient } from "../posthog-api";
5
5
  import { TreeTracker } from "../tree-tracker";
6
6
  import type {
7
7
  DeviceInfo,
8
+ GitCheckpointEvent,
8
9
  StoredNotification,
9
10
  TreeSnapshotEvent,
10
11
  } from "../types";
@@ -34,6 +35,7 @@ export interface ResumeInput {
34
35
  export interface ResumeOutput {
35
36
  conversation: ConversationTurn[];
36
37
  latestSnapshot: TreeSnapshotEvent | null;
38
+ latestGitCheckpoint: GitCheckpointEvent | null;
37
39
  snapshotApplied: boolean;
38
40
  interrupted: boolean;
39
41
  lastDevice?: DeviceInfo;
@@ -75,6 +77,11 @@ export class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {
75
77
  Promise.resolve(this.findLatestTreeSnapshot(entries)),
76
78
  );
77
79
 
80
+ const latestGitCheckpoint = await this.readOnlyStep(
81
+ "find_git_checkpoint",
82
+ () => Promise.resolve(this.findLatestGitCheckpoint(entries)),
83
+ );
84
+
78
85
  // Step 4: Apply snapshot if present (wrapped in step for consistent logging)
79
86
  // Note: We use a try/catch inside the step because snapshot failure should NOT fail the saga
80
87
  let snapshotApplied = false;
@@ -158,6 +165,7 @@ export class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {
158
165
  return {
159
166
  conversation,
160
167
  latestSnapshot,
168
+ latestGitCheckpoint,
161
169
  snapshotApplied,
162
170
  interrupted: latestSnapshot?.interrupted ?? false,
163
171
  lastDevice,
@@ -169,6 +177,7 @@ export class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {
169
177
  return {
170
178
  conversation: [],
171
179
  latestSnapshot: null,
180
+ latestGitCheckpoint: null,
172
181
  snapshotApplied: false,
173
182
  interrupted: false,
174
183
  logEntryCount: 0,
@@ -197,6 +206,29 @@ export class ResumeSaga extends Saga<ResumeInput, ResumeOutput> {
197
206
  return null;
198
207
  }
199
208
 
209
+ private findLatestGitCheckpoint(
210
+ entries: StoredNotification[],
211
+ ): GitCheckpointEvent | null {
212
+ const sdkPrefixedMethod = `_${POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT}`;
213
+
214
+ for (let i = entries.length - 1; i >= 0; i--) {
215
+ const entry = entries[i];
216
+ const method = entry.notification?.method;
217
+ if (
218
+ method === sdkPrefixedMethod ||
219
+ method === POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT
220
+ ) {
221
+ const params = entry.notification?.params as
222
+ | GitCheckpointEvent
223
+ | undefined;
224
+ if (params?.checkpointId && params?.checkpointRef) {
225
+ return params;
226
+ }
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+
200
232
  private findLastDeviceInfo(
201
233
  entries: StoredNotification[],
202
234
  ): DeviceInfo | undefined {
@@ -67,6 +67,52 @@ export async function createTestRepo(prefix = "test-repo"): Promise<TestRepo> {
67
67
  };
68
68
  }
69
69
 
70
+ export async function cloneTestRepo(
71
+ sourcePath: string,
72
+ prefix = "test-repo-clone",
73
+ ): Promise<TestRepo> {
74
+ const clonePath = join(
75
+ tmpdir(),
76
+ `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2)}`,
77
+ );
78
+ await execFileAsync("git", ["clone", sourcePath, clonePath]);
79
+ await execFileAsync("git", ["config", "user.email", "test@test.com"], {
80
+ cwd: clonePath,
81
+ });
82
+ await execFileAsync("git", ["config", "user.name", "Test"], {
83
+ cwd: clonePath,
84
+ });
85
+ await execFileAsync("git", ["config", "commit.gpgsign", "false"], {
86
+ cwd: clonePath,
87
+ });
88
+
89
+ const git = async (args: string[]): Promise<string> => {
90
+ const { stdout } = await execFileAsync("git", args, { cwd: clonePath });
91
+ return stdout.trim();
92
+ };
93
+
94
+ return {
95
+ path: clonePath,
96
+ cleanup: () => rm(clonePath, { recursive: true, force: true }),
97
+ git,
98
+ writeFile: async (relativePath: string, content: string) => {
99
+ const fullPath = join(clonePath, relativePath);
100
+ const dir = join(fullPath, "..");
101
+ await mkdir(dir, { recursive: true });
102
+ await writeFile(fullPath, content);
103
+ },
104
+ readFile: async (relativePath: string) => {
105
+ return readFile(join(clonePath, relativePath), "utf-8");
106
+ },
107
+ deleteFile: async (relativePath: string) => {
108
+ await rm(join(clonePath, relativePath), { force: true });
109
+ },
110
+ exists: (relativePath: string) => {
111
+ return existsSync(join(clonePath, relativePath));
112
+ },
113
+ };
114
+ }
115
+
70
116
  export function createMockLogger(): SagaLogger {
71
117
  return {
72
118
  info: vi.fn(),
@@ -25,12 +25,12 @@ import {
25
25
  type AgentErrorClassification,
26
26
  classifyAgentError,
27
27
  } from "../adapters/claude/conversion/sdk-to-acp";
28
- import { selectRecentTurns } from "../adapters/claude/session/jsonl-hydration";
29
28
  import type { PermissionMode } from "../execution-mode";
30
29
  import { DEFAULT_CODEX_MODEL } from "../gateway-models";
30
+ import { HandoffCheckpointTracker } from "../handoff-checkpoint";
31
31
  import { PostHogAPIClient } from "../posthog-api";
32
32
  import {
33
- type ConversationTurn,
33
+ formatConversationForResume,
34
34
  type ResumeState,
35
35
  resumeFromLog,
36
36
  } from "../resume";
@@ -39,6 +39,8 @@ import { TreeTracker } from "../tree-tracker";
39
39
  import type {
40
40
  AgentMode,
41
41
  DeviceInfo,
42
+ GitCheckpointEvent,
43
+ HandoffLocalGitState,
42
44
  LogLevel,
43
45
  TaskRun,
44
46
  TaskRunArtifact,
@@ -53,7 +55,11 @@ import {
53
55
  promptBlocksToText,
54
56
  } from "./cloud-prompt";
55
57
  import { type JwtPayload, JwtValidationError, validateJwt } from "./jwt";
56
- import { jsonRpcRequestSchema, validateCommandParams } from "./schemas";
58
+ import {
59
+ handoffLocalGitStateSchema,
60
+ jsonRpcRequestSchema,
61
+ validateCommandParams,
62
+ } from "./schemas";
57
63
  import type { AgentServerConfig } from "./types";
58
64
 
59
65
  const agentErrorClassificationSchema = z.enum([
@@ -186,6 +192,7 @@ interface ActiveSession {
186
192
  permissionMode: PermissionMode;
187
193
  /** Whether a desktop client has ever connected via SSE during this session */
188
194
  hasDesktopConnected: boolean;
195
+ pendingHandoffGitState?: HandoffLocalGitState;
189
196
  }
190
197
 
191
198
  function getTaskRunStateString(
@@ -662,6 +669,10 @@ export class AgentServer {
662
669
  case POSTHOG_NOTIFICATIONS.CLOSE:
663
670
  case "close": {
664
671
  this.logger.debug("Close requested");
672
+ const localGitState = this.extractHandoffLocalGitState(params);
673
+ if (localGitState && this.session) {
674
+ this.session.pendingHandoffGitState = localGitState;
675
+ }
665
676
  await this.cleanupSession();
666
677
  return { closed: true };
667
678
  }
@@ -959,6 +970,7 @@ export class AgentServer {
959
970
  logWriter,
960
971
  permissionMode: initialPermissionMode,
961
972
  hasDesktopConnected: sseController !== null,
973
+ pendingHandoffGitState: undefined,
962
974
  };
963
975
 
964
976
  this.logger = new Logger({
@@ -1147,7 +1159,7 @@ export class AgentServer {
1147
1159
  if (!this.session || !this.resumeState) return;
1148
1160
 
1149
1161
  try {
1150
- const conversationSummary = this.formatConversationForResume(
1162
+ const conversationSummary = formatConversationForResume(
1151
1163
  this.resumeState.conversation,
1152
1164
  );
1153
1165
 
@@ -1229,59 +1241,6 @@ export class AgentServer {
1229
1241
  }
1230
1242
  }
1231
1243
 
1232
- private static RESUME_HISTORY_TOKEN_BUDGET = 50_000;
1233
- private static TOOL_RESULT_MAX_CHARS = 2000;
1234
-
1235
- private formatConversationForResume(
1236
- conversation: ConversationTurn[],
1237
- ): string {
1238
- const selected = selectRecentTurns(
1239
- conversation,
1240
- AgentServer.RESUME_HISTORY_TOKEN_BUDGET,
1241
- );
1242
- const parts: string[] = [];
1243
-
1244
- if (selected.length < conversation.length) {
1245
- parts.push(
1246
- `*(${conversation.length - selected.length} earlier turns omitted)*`,
1247
- );
1248
- }
1249
-
1250
- for (const turn of selected) {
1251
- const role = turn.role === "user" ? "User" : "Assistant";
1252
-
1253
- const textParts = turn.content
1254
- .filter((block) => block.type === "text")
1255
- .map((block) => (block as { type: "text"; text: string }).text);
1256
-
1257
- if (textParts.length > 0) {
1258
- parts.push(`**${role}**: ${textParts.join("\n")}`);
1259
- }
1260
-
1261
- if (turn.toolCalls?.length) {
1262
- const toolSummary = turn.toolCalls
1263
- .map((tc) => {
1264
- let resultStr = "";
1265
- if (tc.result !== undefined) {
1266
- const raw =
1267
- typeof tc.result === "string"
1268
- ? tc.result
1269
- : JSON.stringify(tc.result);
1270
- resultStr =
1271
- raw.length > AgentServer.TOOL_RESULT_MAX_CHARS
1272
- ? ` → ${raw.substring(0, AgentServer.TOOL_RESULT_MAX_CHARS)}...(truncated)`
1273
- : ` → ${raw}`;
1274
- }
1275
- return ` - ${tc.toolName}${resultStr}`;
1276
- })
1277
- .join("\n");
1278
- parts.push(`**${role} (tools)**:\n${toolSummary}`);
1279
- }
1280
- }
1281
-
1282
- return parts.join("\n\n");
1283
- }
1284
-
1285
1244
  private getInitialPromptOverride(taskRun: TaskRun): string | null {
1286
1245
  const state = taskRun.state as Record<string, unknown> | undefined;
1287
1246
  const override = state?.initial_prompt_override;
@@ -2158,6 +2117,12 @@ ${attributionInstructions}
2158
2117
 
2159
2118
  this.logger.debug("Cleaning up session");
2160
2119
 
2120
+ try {
2121
+ await this.captureHandoffCheckpoint();
2122
+ } catch (error) {
2123
+ this.logger.error("Failed to capture handoff checkpoint", error);
2124
+ }
2125
+
2161
2126
  try {
2162
2127
  await this.captureTreeState();
2163
2128
  } catch (error) {
@@ -2234,6 +2199,60 @@ ${attributionInstructions}
2234
2199
  }
2235
2200
  }
2236
2201
 
2202
+ private async captureHandoffCheckpoint(): Promise<void> {
2203
+ if (!this.session?.treeTracker || !this.session.pendingHandoffGitState) {
2204
+ return;
2205
+ }
2206
+ if (!this.posthogAPI) {
2207
+ this.logger.warn(
2208
+ "Skipping handoff checkpoint capture: PostHog API client is not configured",
2209
+ );
2210
+ return;
2211
+ }
2212
+
2213
+ const tracker = new HandoffCheckpointTracker({
2214
+ repositoryPath: this.config.repositoryPath ?? "/tmp/workspace",
2215
+ taskId: this.session.payload.task_id,
2216
+ runId: this.session.payload.run_id,
2217
+ apiClient: this.posthogAPI,
2218
+ logger: this.logger.child("HandoffCheckpoint"),
2219
+ });
2220
+
2221
+ const checkpoint = await tracker.captureForHandoff(
2222
+ this.session.pendingHandoffGitState,
2223
+ );
2224
+ if (!checkpoint) return;
2225
+
2226
+ const checkpointWithDevice: GitCheckpointEvent = {
2227
+ ...checkpoint,
2228
+ device: this.session.deviceInfo,
2229
+ };
2230
+
2231
+ const notification = {
2232
+ jsonrpc: "2.0" as const,
2233
+ method: POSTHOG_NOTIFICATIONS.GIT_CHECKPOINT,
2234
+ params: checkpointWithDevice,
2235
+ };
2236
+
2237
+ this.broadcastEvent({
2238
+ type: "notification",
2239
+ timestamp: new Date().toISOString(),
2240
+ notification,
2241
+ });
2242
+
2243
+ this.session.logWriter.appendRawLine(
2244
+ this.session.payload.run_id,
2245
+ JSON.stringify(notification),
2246
+ );
2247
+ }
2248
+
2249
+ private extractHandoffLocalGitState(
2250
+ params: Record<string, unknown>,
2251
+ ): HandoffLocalGitState | null {
2252
+ const result = handoffLocalGitStateSchema.safeParse(params.localGitState);
2253
+ return result.success ? result.data : null;
2254
+ }
2255
+
2237
2256
  private broadcastTurnComplete(stopReason: string): void {
2238
2257
  if (!this.session) return;
2239
2258
  this.broadcastEvent({
@@ -5,6 +5,19 @@ const httpHeaderSchema = z.object({
5
5
  value: z.string(),
6
6
  });
7
7
 
8
+ const nullishString = z
9
+ .string()
10
+ .nullish()
11
+ .transform((value) => value ?? null);
12
+
13
+ export const handoffLocalGitStateSchema = z.object({
14
+ head: nullishString,
15
+ branch: nullishString,
16
+ upstreamHead: nullishString,
17
+ upstreamRemote: nullishString,
18
+ upstreamMergeRef: nullishString,
19
+ });
20
+
8
21
  const remoteMcpServerSchema = z.object({
9
22
  type: z.enum(["http", "sse"]),
10
23
  name: z.string().min(1, "MCP server name is required"),
@@ -83,13 +96,19 @@ export const refreshSessionParamsSchema = z.object({
83
96
  mcpServers: mcpServersSchema,
84
97
  });
85
98
 
99
+ export const closeParamsSchema = z
100
+ .object({
101
+ localGitState: handoffLocalGitStateSchema.optional(),
102
+ })
103
+ .optional();
104
+
86
105
  export const commandParamsSchemas = {
87
106
  user_message: userMessageParamsSchema,
88
107
  "posthog/user_message": userMessageParamsSchema,
89
108
  cancel: z.object({}).optional(),
90
109
  "posthog/cancel": z.object({}).optional(),
91
- close: z.object({}).optional(),
92
- "posthog/close": z.object({}).optional(),
110
+ close: closeParamsSchema,
111
+ "posthog/close": closeParamsSchema,
93
112
  permission_response: permissionResponseParamsSchema,
94
113
  "posthog/permission_response": permissionResponseParamsSchema,
95
114
  set_config_option: setConfigOptionParamsSchema,
package/src/types.ts CHANGED
@@ -1,3 +1,8 @@
1
+ import type {
2
+ GitHandoffCheckpoint,
3
+ HandoffLocalGitState as GitHandoffLocalGitState,
4
+ } from "@posthog/git/handoff";
5
+
1
6
  /**
2
7
  * Stored custom notification following ACP extensibility model.
3
8
  * Custom notifications use underscore-prefixed methods (e.g., `_posthog/phase_start`).
@@ -196,3 +201,22 @@ export interface TreeSnapshot {
196
201
  export interface TreeSnapshotEvent extends TreeSnapshot {
197
202
  device?: DeviceInfo;
198
203
  }
204
+
205
+ export type HandoffLocalGitState = GitHandoffLocalGitState;
206
+
207
+ export interface GitCheckpoint extends GitHandoffCheckpoint {
208
+ artifactPath?: string;
209
+ indexArtifactPath?: string;
210
+ }
211
+
212
+ export interface GitCheckpointEvent extends GitCheckpoint {
213
+ device?: DeviceInfo;
214
+ }
215
+
216
+ /**
217
+ * Keeps the emitted `@posthog/agent/types` entrypoint as a runtime ESM module.
218
+ *
219
+ * `export {}` is stripped by tsup in this package, which leaves `dist/types.js`
220
+ * empty and breaks downstream type resolution for the exported subpath.
221
+ */
222
+ export const AGENT_TYPES_MODULE = true;