@posthog/wizard 2.52.1 → 2.54.0
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/{add-mcp-server-to-clients-C7P4_dhF.js → add-mcp-server-to-clients-B3TcwTT5.js} +5 -5
- package/dist/{add-mcp-server-to-clients-C7P4_dhF.js.map → add-mcp-server-to-clients-B3TcwTT5.js.map} +1 -1
- package/dist/{agent-interface-oqI_K3-4.js → agent-interface-CLvG6Rk9.js} +338 -28
- package/dist/agent-interface-CLvG6Rk9.js.map +1 -0
- package/dist/{agent-prompt-loader-DwFFfuZ4.js → agent-prompt-loader-Baqm1JMD.js} +5 -5
- package/dist/{agent-prompt-loader-DwFFfuZ4.js.map → agent-prompt-loader-Baqm1JMD.js.map} +1 -1
- package/dist/{agent-runner-CunYPDzL.js → agent-runner-D6zGTJqn.js} +179 -145
- package/dist/agent-runner-D6zGTJqn.js.map +1 -0
- package/dist/analytics-BBJE4hIu.js +2 -0
- package/dist/{analytics-DsXLkDwE.js → analytics-D7hsukY7.js} +5 -2
- package/dist/analytics-D7hsukY7.js.map +1 -0
- package/dist/{api-Dpnv0jVL.js → api-BSq9hLu5.js} +3 -3
- package/dist/{api-Dpnv0jVL.js.map → api-BSq9hLu5.js.map} +1 -1
- package/dist/bin.js +329 -190
- package/dist/bin.js.map +1 -1
- package/dist/{bounded-fs-bvlHO1PM.js → bounded-fs-Cq960M-V.js} +3 -3
- package/dist/{bounded-fs-bvlHO1PM.js.map → bounded-fs-Cq960M-V.js.map} +1 -1
- package/dist/{ci-install-BLSCzEZs.js → ci-install-w-mywB9x.js} +5 -5
- package/dist/{ci-install-BLSCzEZs.js.map → ci-install-w-mywB9x.js.map} +1 -1
- package/dist/{debug-D-lnRFLU.js → debug-CX6ZqL9r.js} +1 -1
- package/dist/{debug-BH67OSPW.js → debug-CbgLkSCY.js} +19 -10
- package/dist/debug-CbgLkSCY.js.map +1 -0
- package/dist/{defaults-DAE9_QMn.js → defaults-BWLIffh5.js} +1 -1
- package/dist/{defaults-DAE9_QMn.js.map → defaults-BWLIffh5.js.map} +1 -1
- package/dist/{env-api-key-DDVkPQmo.js → env-api-key-cjD4yfGT.js} +1 -1
- package/dist/{env-api-key-DDVkPQmo.js.map → env-api-key-cjD4yfGT.js.map} +1 -1
- package/dist/{environment-CPPBK-08.js → environment-BWuaOExh.js} +4 -4
- package/dist/{environment-CPPBK-08.js.map → environment-BWuaOExh.js.map} +1 -1
- package/dist/{file-watcher-Dgbe0xK9.js → file-watcher-D9tgVBkp.js} +2 -2
- package/dist/{file-watcher-Dgbe0xK9.js.map → file-watcher-D9tgVBkp.js.map} +1 -1
- package/dist/{headless-ui-BJbByiLj.js → headless-ui-rH97E35y.js} +2 -2
- package/dist/{headless-ui-BJbByiLj.js.map → headless-ui-rH97E35y.js.map} +1 -1
- package/dist/{interactive-ClJi6jV4.js → interactive-D-MVMdBl.js} +3 -3
- package/dist/{interactive-ClJi6jV4.js.map → interactive-D-MVMdBl.js.map} +1 -1
- package/dist/{mcp-C5pjmTsm.js → mcp-jKHwa45f.js} +2 -2
- package/dist/{mcp-C5pjmTsm.js.map → mcp-jKHwa45f.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-BFDtuwJN.js → mcp-prompt-streaming-DxDftNHb.js} +5 -4
- package/dist/{mcp-prompt-streaming-BFDtuwJN.js.map → mcp-prompt-streaming-DxDftNHb.js.map} +1 -1
- package/dist/{non-interactive-C3DW7sGO.js → non-interactive-eXT2_WCR.js} +2 -2
- package/dist/{non-interactive-C3DW7sGO.js.map → non-interactive-eXT2_WCR.js.map} +1 -1
- package/dist/{orchestrator-tools-S88ycFO0.js → orchestrator-tools-CsXrPhsd.js} +3 -3
- package/dist/{orchestrator-tools-S88ycFO0.js.map → orchestrator-tools-CsXrPhsd.js.map} +1 -1
- package/dist/{package-manager-De6lmm9s.js → package-manager-CASp14jY.js} +2 -2
- package/dist/{package-manager-De6lmm9s.js.map → package-manager-CASp14jY.js.map} +1 -1
- package/dist/pi-CfdqOKDI.js +611 -0
- package/dist/pi-CfdqOKDI.js.map +1 -0
- package/dist/{playground-B4yTvjn4.js → playground-DuBvnGkP.js} +354 -19
- package/dist/playground-DuBvnGkP.js.map +1 -0
- package/dist/{posthog-mgz-FQlS.js → posthog-DDLIrku0.js} +1 -1
- package/dist/{posthog-mgz-FQlS.js.map → posthog-DDLIrku0.js.map} +1 -1
- package/dist/{posthog-integration-BA2aFHGz.js → posthog-integration-Dy5cNPaa.js} +16 -16
- package/dist/{posthog-integration-BA2aFHGz.js.map → posthog-integration-Dy5cNPaa.js.map} +1 -1
- package/dist/{provisioning-CtC0Ekeq.js → provisioning-CS6fQriR.js} +3 -3
- package/dist/{provisioning-CtC0Ekeq.js.map → provisioning-CS6fQriR.js.map} +1 -1
- package/dist/{queue-tools-DXg_5old.js → queue-tools-ai7gCWyU.js} +45 -32
- package/dist/queue-tools-ai7gCWyU.js.map +1 -0
- package/dist/{registry-B6ubVKTJ.js → registry-D0hx_-KP.js} +5 -5
- package/dist/{registry-B6ubVKTJ.js.map → registry-D0hx_-KP.js.map} +1 -1
- package/dist/{security-np5qnEMM.js → security-BNqgFjYh.js} +5 -5
- package/dist/security-BNqgFjYh.js.map +1 -0
- package/dist/{setup-utils-QwrY_J01.js → setup-utils-6qyTDNyd.js} +8 -8
- package/dist/{setup-utils-QwrY_J01.js.map → setup-utils-6qyTDNyd.js.map} +1 -1
- package/dist/{start-tui-DHEtMtnm.js → start-tui-DNkm-n-4.js} +36 -22
- package/dist/start-tui-DNkm-n-4.js.map +1 -0
- package/dist/{steps-ehGnaz9V.js → steps-CqHAVc8R.js} +7 -7
- package/dist/{steps-ehGnaz9V.js.map → steps-CqHAVc8R.js.map} +1 -1
- package/dist/{store-Cp_jvnNp.js → store-sIaF89XA.js} +6 -6
- package/dist/{store-Cp_jvnNp.js.map → store-sIaF89XA.js.map} +1 -1
- package/dist/{subagent-B9GiHZVj.js → subagent-CKNbRtfQ.js} +2 -2
- package/dist/{subagent-B9GiHZVj.js.map → subagent-CKNbRtfQ.js.map} +1 -1
- package/dist/{task-CmWToh2Q.js → task-DwzRxORh.js} +33 -23
- package/dist/task-DwzRxORh.js.map +1 -0
- package/dist/{task-stream-4I08S7A6.js → task-stream-BdJV-usk.js} +27 -6
- package/dist/task-stream-BdJV-usk.js.map +1 -0
- package/dist/{tasks-C6CfAC-D.js → tasks-yqJVuTvc.js} +2 -2
- package/dist/{tasks-C6CfAC-D.js.map → tasks-yqJVuTvc.js.map} +1 -1
- package/dist/{telemetry-3fcP3zSF.js → telemetry-UBL5MQtw.js} +3 -3
- package/dist/{telemetry-3fcP3zSF.js.map → telemetry-UBL5MQtw.js.map} +1 -1
- package/dist/{terminal-D4A6Hmkf.js → terminal-vo5Zl7j1.js} +807 -63
- package/dist/terminal-vo5Zl7j1.js.map +1 -0
- package/dist/{tools-pH2CNwNX.js → tools-Br8lP29r.js} +8 -10
- package/dist/tools-Br8lP29r.js.map +1 -0
- package/dist/warlock-smoke-test.ts +177 -1
- package/dist/{wizard-abort-BEBAXmPp.js → wizard-abort-BRsd7-aB.js} +1 -1
- package/dist/{wizard-abort-BbsBpCCV.js → wizard-abort-FIC6lKCs.js} +4 -4
- package/dist/{wizard-abort-BbsBpCCV.js.map → wizard-abort-FIC6lKCs.js.map} +1 -1
- package/dist/{wizard-session-BAZql11N.js → wizard-session-CwgR5y3-.js} +2 -1
- package/dist/wizard-session-CwgR5y3-.js.map +1 -0
- package/dist/{wizard-session-C5lZPPEc.js → wizard-session-DsK16SGs.js} +1 -1
- package/dist/yara-hooks--QA2Cwjv.js +2 -0
- package/dist/{yara-hooks-BGBQ4IcA.js → yara-hooks-DxpCD72V.js} +39 -83
- package/dist/yara-hooks-DxpCD72V.js.map +1 -0
- package/package.json +2 -1
- package/dist/agent-interface-oqI_K3-4.js.map +0 -1
- package/dist/agent-runner-CunYPDzL.js.map +0 -1
- package/dist/analytics-DsXLkDwE.js.map +0 -1
- package/dist/analytics-agQIoFg6.js +0 -2
- package/dist/commandments-BagyrYRW.js +0 -2
- package/dist/commandments-DiMtIz1o.js +0 -42
- package/dist/commandments-DiMtIz1o.js.map +0 -1
- package/dist/debug-BH67OSPW.js.map +0 -1
- package/dist/pi-BOCjsfgo.js +0 -575
- package/dist/pi-BOCjsfgo.js.map +0 -1
- package/dist/playground-B4yTvjn4.js.map +0 -1
- package/dist/queue-tools-DXg_5old.js.map +0 -1
- package/dist/security-np5qnEMM.js.map +0 -1
- package/dist/start-tui-DHEtMtnm.js.map +0 -1
- package/dist/task-CmWToh2Q.js.map +0 -1
- package/dist/task-stream-4I08S7A6.js.map +0 -1
- package/dist/terminal-D4A6Hmkf.js.map +0 -1
- package/dist/tools-pH2CNwNX.js.map +0 -1
- package/dist/wizard-session-BAZql11N.js.map +0 -1
- package/dist/yara-hooks-BGBQ4IcA.js.map +0 -1
- package/dist/yara-hooks-BfhKhlIT.js +0 -2
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import { a as getLogFilePath, f as getUI, ft as WIZARD_USER_AGENT, ht as VERSION, s as logToFile, ut as WIZARD_REMARK_EVENT_NAME } from "./debug-CbgLkSCY.js";
|
|
2
|
+
import { n as analytics } from "./analytics-D7hsukY7.js";
|
|
3
|
+
import { M as GATEWAY_PROVIDER, N as buildGatewayProvider, f as AgentOutputSignals, h as assembleCommandments, m as REMARK_INSTRUCTION, p as AgentSignals } from "./agent-interface-CLvG6Rk9.js";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
//#region src/lib/agent/aio-capture.ts
|
|
7
|
+
/**
|
|
8
|
+
* aio-capture — mirror wizard LLM calls into the authenticated project's
|
|
9
|
+
* AI Observability tab as `$ai_generation` events. Gated on `--capture-aio`
|
|
10
|
+
* (dev/test builds only); returns a no-op instance when disabled so the
|
|
11
|
+
* wire-in points stay a no-op with zero overhead.
|
|
12
|
+
*
|
|
13
|
+
* The wizard's LLM calls happen inside subprocesses we don't own (the
|
|
14
|
+
* claude-agent-sdk CLI on the anthropic path, pi's own runtime on pi), so we
|
|
15
|
+
* can't wrap the client with `posthog-node`'s `wrapAnthropic`. Instead, each
|
|
16
|
+
* harness surfaces assistant turns on the way back — the SDK message stream
|
|
17
|
+
* on anthropic, `agentSession.subscribe(message_end)` on pi — and this module
|
|
18
|
+
* composes an `$ai_generation` event from either shape.
|
|
19
|
+
*
|
|
20
|
+
* For the Conversation tab in AIO to render anything, each generation needs
|
|
21
|
+
* `$ai_input` (the messages the model saw when it produced this turn). The
|
|
22
|
+
* initial prompt is passed to the SDK subprocess and never echoed back on
|
|
23
|
+
* the stream, so callers must hand it to `setInitialPrompt` before the run
|
|
24
|
+
* starts. From there we track user/assistant turns as they flow through the
|
|
25
|
+
* stream, snapshotting the running conversation into each generation's
|
|
26
|
+
* `$ai_input` and firing `$ai_input_state` / `$ai_output_state` on the
|
|
27
|
+
* terminal `$ai_trace` event.
|
|
28
|
+
*
|
|
29
|
+
* POSTs are fire-and-forget: failures are debug-logged and never surfaced to
|
|
30
|
+
* the main agent flow. Losing an AIO event must never break a run.
|
|
31
|
+
*/
|
|
32
|
+
const noop = (_) => void 0;
|
|
33
|
+
const NOOP = {
|
|
34
|
+
setInitialPrompt: noop,
|
|
35
|
+
captureFromAnthropicSDKMessage: noop,
|
|
36
|
+
captureFromPiMessageEndEvent: noop
|
|
37
|
+
};
|
|
38
|
+
function createAioCapture(args) {
|
|
39
|
+
if (!args.enabled || !args.projectApiKey || !args.apiHost) return NOOP;
|
|
40
|
+
const { projectApiKey, apiHost, runTags } = args;
|
|
41
|
+
const distinctId = `wizard-cli-${VERSION}`;
|
|
42
|
+
const captureUrl = `${apiHost.replace(/\/$/, "")}/capture/`;
|
|
43
|
+
const runStartAt = Date.now();
|
|
44
|
+
let prevTurnAt = runStartAt;
|
|
45
|
+
let traceEnded = false;
|
|
46
|
+
let initialPrompt;
|
|
47
|
+
let conversation = [];
|
|
48
|
+
let lastAssistantContent;
|
|
49
|
+
const postEvent = (event, properties) => {
|
|
50
|
+
const body = {
|
|
51
|
+
api_key: projectApiKey,
|
|
52
|
+
event,
|
|
53
|
+
distinct_id: distinctId,
|
|
54
|
+
properties: {
|
|
55
|
+
$ai_trace_id: runTags.run_id ?? "",
|
|
56
|
+
...runTags,
|
|
57
|
+
...properties
|
|
58
|
+
},
|
|
59
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
60
|
+
};
|
|
61
|
+
fetch(captureUrl, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify(body)
|
|
65
|
+
}).then((res) => {
|
|
66
|
+
if (!res.ok) logToFile(`[aio-capture] POST failed status=${res.status}`);
|
|
67
|
+
}).catch((err) => {
|
|
68
|
+
logToFile(`[aio-capture] POST error: ${err instanceof Error ? err.message : String(err)}`);
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
const postGeneration = (turn) => {
|
|
72
|
+
postEvent("$ai_generation", {
|
|
73
|
+
$ai_provider: turn.provider,
|
|
74
|
+
$ai_model: turn.model,
|
|
75
|
+
$ai_input: turn.input,
|
|
76
|
+
$ai_output_choices: turn.outputChoices,
|
|
77
|
+
$ai_input_tokens: turn.inputTokens ?? 0,
|
|
78
|
+
$ai_output_tokens: turn.outputTokens ?? 0,
|
|
79
|
+
$ai_cache_read_input_tokens: turn.cacheReadTokens ?? 0,
|
|
80
|
+
$ai_cache_creation_input_tokens: turn.cacheCreationTokens ?? 0,
|
|
81
|
+
$ai_latency: turn.latencyMs / 1e3,
|
|
82
|
+
$ai_generation_id: turn.generationId ?? "",
|
|
83
|
+
$ai_is_error: turn.isError
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
const sendTraceEnd = (opts) => {
|
|
87
|
+
if (traceEnded) return;
|
|
88
|
+
traceEnded = true;
|
|
89
|
+
postEvent("$ai_trace", {
|
|
90
|
+
$ai_span_name: runTags.program_id ?? "wizard-run",
|
|
91
|
+
$ai_latency: (Date.now() - runStartAt) / 1e3,
|
|
92
|
+
$ai_is_error: opts.isError,
|
|
93
|
+
$ai_input_state: initialPrompt ? [{
|
|
94
|
+
role: "user",
|
|
95
|
+
content: [{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: initialPrompt
|
|
98
|
+
}]
|
|
99
|
+
}] : [],
|
|
100
|
+
$ai_output_state: lastAssistantContent ?? []
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
const anthropicAssistantTurn = (msg) => {
|
|
104
|
+
const m = msg;
|
|
105
|
+
if (m?.type !== "assistant" || !m.message) return null;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const latencyMs = now - prevTurnAt;
|
|
108
|
+
prevTurnAt = now;
|
|
109
|
+
return {
|
|
110
|
+
provider: "anthropic",
|
|
111
|
+
model: m.message.model ?? "unknown",
|
|
112
|
+
input: [...conversation],
|
|
113
|
+
outputChoices: Array.isArray(m.message.content) ? m.message.content : [],
|
|
114
|
+
inputTokens: m.message.usage?.input_tokens,
|
|
115
|
+
outputTokens: m.message.usage?.output_tokens,
|
|
116
|
+
cacheReadTokens: m.message.usage?.cache_read_input_tokens,
|
|
117
|
+
cacheCreationTokens: m.message.usage?.cache_creation_input_tokens,
|
|
118
|
+
latencyMs,
|
|
119
|
+
generationId: m.message.id,
|
|
120
|
+
isError: false
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
const piAssistantTurn = (event) => {
|
|
124
|
+
const e = event;
|
|
125
|
+
if (e?.type !== "message_end" || e.message?.role !== "assistant") return null;
|
|
126
|
+
const now = Date.now();
|
|
127
|
+
const latencyMs = now - prevTurnAt;
|
|
128
|
+
prevTurnAt = now;
|
|
129
|
+
return {
|
|
130
|
+
provider: "anthropic",
|
|
131
|
+
model: e.message.model ?? "unknown",
|
|
132
|
+
input: [...conversation],
|
|
133
|
+
outputChoices: Array.isArray(e.message.content) ? e.message.content : [],
|
|
134
|
+
inputTokens: e.message.usage?.input_tokens,
|
|
135
|
+
outputTokens: e.message.usage?.output_tokens,
|
|
136
|
+
cacheReadTokens: e.message.usage?.cache_read_input_tokens,
|
|
137
|
+
cacheCreationTokens: e.message.usage?.cache_creation_input_tokens,
|
|
138
|
+
latencyMs,
|
|
139
|
+
generationId: e.message.id,
|
|
140
|
+
isError: false
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
return {
|
|
144
|
+
setInitialPrompt(prompt) {
|
|
145
|
+
initialPrompt = prompt;
|
|
146
|
+
conversation = [{
|
|
147
|
+
role: "user",
|
|
148
|
+
content: [{
|
|
149
|
+
type: "text",
|
|
150
|
+
text: prompt
|
|
151
|
+
}]
|
|
152
|
+
}];
|
|
153
|
+
lastAssistantContent = void 0;
|
|
154
|
+
},
|
|
155
|
+
captureFromAnthropicSDKMessage(msg) {
|
|
156
|
+
try {
|
|
157
|
+
const m = msg;
|
|
158
|
+
if (m?.type === "user" && m.message) conversation.push({
|
|
159
|
+
role: "user",
|
|
160
|
+
content: m.message.content ?? []
|
|
161
|
+
});
|
|
162
|
+
const turn = anthropicAssistantTurn(msg);
|
|
163
|
+
if (turn) {
|
|
164
|
+
postGeneration(turn);
|
|
165
|
+
conversation.push({
|
|
166
|
+
role: "assistant",
|
|
167
|
+
content: turn.outputChoices
|
|
168
|
+
});
|
|
169
|
+
lastAssistantContent = turn.outputChoices;
|
|
170
|
+
}
|
|
171
|
+
if (m?.type === "result") sendTraceEnd({ isError: !!m.is_error });
|
|
172
|
+
} catch (err) {
|
|
173
|
+
logToFile(`[aio-capture] anthropic compose failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
captureFromPiMessageEndEvent(event) {
|
|
177
|
+
try {
|
|
178
|
+
const e = event;
|
|
179
|
+
if (e?.type === "message_end" && e.message?.role === "user" && e.message) conversation.push({
|
|
180
|
+
role: "user",
|
|
181
|
+
content: e.message.content ?? []
|
|
182
|
+
});
|
|
183
|
+
const turn = piAssistantTurn(event);
|
|
184
|
+
if (turn) {
|
|
185
|
+
postGeneration(turn);
|
|
186
|
+
conversation.push({
|
|
187
|
+
role: "assistant",
|
|
188
|
+
content: turn.outputChoices
|
|
189
|
+
});
|
|
190
|
+
lastAssistantContent = turn.outputChoices;
|
|
191
|
+
}
|
|
192
|
+
if (e?.type === "agent_end" && !e.willRetry) sendTraceEnd({ isError: false });
|
|
193
|
+
} catch (err) {
|
|
194
|
+
logToFile(`[aio-capture] pi compose failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/lib/agent/runner/harness/pi/completion.ts
|
|
201
|
+
/** Which completion guard should fail a pi run, or undefined for a clean finish. */
|
|
202
|
+
function completionFailure(args) {
|
|
203
|
+
if (args.toolCalls === 0) return "WIZARD_NO_PROGRESS";
|
|
204
|
+
if (args.openTasks) return "WIZARD_INCOMPLETE_TASKS";
|
|
205
|
+
}
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region src/lib/agent/runner/harness/pi/index.ts
|
|
208
|
+
/**
|
|
209
|
+
* The `pi` backend — the challenger. Drives pi.dev's coding agent
|
|
210
|
+
* (`@earendil-works/pi-coding-agent`) against the PostHog LLM gateway, behind
|
|
211
|
+
* `wizard-orchestrator`. It owns the agent loop and model transport; prompt
|
|
212
|
+
* assembly, error routing, and the outro stay in `linear.ts`, shared with the
|
|
213
|
+
* `anthropic` control.
|
|
214
|
+
*
|
|
215
|
+
* Transport: the gateway is registered as an `anthropic-messages` provider
|
|
216
|
+
* (same protocol the claude-agent-sdk path uses), bearer auth, Bedrock-fallback
|
|
217
|
+
* + wizard metadata/flag headers, model id matched to `anthropic` for a clean
|
|
218
|
+
* A/B. Security parity (canUseTool + YARA) and skills/MCP discovery are
|
|
219
|
+
* follow-ups (#525, #524 skills) — v1 uses pi's built-in coding tools.
|
|
220
|
+
*/
|
|
221
|
+
/** Injects the MCP server `instructions` pi-mcp-adapter drops (project env, skill steer, tool domains) into the system prompt, falling back to a bootstrap-derived project block when the warm-connect captured none. */
|
|
222
|
+
function piMcpContext(boot, instructions) {
|
|
223
|
+
if (instructions) return [
|
|
224
|
+
"",
|
|
225
|
+
"## PostHog MCP server",
|
|
226
|
+
instructions
|
|
227
|
+
].join("\n");
|
|
228
|
+
return [
|
|
229
|
+
"",
|
|
230
|
+
"## PostHog project",
|
|
231
|
+
"Your `posthog_exec` calls run against this project:",
|
|
232
|
+
`- Project: ${boot.project?.name ? `${boot.project.name} (id ${boot.credentials.projectId})` : `id ${boot.credentials.projectId}`}`,
|
|
233
|
+
`- Host: ${boot.credentials.host.apiHost}`,
|
|
234
|
+
`- Region: ${boot.credentials.host.region}`
|
|
235
|
+
].join("\n");
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The ONLY environment variables pi's tool subprocesses (bash → npm/pip/…) are
|
|
239
|
+
* allowed to see. Everything else — every secret (POSTHOG_PERSONAL_API_KEY,
|
|
240
|
+
* ANTHROPIC_*, AWS_*), every ambient credential, the parent process's whole env
|
|
241
|
+
* — is dropped before a child is spawned. pi's own gateway auth is programmatic
|
|
242
|
+
* (the access token never lives in env), so a minimal env costs the agent
|
|
243
|
+
* nothing while closing the leak that exposed the key before. Kept to what a
|
|
244
|
+
* package manager genuinely needs to run.
|
|
245
|
+
*/
|
|
246
|
+
const ALLOWED_SUBPROCESS_ENV_KEYS = [
|
|
247
|
+
"PATH",
|
|
248
|
+
"HOME",
|
|
249
|
+
"SHELL",
|
|
250
|
+
"USER",
|
|
251
|
+
"LOGNAME",
|
|
252
|
+
"TMPDIR",
|
|
253
|
+
"TMP",
|
|
254
|
+
"TEMP",
|
|
255
|
+
"TERM",
|
|
256
|
+
"LANG",
|
|
257
|
+
"LC_ALL",
|
|
258
|
+
"LC_CTYPE",
|
|
259
|
+
"NODE_EXTRA_CA_CERTS",
|
|
260
|
+
"SSL_CERT_FILE",
|
|
261
|
+
"SSL_CERT_DIR",
|
|
262
|
+
"HTTP_PROXY",
|
|
263
|
+
"HTTPS_PROXY",
|
|
264
|
+
"NO_PROXY",
|
|
265
|
+
"http_proxy",
|
|
266
|
+
"https_proxy",
|
|
267
|
+
"no_proxy"
|
|
268
|
+
];
|
|
269
|
+
/** A fresh subprocess env holding only the allowlisted keys present in process.env. */
|
|
270
|
+
function buildScrubbedEnv() {
|
|
271
|
+
const env = {};
|
|
272
|
+
for (const key of ALLOWED_SUBPROCESS_ENV_KEYS) {
|
|
273
|
+
const value = process.env[key];
|
|
274
|
+
if (value !== void 0) env[key] = value;
|
|
275
|
+
}
|
|
276
|
+
return env;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Tag a tool with an execution mode (mutates + returns it). Read-only tools are
|
|
280
|
+
* `parallel` so a single turn that batches independent reads/searches runs them
|
|
281
|
+
* at once; mutating/install tools are `sequential` so a batch never races writes
|
|
282
|
+
* or concurrent installs. pi-agent-core runs a batch in parallel only when no
|
|
283
|
+
* tool in it is `sequential`.
|
|
284
|
+
*/
|
|
285
|
+
function withMode(tool, mode) {
|
|
286
|
+
tool.executionMode = mode;
|
|
287
|
+
return tool;
|
|
288
|
+
}
|
|
289
|
+
/** Pull plain text out of a pi AgentMessage (content is text/image blocks). */
|
|
290
|
+
function extractText(message) {
|
|
291
|
+
const content = message?.content;
|
|
292
|
+
if (typeof content === "string") return content;
|
|
293
|
+
if (Array.isArray(content)) return content.filter((c) => {
|
|
294
|
+
const block = c;
|
|
295
|
+
return block?.type === "text" && typeof block.text === "string";
|
|
296
|
+
}).map((c) => c.text).join("");
|
|
297
|
+
return "";
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Surface `[DASHBOARD_URL]` / `[NOTEBOOK_URL]` markers the agent prints (after
|
|
301
|
+
* the MCP creates them) into the outro link, mirroring the anthropic path's
|
|
302
|
+
* signal parsing (#9). The marker carries the URL the MCP returned.
|
|
303
|
+
*/
|
|
304
|
+
function applyOutroMarkers(textBlock) {
|
|
305
|
+
const markers = [[AgentSignals.DASHBOARD_URL, (url) => getUI().setDashboardUrl(url)], [AgentSignals.NOTEBOOK_URL, (url) => getUI().setNotebookUrl(url)]];
|
|
306
|
+
for (const [marker, apply] of markers) {
|
|
307
|
+
const idx = textBlock.indexOf(marker);
|
|
308
|
+
if (idx === -1) continue;
|
|
309
|
+
const url = textBlock.slice(idx + marker.length).trim().split(/\s/)[0];
|
|
310
|
+
if (url) apply(url);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* The text of the last `[STATUS] …` line in a block, if any. Last wins so the
|
|
315
|
+
* spinner shows the most recent action when a turn prints several.
|
|
316
|
+
*/
|
|
317
|
+
function lastStatusLine(textBlock) {
|
|
318
|
+
let status;
|
|
319
|
+
for (const line of textBlock.split("\n")) {
|
|
320
|
+
const idx = line.indexOf(AgentSignals.STATUS);
|
|
321
|
+
if (idx !== -1) status = line.slice(idx + AgentSignals.STATUS.length).trim();
|
|
322
|
+
}
|
|
323
|
+
return status || void 0;
|
|
324
|
+
}
|
|
325
|
+
/** Cap on completion-guard re-prompts while tasks remain open (see the run loop). */
|
|
326
|
+
const MAX_CONTINUE_NUDGES = 20;
|
|
327
|
+
/** Nudge re-sent when the agent stops early with tasks still open. */
|
|
328
|
+
const CONTINUE_INSTRUCTION = "You still have open tasks in your list (not all are marked `completed`). Do not stop — pick up the next `in_progress` or `pending` task and keep working until every task is `completed`. Continue now.";
|
|
329
|
+
/** True while any task in the store is not yet `completed`. */
|
|
330
|
+
function hasOpenTasks(store) {
|
|
331
|
+
for (const t of store.values()) if (t.status !== "completed") return true;
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
const piBackend = {
|
|
335
|
+
name: "pi",
|
|
336
|
+
async run(inputs) {
|
|
337
|
+
const { session, boot, prompt, spinner, config, programConfig } = inputs;
|
|
338
|
+
const modelId = inputs.model;
|
|
339
|
+
const capture = createAioCapture({
|
|
340
|
+
enabled: session.captureAio,
|
|
341
|
+
projectApiKey: boot.credentials.projectApiKey,
|
|
342
|
+
apiHost: boot.credentials.host.apiHost,
|
|
343
|
+
runTags: boot.wizardMetadata
|
|
344
|
+
});
|
|
345
|
+
getUI().log.step("Initializing Wizard agent...");
|
|
346
|
+
getUI().log.step(`Verbose logs: ${getLogFilePath()}`);
|
|
347
|
+
getUI().log.success("Agent initialized. Let's get cooking!");
|
|
348
|
+
spinner.start(config.spinnerMessage ?? "Customizing your PostHog setup...");
|
|
349
|
+
const startTime = Date.now();
|
|
350
|
+
const signals = new AgentOutputSignals();
|
|
351
|
+
let assistantTurns = 0;
|
|
352
|
+
let toolCalls = 0;
|
|
353
|
+
const runDurations = () => {
|
|
354
|
+
const durationMs = Date.now() - startTime;
|
|
355
|
+
return {
|
|
356
|
+
duration_ms: durationMs,
|
|
357
|
+
duration_seconds: Math.round(durationMs / 1e3)
|
|
358
|
+
};
|
|
359
|
+
};
|
|
360
|
+
const captureAborted = () => analytics.wizardCapture("agent aborted", {
|
|
361
|
+
...runDurations(),
|
|
362
|
+
model: modelId
|
|
363
|
+
});
|
|
364
|
+
try {
|
|
365
|
+
const { createAgentSession, DefaultResourceLoader, SessionManager, AuthStorage, ModelRegistry, getAgentDir, createLsToolDefinition, createFindToolDefinition, createGrepToolDefinition, createBashToolDefinition, createReadToolDefinition, createEditToolDefinition, createWriteToolDefinition } = await import("@earendil-works/pi-coding-agent");
|
|
366
|
+
const { provider, caps } = buildGatewayProvider({
|
|
367
|
+
gatewayUrl: boot.credentials.host.gatewayUrl,
|
|
368
|
+
accessToken: boot.credentials.accessToken,
|
|
369
|
+
wizardMetadata: boot.wizardMetadata,
|
|
370
|
+
wizardFlags: boot.wizardFlags,
|
|
371
|
+
modelId,
|
|
372
|
+
effort: inputs.thinkingLevel
|
|
373
|
+
});
|
|
374
|
+
const registry = ModelRegistry.inMemory(AuthStorage.create());
|
|
375
|
+
registry.registerProvider(GATEWAY_PROVIDER, provider);
|
|
376
|
+
const model = registry.find(GATEWAY_PROVIDER, modelId);
|
|
377
|
+
if (!model) return {
|
|
378
|
+
error: "WIZARD_API_ERROR",
|
|
379
|
+
message: "pi: gateway model could not be resolved"
|
|
380
|
+
};
|
|
381
|
+
const askState = { pending: false };
|
|
382
|
+
const { createSecurityExtension } = await import("./security-BNqgFjYh.js");
|
|
383
|
+
const security = createSecurityExtension({
|
|
384
|
+
disallowedTools: programConfig.disallowedTools,
|
|
385
|
+
getWizardAskPending: () => askState.pending,
|
|
386
|
+
triageProvider: boot.triageProvider,
|
|
387
|
+
workingDirectory: session.installDir
|
|
388
|
+
});
|
|
389
|
+
const { prewarmYaraScanner } = await import("./yara-hooks--QA2Cwjv.js");
|
|
390
|
+
prewarmYaraScanner();
|
|
391
|
+
const extensionFactories = [security.factory];
|
|
392
|
+
let mcpCleanup;
|
|
393
|
+
let mcpInstructions;
|
|
394
|
+
try {
|
|
395
|
+
const { setupPostHogMcp, fetchInstructions } = await import("./mcp-jKHwa45f.js");
|
|
396
|
+
const instructionsPromise = fetchInstructions(boot.credentials.host.mcpUrl, boot.credentials.accessToken, WIZARD_USER_AGENT);
|
|
397
|
+
const mcp = await setupPostHogMcp({
|
|
398
|
+
mcpUrl: boot.credentials.host.mcpUrl,
|
|
399
|
+
accessToken: boot.credentials.accessToken,
|
|
400
|
+
userAgent: WIZARD_USER_AGENT
|
|
401
|
+
});
|
|
402
|
+
extensionFactories.push(mcp.extensionFactory);
|
|
403
|
+
mcpCleanup = mcp.cleanup;
|
|
404
|
+
mcpInstructions = await instructionsPromise;
|
|
405
|
+
} catch (err) {
|
|
406
|
+
logToFile(`[pi] PostHog MCP setup skipped: ${String(err)}`);
|
|
407
|
+
analytics.wizardCapture("mcp setup failed", {
|
|
408
|
+
harness: "pi",
|
|
409
|
+
scope: "run",
|
|
410
|
+
error: String(err).slice(0, 300)
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
const resourceLoader = new DefaultResourceLoader({
|
|
414
|
+
cwd: session.installDir,
|
|
415
|
+
agentDir: getAgentDir(),
|
|
416
|
+
systemPrompt: assembleCommandments({
|
|
417
|
+
program: programConfig.id,
|
|
418
|
+
sequence: "linear",
|
|
419
|
+
harness: "pi",
|
|
420
|
+
caps: {
|
|
421
|
+
bash: true,
|
|
422
|
+
posthogMcp: true
|
|
423
|
+
}
|
|
424
|
+
}) + "\n" + piMcpContext(boot, mcpInstructions),
|
|
425
|
+
noExtensions: true,
|
|
426
|
+
noSkills: true,
|
|
427
|
+
noContextFiles: true,
|
|
428
|
+
noPromptTemplates: true,
|
|
429
|
+
noThemes: true,
|
|
430
|
+
extensionFactories
|
|
431
|
+
});
|
|
432
|
+
await resourceLoader.reload();
|
|
433
|
+
const { createWizardPiTools } = await import("./tools-Br8lP29r.js");
|
|
434
|
+
const { createWizardPiTaskTools } = await import("./tasks-yqJVuTvc.js");
|
|
435
|
+
const { createDispatchAgentTool } = await import("./subagent-CKNbRtfQ.js");
|
|
436
|
+
const wizardTaskTools = createWizardPiTaskTools();
|
|
437
|
+
const scrubbedBash = withMode(createBashToolDefinition(session.installDir, { spawnHook: (ctx) => ({
|
|
438
|
+
...ctx,
|
|
439
|
+
env: buildScrubbedEnv()
|
|
440
|
+
}) }), "sequential");
|
|
441
|
+
const customTools = [
|
|
442
|
+
withMode(createReadToolDefinition(session.installDir), "parallel"),
|
|
443
|
+
withMode(createEditToolDefinition(session.installDir), "sequential"),
|
|
444
|
+
withMode(createWriteToolDefinition(session.installDir), "sequential"),
|
|
445
|
+
scrubbedBash,
|
|
446
|
+
withMode(createLsToolDefinition(session.installDir), "parallel"),
|
|
447
|
+
withMode(createFindToolDefinition(session.installDir), "parallel"),
|
|
448
|
+
withMode(createGrepToolDefinition(session.installDir), "parallel"),
|
|
449
|
+
...createWizardPiTools({
|
|
450
|
+
workingDirectory: session.installDir,
|
|
451
|
+
skillsBaseUrl: boot.skillsBaseUrl,
|
|
452
|
+
triageProvider: boot.triageProvider,
|
|
453
|
+
detectPackageManager: config.detectPackageManager,
|
|
454
|
+
askBridge: inputs.askBridge,
|
|
455
|
+
maxQuestions: config.maxQuestions,
|
|
456
|
+
onAskPendingChange: (pending) => {
|
|
457
|
+
askState.pending = pending;
|
|
458
|
+
},
|
|
459
|
+
disallowedTools: programConfig.disallowedTools
|
|
460
|
+
}),
|
|
461
|
+
...wizardTaskTools.tools,
|
|
462
|
+
createDispatchAgentTool({
|
|
463
|
+
model,
|
|
464
|
+
modelRegistry: registry,
|
|
465
|
+
cwd: session.installDir,
|
|
466
|
+
agentDir: getAgentDir(),
|
|
467
|
+
securityFactory: security.factory,
|
|
468
|
+
bashTool: scrubbedBash,
|
|
469
|
+
sdk: {
|
|
470
|
+
createAgentSession,
|
|
471
|
+
DefaultResourceLoader,
|
|
472
|
+
SessionManager
|
|
473
|
+
}
|
|
474
|
+
})
|
|
475
|
+
];
|
|
476
|
+
const { session: agentSession } = await createAgentSession({
|
|
477
|
+
model,
|
|
478
|
+
modelRegistry: registry,
|
|
479
|
+
thinkingLevel: caps.thinkingLevel,
|
|
480
|
+
cwd: session.installDir,
|
|
481
|
+
sessionManager: SessionManager.inMemory(session.installDir),
|
|
482
|
+
resourceLoader,
|
|
483
|
+
noTools: "builtin",
|
|
484
|
+
customTools
|
|
485
|
+
});
|
|
486
|
+
await agentSession.bindExtensions({});
|
|
487
|
+
const unsubscribe = agentSession.subscribe((event) => {
|
|
488
|
+
capture.captureFromPiMessageEndEvent(event);
|
|
489
|
+
switch (event.type) {
|
|
490
|
+
case "message_end": {
|
|
491
|
+
if (event.message?.role !== "assistant") break;
|
|
492
|
+
assistantTurns += 1;
|
|
493
|
+
const assistant = extractText(event.message).trim();
|
|
494
|
+
if (assistant) {
|
|
495
|
+
logToFile(`[pi] assistant: ${assistant.slice(0, 1e3)}`);
|
|
496
|
+
applyOutroMarkers(assistant);
|
|
497
|
+
const statusText = lastStatusLine(assistant);
|
|
498
|
+
if (statusText) {
|
|
499
|
+
getUI().pushStatus(statusText);
|
|
500
|
+
spinner.message(statusText);
|
|
501
|
+
}
|
|
502
|
+
for (const line of assistant.split("\n")) signals.push(line);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case "tool_execution_start": {
|
|
507
|
+
toolCalls += 1;
|
|
508
|
+
const args = JSON.stringify(event.args ?? {}).slice(0, 200);
|
|
509
|
+
logToFile(`[pi] → ${event.toolName} ${args}`);
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
case "tool_execution_end":
|
|
513
|
+
logToFile(`[pi] ${event.isError ? "✗" : "←"} ${event.toolName}: ${typeof event.result === "string" ? event.result : JSON.stringify(event.result ?? "")}`);
|
|
514
|
+
break;
|
|
515
|
+
case "agent_end":
|
|
516
|
+
logToFile(`[pi] agent_end (willRetry=${String(event.willRetry)})`);
|
|
517
|
+
break;
|
|
518
|
+
default: break;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
capture.setInitialPrompt(prompt);
|
|
522
|
+
try {
|
|
523
|
+
await agentSession.prompt(prompt);
|
|
524
|
+
let continueNudges = 0;
|
|
525
|
+
while (continueNudges < MAX_CONTINUE_NUDGES && !security.state.criticalViolation && hasOpenTasks(wizardTaskTools.store)) {
|
|
526
|
+
continueNudges += 1;
|
|
527
|
+
logToFile(`[pi] completion guard: tasks still open, nudge ${continueNudges}/${MAX_CONTINUE_NUDGES}`);
|
|
528
|
+
await agentSession.prompt(CONTINUE_INSTRUCTION);
|
|
529
|
+
}
|
|
530
|
+
if (!security.state.criticalViolation) try {
|
|
531
|
+
await agentSession.prompt(REMARK_INSTRUCTION);
|
|
532
|
+
} catch (err) {
|
|
533
|
+
logToFile(`[pi] remark request failed: ${String(err)}`);
|
|
534
|
+
}
|
|
535
|
+
} finally {
|
|
536
|
+
unsubscribe();
|
|
537
|
+
mcpCleanup?.();
|
|
538
|
+
}
|
|
539
|
+
if (security.state.criticalViolation) {
|
|
540
|
+
spinner.stop("Security violation detected");
|
|
541
|
+
logToFile(`[pi] terminated: YARA violation (blocked ${security.state.blockedCount} call(s))`);
|
|
542
|
+
captureAborted();
|
|
543
|
+
return { error: "WIZARD_YARA_VIOLATION" };
|
|
544
|
+
}
|
|
545
|
+
const openTasks = hasOpenTasks(wizardTaskTools.store);
|
|
546
|
+
const failure = completionFailure({
|
|
547
|
+
toolCalls,
|
|
548
|
+
openTasks
|
|
549
|
+
});
|
|
550
|
+
if (failure === "WIZARD_NO_PROGRESS") {
|
|
551
|
+
spinner.stop("Agent made no changes");
|
|
552
|
+
logToFile(`[pi] no progress: ${assistantTurns} assistant turn(s), 0 tool calls`);
|
|
553
|
+
analytics.wizardCapture("agent no progress", { assistant_turns: assistantTurns });
|
|
554
|
+
captureAborted();
|
|
555
|
+
return { error: failure };
|
|
556
|
+
}
|
|
557
|
+
if (failure === "WIZARD_INCOMPLETE_TASKS") {
|
|
558
|
+
spinner.stop("Agent stopped before finishing");
|
|
559
|
+
logToFile("[pi] incomplete: tasks left open");
|
|
560
|
+
analytics.wizardCapture("agent incomplete tasks", { open_tasks: true });
|
|
561
|
+
captureAborted();
|
|
562
|
+
return { error: failure };
|
|
563
|
+
}
|
|
564
|
+
const remark = signals.remark();
|
|
565
|
+
if (remark) analytics.capture(WIZARD_REMARK_EVENT_NAME, { remark });
|
|
566
|
+
const skillFailure = signals.skillInstallFailure();
|
|
567
|
+
if (skillFailure !== void 0) analytics.wizardCapture("agent continued without skill", { detail: skillFailure });
|
|
568
|
+
try {
|
|
569
|
+
const planFile = path.join(session.installDir, ".posthog-events.json");
|
|
570
|
+
if (fs.existsSync(planFile)) await fs.promises.rm(planFile);
|
|
571
|
+
} catch (err) {
|
|
572
|
+
logToFile(`[pi] .posthog-events.json cleanup skipped: ${String(err)}`);
|
|
573
|
+
}
|
|
574
|
+
const stats = agentSession.getSessionStats();
|
|
575
|
+
analytics.wizardCapture("agent completed", {
|
|
576
|
+
...runDurations(),
|
|
577
|
+
model: modelId,
|
|
578
|
+
num_turns: assistantTurns,
|
|
579
|
+
input_tokens: stats.tokens.input,
|
|
580
|
+
output_tokens: stats.tokens.output,
|
|
581
|
+
cache_creation_input_tokens: stats.tokens.cacheWrite,
|
|
582
|
+
cache_read_input_tokens: stats.tokens.cacheRead
|
|
583
|
+
});
|
|
584
|
+
spinner.stop(config.successMessage ?? "PostHog integration complete");
|
|
585
|
+
return {};
|
|
586
|
+
} catch (err) {
|
|
587
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
588
|
+
logToFile(`[pi] run error: ${message}`);
|
|
589
|
+
spinner.stop(config.errorMessage ?? `${config.integrationLabel} failed`);
|
|
590
|
+
getUI().log.error(`pi backend error: ${message}`);
|
|
591
|
+
captureAborted();
|
|
592
|
+
const lower = message.toLowerCase();
|
|
593
|
+
if (lower.includes("rate limit") || lower.includes("429")) return {
|
|
594
|
+
error: "WIZARD_RATE_LIMIT",
|
|
595
|
+
message
|
|
596
|
+
};
|
|
597
|
+
return {
|
|
598
|
+
error: "WIZARD_API_ERROR",
|
|
599
|
+
message
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
async runTask(inputs) {
|
|
604
|
+
const { runPiTask } = await import("./task-DwzRxORh.js");
|
|
605
|
+
return runPiTask(inputs);
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
//#endregion
|
|
609
|
+
export { piBackend as a, lastStatusLine as i, buildScrubbedEnv as n, withMode as o, extractText as r, createAioCapture as s, applyOutroMarkers as t };
|
|
610
|
+
|
|
611
|
+
//# sourceMappingURL=pi-CfdqOKDI.js.map
|