@posthog/agent 2.3.727 → 2.3.735

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.3.727",
3
+ "version": "2.3.735",
4
4
  "repository": "https://github.com/PostHog/code",
5
5
  "description": "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
6
6
  "exports": {
@@ -107,8 +107,8 @@
107
107
  "typescript": "^5.5.0",
108
108
  "vitest": "^2.1.8",
109
109
  "@posthog/shared": "1.0.0",
110
- "@posthog/git": "1.0.0",
111
- "@posthog/enricher": "1.0.0"
110
+ "@posthog/enricher": "1.0.0",
111
+ "@posthog/git": "1.0.0"
112
112
  },
113
113
  "dependencies": {
114
114
  "@agentclientprotocol/sdk": "0.19.0",
@@ -53,6 +53,7 @@ Fork of `@anthropic-ai/claude-agent-acp`. Upstream repo: https://github.com/anth
53
53
  | Auth methods | `claude-ai-login` + `console-login` | Returns empty `authMethods` | Auth handled externally |
54
54
  | Session fingerprinting | Implicit teardown on cwd/mcp change | Explicit `refreshSession()` | Caller-initiated is more predictable |
55
55
  | Shutdown on ACP close | Process exits | No standalone process | Agent is embedded in server |
56
+ | Unsupported slash commands | Loops silently on early idle | Emits "Unsupported slash command" chunk, gated on `initializationResult().commands` so plugin/skill commands (e.g. `/skills-store`) whose echoes use a fresh uuid are not false-flagged | The SDK consumes some slash commands without producing output (e.g. `/plugin` in non-interactive mode); without this we hang. The known-commands gate avoids racing plugin/skill loads where idle can arrive before the transformed user-message echo. |
56
57
 
57
58
  ## Changes Ported in v0.30.0 Sync
58
59
 
@@ -34,7 +34,11 @@ function makeAgent(): { agent: Agent; client: ClientMocks } {
34
34
  return { agent, client };
35
35
  }
36
36
 
37
- function installFakeSession(agent: Agent, sessionId: string): MockQuery {
37
+ function installFakeSession(
38
+ agent: Agent,
39
+ sessionId: string,
40
+ knownSlashCommands?: Set<string>,
41
+ ): MockQuery {
38
42
  const query = createMockQuery();
39
43
  const input = new Pushable();
40
44
  const abortController = new AbortController();
@@ -63,6 +67,7 @@ function installFakeSession(agent: Agent, sessionId: string): MockQuery {
63
67
  taskRunId: "run-1",
64
68
  lastContextWindowSize: 200_000,
65
69
  modelId: "claude-sonnet-4-6",
70
+ knownSlashCommands,
66
71
  };
67
72
 
68
73
  (agent as unknown as { session: typeof session }).session = session;
@@ -99,6 +104,7 @@ describe("ClaudeAcpAgent.prompt — early idle handling", () => {
99
104
  label: "unsupported slash command surfaces error and ends turn",
100
105
  sessionId: "s-slash",
101
106
  prompt: "/plugin install slack",
107
+ knownCommands: undefined,
102
108
  expectsUnsupportedChunk: true,
103
109
  commandInMessage: "/plugin",
104
110
  },
@@ -106,6 +112,16 @@ describe("ClaudeAcpAgent.prompt — early idle handling", () => {
106
112
  label: "non-slash prompt with early idle is silently skipped",
107
113
  sessionId: "s-regular",
108
114
  prompt: "hello",
115
+ knownCommands: undefined,
116
+ expectsUnsupportedChunk: false,
117
+ commandInMessage: null,
118
+ },
119
+ {
120
+ label:
121
+ "known plugin/skill command with early idle is not flagged as unsupported",
122
+ sessionId: "s-skill",
123
+ prompt: "/skills-store use my address pr review skill",
124
+ knownCommands: new Set(["skills-store"]),
109
125
  expectsUnsupportedChunk: false,
110
126
  commandInMessage: null,
111
127
  },
@@ -113,7 +129,11 @@ describe("ClaudeAcpAgent.prompt — early idle handling", () => {
113
129
 
114
130
  it.each(cases)("$label", async (tc) => {
115
131
  const { agent, client } = makeAgent();
116
- const query = installFakeSession(agent, tc.sessionId);
132
+ const query = installFakeSession(
133
+ agent,
134
+ tc.sessionId,
135
+ tc.knownCommands as Set<string> | undefined,
136
+ );
117
137
 
118
138
  const promptPromise = agent.prompt({
119
139
  sessionId: tc.sessionId,
@@ -43,6 +43,7 @@ import {
43
43
  type Query,
44
44
  query,
45
45
  type SDKUserMessage,
46
+ type SlashCommand,
46
47
  } from "@anthropic-ai/claude-agent-sdk";
47
48
  import { v7 as uuidv7 } from "uuid";
48
49
  import packageJson from "../../../package.json" with { type: "json" };
@@ -143,6 +144,17 @@ function readClaudeMdQuietly(cwd: string, logger: Logger): string | undefined {
143
144
  }
144
145
  }
145
146
 
147
+ function collectKnownSlashCommands(
148
+ commands: SlashCommand[] | undefined,
149
+ ): Set<string> {
150
+ const names = new Set<string>();
151
+ if (!commands) return names;
152
+ for (const cmd of commands) {
153
+ if (cmd.name) names.add(cmd.name);
154
+ }
155
+ return names;
156
+ }
157
+
146
158
  function sanitizeTitle(text: string): string {
147
159
  const sanitized = text
148
160
  .replace(/[\r\n]+/g, " ")
@@ -500,7 +512,17 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
500
512
  // and produced no output (e.g. /plugin in a non-interactive
501
513
  // context). Without this branch we would loop forever waiting
502
514
  // for an echo that never comes; surface a clear error instead.
503
- if (commandMatch) {
515
+ //
516
+ // Only fire for commands the SDK does NOT recognize. Plugin
517
+ // and skill commands (e.g. /skills-store) produce a fresh
518
+ // user-message echo with a new uuid that our replay check
519
+ // can't match, so an early idle here is a race, not a real
520
+ // "unsupported" — fall through and let the loop continue.
521
+ const cmdName = commandMatch?.[1].slice(1);
522
+ const known =
523
+ cmdName !== undefined &&
524
+ this.session.knownSlashCommands?.has(cmdName) === true;
525
+ if (commandMatch && !known) {
504
526
  const cmd = commandMatch[1];
505
527
  this.logger.warn(
506
528
  "Slash command produced no output; treating as unsupported",
@@ -520,6 +542,8 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
520
542
  }
521
543
  this.logger.debug("Skipping idle state before prompt replay", {
522
544
  sessionId: params.sessionId,
545
+ command: commandMatch?.[1],
546
+ known,
523
547
  });
524
548
  break;
525
549
  }
@@ -1305,6 +1329,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
1305
1329
  `Session ${forkSession ? "fork" : "resumption"} timed out for sessionId=${sessionId}`,
1306
1330
  );
1307
1331
  }
1332
+ session.knownSlashCommands = collectKnownSlashCommands(
1333
+ result.value.commands,
1334
+ );
1308
1335
  } catch (err) {
1309
1336
  settingsManager.dispose();
1310
1337
  if (
@@ -1356,6 +1383,9 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
1356
1383
  `Session initialization timed out for sessionId=${sessionId}`,
1357
1384
  );
1358
1385
  }
1386
+ session.knownSlashCommands = collectKnownSlashCommands(
1387
+ initResult.value.commands,
1388
+ );
1359
1389
  } catch (err) {
1360
1390
  settingsManager.dispose();
1361
1391
  this.logger.error("Session initialization failed", {
@@ -68,6 +68,13 @@ export type Session = BaseSession & {
68
68
  emitRawSDKMessages: boolean | SDKMessageFilter[];
69
69
  /** Refreshed at session init and on MCP/skill changes. */
70
70
  contextBreakdownBaseline?: ContextBreakdownBaseline;
71
+ /**
72
+ * Slash command names (without leading slash) the SDK recognizes for this
73
+ * session — built-ins plus plugin/skill commands. Captured from the SDK's
74
+ * init response. Used to distinguish "command produced no output" from
75
+ * "command is genuinely unknown" when the session goes idle without an echo.
76
+ */
77
+ knownSlashCommands?: Set<string>;
71
78
  };
72
79
 
73
80
  export type ToolUseCache = {