@posthog/agent 2.3.519 → 2.3.524
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/LICENSE +21 -33
- package/dist/adapters/codex/structured-output-mcp-server.d.ts +2 -0
- package/dist/adapters/codex/structured-output-mcp-server.js +54 -0
- package/dist/adapters/codex/structured-output-mcp-server.js.map +1 -0
- package/dist/agent.js +127 -21
- package/dist/agent.js.map +1 -1
- package/dist/posthog-api.js +3 -3
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +127 -21
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +150 -43
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +5 -5
- package/src/adapters/acp-connection.ts +1 -0
- package/src/adapters/codex/codex-agent.test.ts +134 -1
- package/src/adapters/codex/codex-agent.ts +122 -17
- package/src/adapters/codex/codex-client.test.ts +178 -0
- package/src/adapters/codex/codex-client.ts +68 -0
- package/src/adapters/codex/structured-output-constants.ts +9 -0
- package/src/adapters/codex/structured-output-mcp-server.ts +72 -0
|
@@ -36,12 +36,45 @@ import {
|
|
|
36
36
|
import type { PermissionMode } from "../../execution-mode";
|
|
37
37
|
import type { Logger } from "../../utils/logger";
|
|
38
38
|
import type { CodexSessionState } from "./session-state";
|
|
39
|
+
import {
|
|
40
|
+
STRUCTURED_OUTPUT_MCP_NAME,
|
|
41
|
+
STRUCTURED_OUTPUT_TOOL_NAME,
|
|
42
|
+
} from "./structured-output-constants";
|
|
39
43
|
|
|
40
44
|
export interface CodexClientCallbacks {
|
|
41
45
|
/** Called when a usage_update session notification is received */
|
|
42
46
|
onUsageUpdate?: (update: Record<string, unknown>) => void;
|
|
43
47
|
/** When set, Read responses are annotated with PostHog enrichment before reaching codex-acp. */
|
|
44
48
|
enrichmentDeps?: FileEnrichmentDeps;
|
|
49
|
+
/**
|
|
50
|
+
* Called once per session when the agent completes the injected
|
|
51
|
+
* `create_output` MCP tool. Matches the Claude adapter's structured
|
|
52
|
+
* output delivery.
|
|
53
|
+
*/
|
|
54
|
+
onStructuredOutput?: (output: Record<string, unknown>) => Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Tool calls for our injected MCP server surface in ACP `tool_call` /
|
|
59
|
+
* `tool_call_update` notifications. The `title` from codex-acp can be
|
|
60
|
+
* either the bare tool name or prefixed (`mcp__<server>__<tool>`); match
|
|
61
|
+
* both forms but require the server name on prefixed titles so an unrelated
|
|
62
|
+
* user tool happening to contain `create_output` doesn't trigger us.
|
|
63
|
+
*/
|
|
64
|
+
function isStructuredOutputToolCall(title: string | undefined | null): boolean {
|
|
65
|
+
if (!title) return false;
|
|
66
|
+
if (title === STRUCTURED_OUTPUT_TOOL_NAME) return true;
|
|
67
|
+
return (
|
|
68
|
+
title.includes(STRUCTURED_OUTPUT_MCP_NAME) &&
|
|
69
|
+
title.includes(STRUCTURED_OUTPUT_TOOL_NAME)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function toRecord(value: unknown): Record<string, unknown> | null {
|
|
74
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
75
|
+
return value as Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
45
78
|
}
|
|
46
79
|
|
|
47
80
|
const AUTO_APPROVED_KINDS: Record<PermissionMode, Set<ToolKind>> = {
|
|
@@ -96,6 +129,14 @@ export function createCodexClient(
|
|
|
96
129
|
callbacks?: CodexClientCallbacks,
|
|
97
130
|
): Client {
|
|
98
131
|
const terminalHandles = new Map<string, TerminalHandle>();
|
|
132
|
+
// Track rawInput across tool_call → tool_call_update → completed so we can
|
|
133
|
+
// fire onStructuredOutput exactly once per tool call id. Entries stay in
|
|
134
|
+
// the map after firing with `fired: true` so a re-emitted completion
|
|
135
|
+
// (if codex-acp ever resends one) is a no-op.
|
|
136
|
+
const structuredOutputState = new Map<
|
|
137
|
+
string,
|
|
138
|
+
{ rawInput?: Record<string, unknown>; fired: boolean }
|
|
139
|
+
>();
|
|
99
140
|
|
|
100
141
|
return {
|
|
101
142
|
async requestPermission(
|
|
@@ -125,6 +166,33 @@ export function createCodexClient(
|
|
|
125
166
|
|
|
126
167
|
async sessionUpdate(params: SessionNotification): Promise<void> {
|
|
127
168
|
const update = params.update as Record<string, unknown> | undefined;
|
|
169
|
+
|
|
170
|
+
if (
|
|
171
|
+
callbacks?.onStructuredOutput &&
|
|
172
|
+
(update?.sessionUpdate === "tool_call" ||
|
|
173
|
+
update?.sessionUpdate === "tool_call_update")
|
|
174
|
+
) {
|
|
175
|
+
const toolCallId = update.toolCallId as string | undefined;
|
|
176
|
+
const title = update.title as string | undefined;
|
|
177
|
+
if (toolCallId && isStructuredOutputToolCall(title)) {
|
|
178
|
+
const entry = structuredOutputState.get(toolCallId) ?? {
|
|
179
|
+
fired: false,
|
|
180
|
+
};
|
|
181
|
+
const rawInput = toRecord(update.rawInput);
|
|
182
|
+
if (rawInput) entry.rawInput = rawInput;
|
|
183
|
+
structuredOutputState.set(toolCallId, entry);
|
|
184
|
+
|
|
185
|
+
if (update.status === "completed" && !entry.fired && entry.rawInput) {
|
|
186
|
+
entry.fired = true;
|
|
187
|
+
try {
|
|
188
|
+
await callbacks.onStructuredOutput(entry.rawInput);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
logger.warn("onStructuredOutput callback threw", { error: err });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
128
196
|
if (update?.sessionUpdate === "usage_update") {
|
|
129
197
|
const used = update.used as number | undefined;
|
|
130
198
|
const size = update.size as number | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared identifiers for the injected structured-output MCP server.
|
|
3
|
+
* Imported by codex-agent.ts (server config), codex-client.ts (tool-call
|
|
4
|
+
* matching), and structured-output-mcp-server.ts (tool registration) so the
|
|
5
|
+
* three stay in sync.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const STRUCTURED_OUTPUT_MCP_NAME = "posthog_output";
|
|
9
|
+
export const STRUCTURED_OUTPUT_TOOL_NAME = "create_output";
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone stdio MCP server for structured output in the Codex adapter.
|
|
3
|
+
*
|
|
4
|
+
* Spawned by codex-acp as an MCP server process. Reads the JSON schema
|
|
5
|
+
* from the POSTHOG_OUTPUT_SCHEMA env var (base64-encoded) and registers
|
|
6
|
+
* a tool whose Zod shape McpServer.tool() validates on each call.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* POSTHOG_OUTPUT_SCHEMA=<base64> node structured-output-mcp-server.js
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
import {
|
|
16
|
+
STRUCTURED_OUTPUT_MCP_NAME,
|
|
17
|
+
STRUCTURED_OUTPUT_TOOL_NAME,
|
|
18
|
+
} from "./structured-output-constants";
|
|
19
|
+
|
|
20
|
+
function die(message: string): never {
|
|
21
|
+
process.stderr.write(`[structured-output-mcp-server] ${message}\n`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const schemaEnv = process.env.POSTHOG_OUTPUT_SCHEMA;
|
|
26
|
+
if (!schemaEnv) {
|
|
27
|
+
die("POSTHOG_OUTPUT_SCHEMA env var is required");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let jsonSchema: Record<string, unknown>;
|
|
31
|
+
try {
|
|
32
|
+
jsonSchema = JSON.parse(Buffer.from(schemaEnv, "base64").toString("utf-8"));
|
|
33
|
+
} catch (err) {
|
|
34
|
+
die(`Failed to parse POSTHOG_OUTPUT_SCHEMA as base64-encoded JSON: ${err}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const zodType = z.fromJSONSchema(jsonSchema);
|
|
38
|
+
if (!(zodType instanceof z.ZodObject)) {
|
|
39
|
+
die(
|
|
40
|
+
`POSTHOG_OUTPUT_SCHEMA must describe a JSON object schema (got ${zodType.constructor.name})`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
// McpServer.tool() expects a mutable ZodRawShape
|
|
44
|
+
const zodShape = { ...zodType.shape } as z.ZodRawShape;
|
|
45
|
+
|
|
46
|
+
const server = new McpServer({
|
|
47
|
+
name: STRUCTURED_OUTPUT_MCP_NAME,
|
|
48
|
+
version: "1.0.0",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
server.tool(
|
|
52
|
+
STRUCTURED_OUTPUT_TOOL_NAME,
|
|
53
|
+
"Submit the structured output for this task. Call this tool with the required fields to deliver your final result.",
|
|
54
|
+
zodShape,
|
|
55
|
+
async () => {
|
|
56
|
+
// McpServer.tool() validates `args` against `zodShape` before invoking
|
|
57
|
+
// this handler, so reaching this point means the input is valid. The
|
|
58
|
+
// parent process captures the validated output by intercepting the
|
|
59
|
+
// tool call in the ACP stream.
|
|
60
|
+
return {
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: "text" as const,
|
|
64
|
+
text: "Output submitted successfully.",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const transport = new StdioServerTransport();
|
|
72
|
+
await server.connect(transport);
|