@posthog/agent 2.3.678 → 2.3.703
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/dist/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/mcp/tool-metadata.d.ts +4 -1
- package/dist/adapters/claude/mcp/tool-metadata.js +4 -0
- package/dist/adapters/claude/mcp/tool-metadata.js.map +1 -1
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +287 -28
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.js +70 -0
- package/dist/handoff-checkpoint.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/resume.js +70 -0
- package/dist/resume.js.map +1 -1
- package/dist/server/agent-server.js +436 -179
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +429 -172
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/claude/claude-agent.slash-command.test.ts +153 -0
- package/src/adapters/claude/claude-agent.ts +84 -1
- package/src/adapters/claude/context-breakdown.test.ts +130 -0
- package/src/adapters/claude/context-breakdown.ts +127 -0
- package/src/adapters/claude/conversion/acp-to-sdk.test.ts +39 -0
- package/src/adapters/claude/conversion/acp-to-sdk.ts +20 -24
- package/src/adapters/claude/mcp/tool-metadata.ts +6 -0
- package/src/adapters/claude/types.ts +3 -0
- package/src/adapters/codex/codex-agent.ts +46 -5
- package/src/adapters/codex/codex-client.test.ts +49 -1
- package/src/adapters/codex/session-state.ts +36 -5
|
@@ -116,6 +116,12 @@ export function getConnectedMcpServerNames(): string[] {
|
|
|
116
116
|
return [...names];
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/** Snapshot of every tool currently in the metadata cache. Used by the
|
|
120
|
+
* context-breakdown estimator to size the MCP category. */
|
|
121
|
+
export function getCachedMcpTools(): McpToolMetadata[] {
|
|
122
|
+
return [...mcpToolMetadataCache.values()];
|
|
123
|
+
}
|
|
124
|
+
|
|
119
125
|
export function getMcpToolApprovalState(
|
|
120
126
|
toolName: string,
|
|
121
127
|
): McpToolApprovalState | undefined {
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
11
11
|
import type { Pushable } from "../../utils/streams";
|
|
12
12
|
import type { BaseSession } from "../base-acp-agent";
|
|
13
|
+
import type { ContextBreakdownBaseline } from "./context-breakdown";
|
|
13
14
|
import type { McpToolApprovals } from "./mcp/tool-metadata";
|
|
14
15
|
import type { SettingsManager } from "./session/settings";
|
|
15
16
|
import type { CodeExecutionMode } from "./tools";
|
|
@@ -65,6 +66,8 @@ export type Session = BaseSession & {
|
|
|
65
66
|
pendingMessages: Map<string, PendingMessage>;
|
|
66
67
|
nextPendingOrder: number;
|
|
67
68
|
emitRawSDKMessages: boolean | SDKMessageFilter[];
|
|
69
|
+
/** Refreshed at session init and on MCP/skill changes. */
|
|
70
|
+
contextBreakdownBaseline?: ContextBreakdownBaseline;
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
export type ToolUseCache = {
|
|
@@ -64,6 +64,12 @@ import {
|
|
|
64
64
|
nodeWritableToWebWritable,
|
|
65
65
|
} from "../../utils/streams";
|
|
66
66
|
import { BaseAcpAgent, type BaseSession } from "../base-acp-agent";
|
|
67
|
+
import {
|
|
68
|
+
buildBreakdown,
|
|
69
|
+
type ContextBreakdownBaseline,
|
|
70
|
+
emptyBaseline,
|
|
71
|
+
estimateTokens,
|
|
72
|
+
} from "../claude/context-breakdown";
|
|
67
73
|
import { classifyAgentError } from "../error-classification";
|
|
68
74
|
import {
|
|
69
75
|
enabledLocalTools,
|
|
@@ -76,6 +82,7 @@ import { normalizeCodexConfigOptions } from "./models";
|
|
|
76
82
|
import {
|
|
77
83
|
type CodexSessionState,
|
|
78
84
|
createSessionState,
|
|
85
|
+
resetSessionState,
|
|
79
86
|
resetUsage,
|
|
80
87
|
} from "./session-state";
|
|
81
88
|
import { CodexSettingsManager } from "./settings";
|
|
@@ -161,6 +168,19 @@ function classifyPromptError(error: unknown): unknown {
|
|
|
161
168
|
);
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
// codex-rs/protocol/src/protocol.rs BASELINE_TOKENS — the always-resident
|
|
172
|
+
// floor (MCP schemas, skills, preset prompt) we can't attribute per-source.
|
|
173
|
+
const CODEX_BASELINE_TOKENS = 12000;
|
|
174
|
+
|
|
175
|
+
function buildCodexBaseline(
|
|
176
|
+
meta: NewSessionMeta | undefined,
|
|
177
|
+
): ContextBreakdownBaseline {
|
|
178
|
+
const baseline = emptyBaseline();
|
|
179
|
+
baseline.systemPrompt =
|
|
180
|
+
CODEX_BASELINE_TOKENS + estimateTokens(meta?.systemPrompt);
|
|
181
|
+
return baseline;
|
|
182
|
+
}
|
|
183
|
+
|
|
164
184
|
const CODEX_NATIVE_MODE: Record<CodeExecutionMode, CodexNativeMode> = {
|
|
165
185
|
auto: "auto",
|
|
166
186
|
default: "auto",
|
|
@@ -392,8 +412,10 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
392
412
|
response.configOptions,
|
|
393
413
|
);
|
|
394
414
|
|
|
395
|
-
// Initialize session state
|
|
396
|
-
this
|
|
415
|
+
// Initialize session state. Mutate in place — codex-client closure-
|
|
416
|
+
// captured this object in the constructor and writes contextUsed/
|
|
417
|
+
// accumulatedUsage to it on every upstream usage_update.
|
|
418
|
+
resetSessionState(this.sessionState, response.sessionId, params.cwd, {
|
|
397
419
|
taskRunId: meta?.taskRunId,
|
|
398
420
|
taskId: resolveTaskId(meta),
|
|
399
421
|
modeId: response.modes?.currentModeId ?? "auto",
|
|
@@ -402,6 +424,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
402
424
|
});
|
|
403
425
|
this.sessionId = response.sessionId;
|
|
404
426
|
this.sessionState.configOptions = response.configOptions ?? [];
|
|
427
|
+
this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
|
|
405
428
|
|
|
406
429
|
await this.applyInitialPermissionMode(
|
|
407
430
|
response.sessionId,
|
|
@@ -445,7 +468,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
445
468
|
// notifications (TURN_COMPLETE, USAGE_UPDATE) after a reload. newSession
|
|
446
469
|
// and unstable_resumeSession both do this; loadSession historically did
|
|
447
470
|
// not, which silently broke task-completion tracking on re-attach.
|
|
448
|
-
this.sessionState
|
|
471
|
+
resetSessionState(this.sessionState, params.sessionId, params.cwd, {
|
|
449
472
|
taskRunId: meta?.taskRunId,
|
|
450
473
|
taskId: resolveTaskId(meta),
|
|
451
474
|
modeId: response.modes?.currentModeId ?? "auto",
|
|
@@ -453,6 +476,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
453
476
|
});
|
|
454
477
|
this.sessionId = params.sessionId;
|
|
455
478
|
this.sessionState.configOptions = response.configOptions ?? [];
|
|
479
|
+
this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
|
|
456
480
|
|
|
457
481
|
if (meta?.taskRunId) {
|
|
458
482
|
await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
|
|
@@ -491,7 +515,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
491
515
|
loadResponse.modes?.currentModeId,
|
|
492
516
|
meta?.permissionMode,
|
|
493
517
|
);
|
|
494
|
-
this.sessionState
|
|
518
|
+
resetSessionState(this.sessionState, params.sessionId, params.cwd, {
|
|
495
519
|
taskRunId: meta?.taskRunId,
|
|
496
520
|
taskId: resolveTaskId(meta),
|
|
497
521
|
modeId: loadResponse.modes?.currentModeId ?? "auto",
|
|
@@ -499,6 +523,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
499
523
|
});
|
|
500
524
|
this.sessionId = params.sessionId;
|
|
501
525
|
this.sessionState.configOptions = loadResponse.configOptions ?? [];
|
|
526
|
+
this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
|
|
502
527
|
|
|
503
528
|
if (meta?.taskRunId) {
|
|
504
529
|
await this.client.extNotification(POSTHOG_NOTIFICATIONS.SDK_SESSION, {
|
|
@@ -538,7 +563,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
538
563
|
);
|
|
539
564
|
|
|
540
565
|
const requestedPermissionMode = toCodexPermissionMode(meta?.permissionMode);
|
|
541
|
-
this.sessionState
|
|
566
|
+
resetSessionState(this.sessionState, newResponse.sessionId, params.cwd, {
|
|
542
567
|
taskRunId: meta?.taskRunId,
|
|
543
568
|
taskId: resolveTaskId(meta),
|
|
544
569
|
modeId: newResponse.modes?.currentModeId ?? "auto",
|
|
@@ -546,6 +571,7 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
546
571
|
});
|
|
547
572
|
this.sessionId = newResponse.sessionId;
|
|
548
573
|
this.sessionState.configOptions = newResponse.configOptions ?? [];
|
|
574
|
+
this.sessionState.contextBreakdownBaseline = buildCodexBaseline(meta);
|
|
549
575
|
|
|
550
576
|
await this.applyInitialPermissionMode(
|
|
551
577
|
newResponse.sessionId,
|
|
@@ -730,6 +756,21 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
730
756
|
}
|
|
731
757
|
}
|
|
732
758
|
|
|
759
|
+
// Emit the per-source breakdown so the renderer's ContextBreakdownPopover
|
|
760
|
+
// has data to show. This fires regardless of `taskRunId` (local sessions
|
|
761
|
+
// need it too) and regardless of `response.usage` (codex-acp doesn't
|
|
762
|
+
// populate it — context size comes from upstream usage_update events,
|
|
763
|
+
// tracked on sessionState.contextUsed).
|
|
764
|
+
if (this.sessionState.contextUsed !== undefined) {
|
|
765
|
+
await this.client.extNotification(POSTHOG_NOTIFICATIONS.USAGE_UPDATE, {
|
|
766
|
+
sessionId: params.sessionId,
|
|
767
|
+
breakdown: buildBreakdown(
|
|
768
|
+
this.sessionState.contextBreakdownBaseline ?? emptyBaseline(),
|
|
769
|
+
this.sessionState.contextUsed,
|
|
770
|
+
),
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
|
|
733
774
|
return response;
|
|
734
775
|
}
|
|
735
776
|
|
|
@@ -14,7 +14,7 @@ vi.mock("../../enrichment/file-enricher", () => ({
|
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
16
|
import { createCodexClient } from "./codex-client";
|
|
17
|
-
import { createSessionState } from "./session-state";
|
|
17
|
+
import { createSessionState, resetSessionState } from "./session-state";
|
|
18
18
|
|
|
19
19
|
function makeUpstream(response: ReadTextFileResponse): AgentSideConnection & {
|
|
20
20
|
readTextFile: ReturnType<typeof vi.fn>;
|
|
@@ -288,3 +288,51 @@ describe("createCodexClient onStructuredOutput", () => {
|
|
|
288
288
|
expect(upstream.sessionUpdate).toHaveBeenCalledTimes(1);
|
|
289
289
|
});
|
|
290
290
|
});
|
|
291
|
+
|
|
292
|
+
describe("createCodexClient usage_update propagation", () => {
|
|
293
|
+
const logger = new Logger({ debug: false, prefix: "[test]" });
|
|
294
|
+
|
|
295
|
+
function makeUpstream(): AgentSideConnection {
|
|
296
|
+
return {
|
|
297
|
+
sessionUpdate: vi.fn(async () => {}),
|
|
298
|
+
requestPermission: vi.fn(),
|
|
299
|
+
readTextFile: vi.fn(),
|
|
300
|
+
writeTextFile: vi.fn(),
|
|
301
|
+
createTerminal: vi.fn(),
|
|
302
|
+
terminalOutput: vi.fn(),
|
|
303
|
+
releaseTerminal: vi.fn(),
|
|
304
|
+
waitForTerminalExit: vi.fn(),
|
|
305
|
+
killTerminal: vi.fn(),
|
|
306
|
+
extMethod: vi.fn(),
|
|
307
|
+
extNotification: vi.fn(),
|
|
308
|
+
} as unknown as AgentSideConnection;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Regression: codex-client closure-captures the sessionState reference in
|
|
312
|
+
// its factory. CodexAcpAgent constructs the client once at startup with the
|
|
313
|
+
// initial "" sessionId state, then resetSessionState() mutates that same
|
|
314
|
+
// object on every newSession/loadSession/etc. If the agent ever reassigned
|
|
315
|
+
// `this.sessionState`, contextUsed writes would land on an orphan and the
|
|
316
|
+
// breakdown notification would never fire.
|
|
317
|
+
test("writes contextUsed to the same state object after resetSessionState", async () => {
|
|
318
|
+
const sessionState = createSessionState("", "/tmp");
|
|
319
|
+
const upstream = makeUpstream();
|
|
320
|
+
const client = createCodexClient(upstream, logger, sessionState);
|
|
321
|
+
|
|
322
|
+
resetSessionState(sessionState, "real-session", "/tmp/repo", {
|
|
323
|
+
taskRunId: "run-1",
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
await client.sessionUpdate?.({
|
|
327
|
+
sessionId: "real-session",
|
|
328
|
+
update: {
|
|
329
|
+
sessionUpdate: "usage_update",
|
|
330
|
+
used: 123_456,
|
|
331
|
+
size: 200_000,
|
|
332
|
+
},
|
|
333
|
+
} as unknown as SessionNotification);
|
|
334
|
+
|
|
335
|
+
expect(sessionState.contextUsed).toBe(123_456);
|
|
336
|
+
expect(sessionState.contextSize).toBe(200_000);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session state tracking for Codex proxy agent.
|
|
3
|
-
* Tracks usage accumulation, model/mode state, and config options.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
1
|
import type { SessionConfigOption } from "@agentclientprotocol/sdk";
|
|
7
2
|
import type { PermissionMode } from "../../execution-mode";
|
|
3
|
+
import type { ContextBreakdownBaseline } from "../claude/context-breakdown";
|
|
8
4
|
|
|
9
5
|
export interface CodexUsage {
|
|
10
6
|
inputTokens: number;
|
|
@@ -22,6 +18,7 @@ export interface CodexSessionState {
|
|
|
22
18
|
accumulatedUsage: CodexUsage;
|
|
23
19
|
contextSize?: number;
|
|
24
20
|
contextUsed?: number;
|
|
21
|
+
contextBreakdownBaseline?: ContextBreakdownBaseline;
|
|
25
22
|
permissionMode: PermissionMode;
|
|
26
23
|
taskRunId?: string;
|
|
27
24
|
taskId?: string;
|
|
@@ -56,6 +53,40 @@ export function createSessionState(
|
|
|
56
53
|
};
|
|
57
54
|
}
|
|
58
55
|
|
|
56
|
+
// codex-client closure-captures the original sessionState reference, so we
|
|
57
|
+
// must mutate in place across newSession/loadSession/resumeSession/forkSession
|
|
58
|
+
// — reassigning would orphan it and silently break usage propagation.
|
|
59
|
+
export function resetSessionState(
|
|
60
|
+
state: CodexSessionState,
|
|
61
|
+
sessionId: string,
|
|
62
|
+
cwd: string,
|
|
63
|
+
opts?: {
|
|
64
|
+
taskRunId?: string;
|
|
65
|
+
taskId?: string;
|
|
66
|
+
modeId?: string;
|
|
67
|
+
modelId?: string;
|
|
68
|
+
permissionMode?: PermissionMode;
|
|
69
|
+
},
|
|
70
|
+
): void {
|
|
71
|
+
state.sessionId = sessionId;
|
|
72
|
+
state.cwd = cwd;
|
|
73
|
+
state.modeId = opts?.modeId ?? "auto";
|
|
74
|
+
state.modelId = opts?.modelId;
|
|
75
|
+
state.configOptions = [];
|
|
76
|
+
state.accumulatedUsage = {
|
|
77
|
+
inputTokens: 0,
|
|
78
|
+
outputTokens: 0,
|
|
79
|
+
cachedReadTokens: 0,
|
|
80
|
+
cachedWriteTokens: 0,
|
|
81
|
+
};
|
|
82
|
+
state.contextSize = undefined;
|
|
83
|
+
state.contextUsed = undefined;
|
|
84
|
+
state.contextBreakdownBaseline = undefined;
|
|
85
|
+
state.permissionMode = opts?.permissionMode ?? "auto";
|
|
86
|
+
state.taskRunId = opts?.taskRunId;
|
|
87
|
+
state.taskId = opts?.taskId;
|
|
88
|
+
}
|
|
89
|
+
|
|
59
90
|
export function resetUsage(state: CodexSessionState): void {
|
|
60
91
|
state.accumulatedUsage = {
|
|
61
92
|
inputTokens: 0,
|