@posthog/agent 2.3.305 → 2.3.308
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/session/jsonl-hydration.js +0 -3
- package/dist/adapters/claude/session/jsonl-hydration.js.map +1 -1
- package/dist/agent.js +20 -29
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +1 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +22 -34
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +22 -34
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +1 -1
- package/src/adapters/acp-connection.ts +0 -2
- package/src/adapters/claude/claude-agent.ts +1 -25
- package/src/adapters/claude/session/jsonl-hydration.ts +0 -3
- package/src/adapters/codex/codex-agent.test.ts +86 -0
- package/src/adapters/codex/codex-agent.ts +18 -0
- package/src/agent.ts +0 -3
- package/src/session-log-writer.ts +0 -4
package/package.json
CHANGED
|
@@ -103,7 +103,6 @@ function createClaudeConnection(config: AcpConnectionConfig): AcpConnection {
|
|
|
103
103
|
...config.processCallbacks,
|
|
104
104
|
onStructuredOutput: config.onStructuredOutput,
|
|
105
105
|
});
|
|
106
|
-
logger.info(`Created ${agent.adapterName} agent`);
|
|
107
106
|
return agent;
|
|
108
107
|
}, agentStream);
|
|
109
108
|
|
|
@@ -194,7 +193,6 @@ function createCodexConnection(config: AcpConnectionConfig): AcpConnection {
|
|
|
194
193
|
codexProcessOptions: config.codexOptions ?? {},
|
|
195
194
|
processCallbacks: config.processCallbacks,
|
|
196
195
|
});
|
|
197
|
-
logger.info(`Created ${agent.adapterName} agent`);
|
|
198
196
|
return agent;
|
|
199
197
|
}, agentStream);
|
|
200
198
|
|
|
@@ -433,11 +433,6 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
435
|
this.session.lastContextWindowSize = lastContextWindowSize;
|
|
436
|
-
this.logger.debug("Context window size from result", {
|
|
437
|
-
sdkReported: contextWindows,
|
|
438
|
-
resolved: lastContextWindowSize,
|
|
439
|
-
modelId: this.session.modelId,
|
|
440
|
-
});
|
|
441
436
|
|
|
442
437
|
this.session.contextSize = lastContextWindowSize;
|
|
443
438
|
if (lastAssistantTotalUsage !== null) {
|
|
@@ -963,7 +958,7 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
963
958
|
? { type: "json_schema" as const, schema: meta.jsonSchema }
|
|
964
959
|
: undefined;
|
|
965
960
|
|
|
966
|
-
this.logger.
|
|
961
|
+
this.logger.debug(isResume ? "Resuming session" : "Creating new session", {
|
|
967
962
|
sessionId,
|
|
968
963
|
taskId,
|
|
969
964
|
taskRunId: meta?.taskRunId,
|
|
@@ -1033,13 +1028,6 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
1033
1028
|
this.session = session;
|
|
1034
1029
|
this.sessionId = sessionId;
|
|
1035
1030
|
|
|
1036
|
-
this.logger.info(
|
|
1037
|
-
isResume
|
|
1038
|
-
? "Session query initialized, awaiting resumption"
|
|
1039
|
-
: "Session query initialized, awaiting initialization",
|
|
1040
|
-
{ sessionId, taskId, taskRunId: meta?.taskRunId },
|
|
1041
|
-
);
|
|
1042
|
-
|
|
1043
1031
|
if (isResume) {
|
|
1044
1032
|
// Resume must block on initialization to validate the session is still alive.
|
|
1045
1033
|
// For stale sessions this throws (e.g. "No conversation found").
|
|
@@ -1166,17 +1154,6 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
1166
1154
|
this.deferBackgroundFetches(q);
|
|
1167
1155
|
}
|
|
1168
1156
|
|
|
1169
|
-
this.logger.info(
|
|
1170
|
-
isResume
|
|
1171
|
-
? "Session resumed successfully"
|
|
1172
|
-
: "Session created successfully",
|
|
1173
|
-
{
|
|
1174
|
-
sessionId,
|
|
1175
|
-
taskId,
|
|
1176
|
-
taskRunId: meta?.taskRunId,
|
|
1177
|
-
},
|
|
1178
|
-
);
|
|
1179
|
-
|
|
1180
1157
|
return { sessionId, modes, models, configOptions };
|
|
1181
1158
|
}
|
|
1182
1159
|
|
|
@@ -1412,7 +1389,6 @@ export class ClaudeAcpAgent extends BaseAcpAgent {
|
|
|
1412
1389
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
1413
1390
|
*/
|
|
1414
1391
|
private deferBackgroundFetches(q: Query): void {
|
|
1415
|
-
this.logger.info("Starting background fetches (commands + MCP metadata)");
|
|
1416
1392
|
Promise.all([
|
|
1417
1393
|
new Promise<void>((resolve) => setTimeout(resolve, 10)).then(() =>
|
|
1418
1394
|
this.sendAvailableCommandsUpdate(),
|
|
@@ -503,9 +503,6 @@ export async function hydrateSessionJsonl(params: {
|
|
|
503
503
|
const jsonlPath = getSessionJsonlPath(params.sessionId, params.cwd);
|
|
504
504
|
try {
|
|
505
505
|
await fs.access(jsonlPath);
|
|
506
|
-
log.info("Local JSONL exists, skipping S3 hydration", {
|
|
507
|
-
sessionId: params.sessionId,
|
|
508
|
-
});
|
|
509
506
|
return true;
|
|
510
507
|
} catch {
|
|
511
508
|
// File doesn't exist, proceed with hydration
|
|
@@ -209,6 +209,92 @@ describe("CodexAcpAgent", () => {
|
|
|
209
209
|
});
|
|
210
210
|
});
|
|
211
211
|
|
|
212
|
+
it("serializes concurrent prompts so usage accumulators are not wiped mid-turn", async () => {
|
|
213
|
+
const { agent } = createAgent();
|
|
214
|
+
mockCodexConnection.newSession.mockResolvedValue({
|
|
215
|
+
sessionId: "session-1",
|
|
216
|
+
modes: { currentModeId: "auto", availableModes: [] },
|
|
217
|
+
configOptions: [],
|
|
218
|
+
} satisfies Partial<NewSessionResponse>);
|
|
219
|
+
await agent.newSession({
|
|
220
|
+
cwd: process.cwd(),
|
|
221
|
+
_meta: { taskRunId: "run-1" },
|
|
222
|
+
} as never);
|
|
223
|
+
|
|
224
|
+
const callOrder: string[] = [];
|
|
225
|
+
let releaseA: () => void;
|
|
226
|
+
const aStarted = new Promise<void>((resolve) => {
|
|
227
|
+
releaseA = resolve;
|
|
228
|
+
});
|
|
229
|
+
let allowAResolve: () => void;
|
|
230
|
+
const aHold = new Promise<void>((resolve) => {
|
|
231
|
+
allowAResolve = resolve;
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
mockCodexConnection.prompt.mockImplementationOnce(async () => {
|
|
235
|
+
callOrder.push("A:start");
|
|
236
|
+
releaseA();
|
|
237
|
+
await aHold;
|
|
238
|
+
callOrder.push("A:end");
|
|
239
|
+
return { stopReason: "end_turn" };
|
|
240
|
+
});
|
|
241
|
+
mockCodexConnection.prompt.mockImplementationOnce(async () => {
|
|
242
|
+
callOrder.push("B:start");
|
|
243
|
+
return { stopReason: "end_turn" };
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const promptA = agent.prompt({
|
|
247
|
+
sessionId: "session-1",
|
|
248
|
+
prompt: [{ type: "text", text: "A" }],
|
|
249
|
+
} as never);
|
|
250
|
+
|
|
251
|
+
await aStarted;
|
|
252
|
+
|
|
253
|
+
const promptB = agent.prompt({
|
|
254
|
+
sessionId: "session-1",
|
|
255
|
+
prompt: [{ type: "text", text: "B" }],
|
|
256
|
+
} as never);
|
|
257
|
+
|
|
258
|
+
// B must not have started while A is still in-flight.
|
|
259
|
+
expect(callOrder).toEqual(["A:start"]);
|
|
260
|
+
|
|
261
|
+
allowAResolve!();
|
|
262
|
+
await Promise.all([promptA, promptB]);
|
|
263
|
+
|
|
264
|
+
expect(callOrder).toEqual(["A:start", "A:end", "B:start"]);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("does not let a failing prompt block subsequent prompts", async () => {
|
|
268
|
+
const { agent } = createAgent();
|
|
269
|
+
mockCodexConnection.newSession.mockResolvedValue({
|
|
270
|
+
sessionId: "session-1",
|
|
271
|
+
modes: { currentModeId: "auto", availableModes: [] },
|
|
272
|
+
configOptions: [],
|
|
273
|
+
} satisfies Partial<NewSessionResponse>);
|
|
274
|
+
await agent.newSession({
|
|
275
|
+
cwd: process.cwd(),
|
|
276
|
+
} as never);
|
|
277
|
+
|
|
278
|
+
mockCodexConnection.prompt.mockRejectedValueOnce(new Error("boom"));
|
|
279
|
+
mockCodexConnection.prompt.mockResolvedValueOnce({
|
|
280
|
+
stopReason: "end_turn",
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
await expect(
|
|
284
|
+
agent.prompt({
|
|
285
|
+
sessionId: "session-1",
|
|
286
|
+
prompt: [{ type: "text", text: "A" }],
|
|
287
|
+
} as never),
|
|
288
|
+
).rejects.toThrow("boom");
|
|
289
|
+
|
|
290
|
+
await expect(
|
|
291
|
+
agent.prompt({
|
|
292
|
+
sessionId: "session-1",
|
|
293
|
+
prompt: [{ type: "text", text: "B" }],
|
|
294
|
+
} as never),
|
|
295
|
+
).resolves.toEqual({ stopReason: "end_turn" });
|
|
296
|
+
});
|
|
297
|
+
|
|
212
298
|
it("broadcasts user prompt as user_message_chunk before delegating to codex-acp", async () => {
|
|
213
299
|
const { agent, client } = createAgent();
|
|
214
300
|
// Seed an active session so prompt() has the state it expects.
|
|
@@ -145,6 +145,17 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
145
145
|
private codexProcess: CodexProcess;
|
|
146
146
|
private codexConnection: ClientSideConnection;
|
|
147
147
|
private sessionState: CodexSessionState;
|
|
148
|
+
/**
|
|
149
|
+
* FIFO serializer for prompt() calls. codex-acp and codex-rs themselves
|
|
150
|
+
* serialize submissions at the conversation level, but our adapter
|
|
151
|
+
* accumulates per-turn usage into sessionState.accumulatedUsage via the
|
|
152
|
+
* codex-client sessionUpdate handler. If two prompts ran concurrently on
|
|
153
|
+
* the JS side, the second's resetUsage() would wipe out the first's
|
|
154
|
+
* in-flight counters and both TURN_COMPLETE notifications would report
|
|
155
|
+
* garbled totals. Serializing on the JS side keeps the accumulator
|
|
156
|
+
* single-owner.
|
|
157
|
+
*/
|
|
158
|
+
private promptMutex: Promise<unknown> = Promise.resolve();
|
|
148
159
|
|
|
149
160
|
constructor(client: AgentSideConnection, options: CodexAcpAgentOptions) {
|
|
150
161
|
super(client);
|
|
@@ -397,6 +408,13 @@ export class CodexAcpAgent extends BaseAcpAgent {
|
|
|
397
408
|
}
|
|
398
409
|
|
|
399
410
|
async prompt(params: PromptRequest): Promise<PromptResponse> {
|
|
411
|
+
const previous = this.promptMutex;
|
|
412
|
+
const next = previous.catch(() => {}).then(() => this.runPrompt(params));
|
|
413
|
+
this.promptMutex = next;
|
|
414
|
+
return next;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private async runPrompt(params: PromptRequest): Promise<PromptResponse> {
|
|
400
418
|
this.session.cancelled = false;
|
|
401
419
|
this.session.interruptReason = undefined;
|
|
402
420
|
resetUsage(this.sessionState);
|
package/src/agent.ts
CHANGED
|
@@ -76,9 +76,6 @@ export class Agent {
|
|
|
76
76
|
options: TaskExecutionOptions = {},
|
|
77
77
|
): Promise<InProcessAcpConnection> {
|
|
78
78
|
const gatewayConfig = await this._configureLlmGateway(options.gatewayUrl);
|
|
79
|
-
this.logger.info("Configured LLM gateway", {
|
|
80
|
-
adapter: options.adapter,
|
|
81
|
-
});
|
|
82
79
|
this.taskRunId = taskRunId;
|
|
83
80
|
|
|
84
81
|
let allowedModelIds: Set<string> | undefined;
|
|
@@ -70,10 +70,6 @@ export class SessionLogWriter {
|
|
|
70
70
|
return;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
this.logger.info("Session registered", {
|
|
74
|
-
taskId: context.taskId,
|
|
75
|
-
runId: context.runId,
|
|
76
|
-
});
|
|
77
73
|
this.sessions.set(sessionId, { context, currentTurnMessages: [] });
|
|
78
74
|
|
|
79
75
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|